a3s_code_core/tools/builtin/
mod.rs1pub(crate) mod bash;
7pub mod batch;
8mod code_intelligence;
9mod download;
10mod edit;
11mod generate_object;
12pub(crate) mod git;
13mod glob_tool;
14mod grep;
15mod ls;
16mod patch;
17mod read;
18mod safe_http;
19mod web_fetch;
20mod web_search;
21mod write;
22
23use super::registry::ToolRegistry;
24use std::sync::Arc;
25
26pub(crate) fn safe_http_source_url(value: &str) -> Option<String> {
29 let mut url = reqwest::Url::parse(value.trim()).ok()?;
30 if !matches!(url.scheme(), "http" | "https") || url.host_str()?.is_empty() {
31 return None;
32 }
33 url.set_username("").ok()?;
34 url.set_password(None).ok()?;
35 url.set_query(None);
36 url.set_fragment(None);
37 Some(url.to_string())
38}
39
40pub fn register_builtins(
50 registry: &ToolRegistry,
51 workspace_services: &crate::workspace::WorkspaceServices,
52) {
53 let capabilities = workspace_services.capabilities();
54 if capabilities.read {
55 registry.register_builtin(Arc::new(read::ReadTool));
56 registry.register_builtin(Arc::new(ls::LsTool));
57 }
58 if capabilities.write {
59 registry.register_builtin(Arc::new(write::WriteTool));
60 if workspace_services.local_root().is_some() {
61 registry.register_builtin(Arc::new(download::DownloadTool));
62 }
63 }
64 if capabilities.read && capabilities.write {
65 registry.register_builtin(Arc::new(edit::EditTool));
66 registry.register_builtin(Arc::new(patch::PatchTool));
67 }
68 if capabilities.exec {
69 registry.register_builtin(Arc::new(bash::BashTool));
70 }
71 if capabilities.search {
72 registry.register_builtin(Arc::new(grep::GrepTool));
73 registry.register_builtin(Arc::new(glob_tool::GlobTool));
74 }
75 if workspace_services.code_intelligence().is_some() {
76 code_intelligence::register(registry);
77 }
78 if capabilities.git {
79 registry.register_builtin(Arc::new(git::GitTool));
80 }
81 registry.register_builtin(Arc::new(web_fetch::WebFetchTool));
82 registry.register_builtin(Arc::new(web_search::WebSearchTool::new()));
83}
84
85#[cfg(test)]
86pub(crate) fn repository_tool_parameter_schemas() -> Vec<(String, serde_json::Value)> {
87 use crate::tools::Tool;
88
89 let read = read::ReadTool;
90 let grep = grep::GrepTool;
91 let glob = glob_tool::GlobTool;
92 let edit = edit::EditTool;
93 vec![
94 (read.name().to_string(), read.parameters()),
95 (grep.name().to_string(), grep.parameters()),
96 (glob.name().to_string(), glob.parameters()),
97 (edit.name().to_string(), edit.parameters()),
98 ]
99}
100
101pub fn register_batch(registry: &Arc<ToolRegistry>) {
103 registry.register_builtin(Arc::new(batch::BatchTool::new(Arc::clone(registry))));
104}
105
106pub fn register_program(registry: &Arc<ToolRegistry>) {
108 register_program_with_catalog(
109 registry,
110 crate::program::ProgramCatalog::with_builtin_programs(),
111 );
112}
113
114pub fn register_program_with_catalog(
116 registry: &Arc<ToolRegistry>,
117 catalog: crate::program::ProgramCatalog,
118) {
119 registry.register_builtin(Arc::new(crate::tools::ProgramTool::with_catalog(
120 Arc::clone(registry),
121 catalog,
122 )));
123}
124
125pub fn register_task(
131 registry: &Arc<ToolRegistry>,
132 llm_client: Arc<dyn crate::llm::LlmClient>,
133 agent_registry: Arc<crate::subagent::AgentRegistry>,
134 workspace: String,
135) {
136 register_task_with_mcp(
137 registry,
138 llm_client,
139 agent_registry,
140 workspace,
141 None,
142 None,
143 None,
144 );
145}
146
147pub fn register_task_with_mcp(
155 registry: &Arc<ToolRegistry>,
156 llm_client: Arc<dyn crate::llm::LlmClient>,
157 agent_registry: Arc<crate::subagent::AgentRegistry>,
158 workspace: String,
159 mcp_manager: Option<Arc<crate::mcp::manager::McpManager>>,
160 parent_context: Option<crate::child_run::ChildRunContext>,
161 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
162) {
163 register_task_with_mcp_managers(
164 registry,
165 llm_client,
166 agent_registry,
167 workspace,
168 mcp_manager.into_iter().collect(),
169 parent_context,
170 subagent_tracker,
171 );
172}
173
174pub fn register_task_with_mcp_managers(
179 registry: &Arc<ToolRegistry>,
180 llm_client: Arc<dyn crate::llm::LlmClient>,
181 agent_registry: Arc<crate::subagent::AgentRegistry>,
182 workspace: String,
183 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
184 parent_context: Option<crate::child_run::ChildRunContext>,
185 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
186) {
187 use crate::tools::task::{ParallelTaskTool, TaskExecutor, TaskTool};
188 let mut executor =
189 TaskExecutor::with_mcp_managers(agent_registry, llm_client, workspace, mcp_managers);
190 if let Some(ctx) = parent_context {
191 executor = executor.with_parent_context(ctx);
192 }
193 if let Some(tracker) = subagent_tracker {
194 executor = executor.with_subagent_tracker(tracker);
195 }
196 let executor = Arc::new(executor);
197 registry.register_builtin(Arc::new(TaskTool::new(Arc::clone(&executor))));
198 registry.register_builtin(Arc::new(ParallelTaskTool::new(Arc::clone(&executor))));
199}
200
201pub(crate) fn register_skill(
203 registry: &Arc<ToolRegistry>,
204 llm_client: Arc<dyn crate::llm::LlmClient>,
205 skill_registry: Arc<crate::skills::SkillRegistry>,
206 tool_executor: Arc<crate::tools::ToolExecutor>,
207 base_config: crate::agent::AgentConfig,
208) {
209 use crate::tools::skill::{SearchSkillsTool, SkillTool};
210 registry.register_builtin(Arc::new(SearchSkillsTool::new(Arc::clone(&skill_registry))));
211 registry.register_builtin(Arc::new(SkillTool::new(
212 skill_registry,
213 llm_client,
214 tool_executor,
215 base_config,
216 )));
217}
218
219pub fn register_generate_object(
224 registry: &Arc<ToolRegistry>,
225 llm_client: Arc<dyn crate::llm::LlmClient>,
226) {
227 registry.register_builtin(Arc::new(generate_object::GenerateObjectTool::new(
228 llm_client,
229 )));
230}
231
232#[cfg(test)]
233mod tests {
234 use super::safe_http_source_url;
235
236 #[test]
237 fn safe_source_url_removes_credentials_query_and_fragment() {
238 assert_eq!(
239 safe_http_source_url(
240 "HTTPS://user:password@Example.COM/report?access_token=secret#section"
241 )
242 .as_deref(),
243 Some("https://example.com/report")
244 );
245 assert!(safe_http_source_url("file:///tmp/source").is_none());
246 }
247}