a3s_code_core/tools/builtin/
mod.rs1pub(crate) mod bash;
7pub mod batch;
8mod bm25;
9mod code_intelligence;
10mod download;
11mod edit;
12mod generate_object;
13pub(crate) mod git;
14mod glob_tool;
15mod grep;
16mod hybrid_search;
17mod ls;
18mod patch;
19mod read;
20mod safe_http;
21mod search;
22mod semantic_search;
23mod web_fetch;
24mod web_search;
25mod write;
26
27use super::registry::ToolRegistry;
28use std::sync::Arc;
29
30pub(crate) fn safe_http_source_url(value: &str) -> Option<String> {
33 let mut url = reqwest::Url::parse(value.trim()).ok()?;
34 if !matches!(url.scheme(), "http" | "https") || url.host_str()?.is_empty() {
35 return None;
36 }
37 url.set_username("").ok()?;
38 url.set_password(None).ok()?;
39 url.set_query(None);
40 url.set_fragment(None);
41 Some(url.to_string())
42}
43
44pub fn register_builtins(
54 registry: &ToolRegistry,
55 workspace_services: &crate::workspace::WorkspaceServices,
56) {
57 let capabilities = workspace_services.capabilities();
58 if capabilities.read {
59 registry.register_builtin(Arc::new(read::ReadTool));
60 registry.register_builtin(Arc::new(ls::LsTool));
61 }
62 if capabilities.write {
63 registry.register_builtin(Arc::new(write::WriteTool));
64 if workspace_services.local_root().is_some() {
65 registry.register_builtin(Arc::new(download::DownloadTool));
66 }
67 }
68 if capabilities.read && capabilities.write {
69 registry.register_builtin(Arc::new(edit::EditTool));
70 registry.register_builtin(Arc::new(patch::PatchTool));
71 }
72 if capabilities.exec {
73 registry.register_builtin(Arc::new(bash::BashTool));
74 }
75 let semantic_enabled = capabilities.read && workspace_services.workspace_retrieval().is_some();
76 if capabilities.search || semantic_enabled {
77 let search = search::SearchTool::new(capabilities.read)
78 .with_backend_search(capabilities.search)
79 .with_semantic(semantic_enabled);
80 registry.register_builtin(Arc::new(search));
81 }
82 if workspace_services.code_intelligence().is_some() {
83 code_intelligence::register(registry);
84 }
85 if capabilities.git {
86 registry.register_builtin(Arc::new(git::GitTool));
87 }
88 registry.register_builtin(Arc::new(web_fetch::WebFetchTool));
89 registry.register_builtin(Arc::new(web_search::WebSearchTool::new()));
90}
91
92#[cfg(test)]
93pub(crate) fn repository_tool_parameter_schemas() -> Vec<(String, serde_json::Value)> {
94 use crate::tools::Tool;
95
96 let read = read::ReadTool;
97 let search = search::SearchTool::new(true);
98 let edit = edit::EditTool;
99 vec![
100 (read.name().to_string(), read.parameters()),
101 (search.name().to_string(), search.parameters()),
102 (edit.name().to_string(), edit.parameters()),
103 ]
104}
105
106pub fn register_batch(registry: &Arc<ToolRegistry>) {
108 registry.register_builtin(Arc::new(batch::BatchTool::new(Arc::clone(registry))));
109}
110
111pub fn register_program(registry: &Arc<ToolRegistry>) {
113 register_program_with_catalog(
114 registry,
115 crate::program::ProgramCatalog::with_builtin_programs(),
116 );
117}
118
119pub fn register_program_with_catalog(
121 registry: &Arc<ToolRegistry>,
122 catalog: crate::program::ProgramCatalog,
123) {
124 registry.register_builtin(Arc::new(crate::tools::ProgramTool::with_catalog(
125 Arc::clone(registry),
126 catalog,
127 )));
128}
129
130pub fn register_task(
136 registry: &Arc<ToolRegistry>,
137 llm_client: Arc<dyn crate::llm::LlmClient>,
138 agent_registry: Arc<crate::subagent::AgentRegistry>,
139 workspace: String,
140) {
141 register_task_with_mcp(
142 registry,
143 llm_client,
144 agent_registry,
145 workspace,
146 None,
147 None,
148 None,
149 );
150}
151
152pub fn register_task_with_mcp(
160 registry: &Arc<ToolRegistry>,
161 llm_client: Arc<dyn crate::llm::LlmClient>,
162 agent_registry: Arc<crate::subagent::AgentRegistry>,
163 workspace: String,
164 mcp_manager: Option<Arc<crate::mcp::manager::McpManager>>,
165 parent_context: Option<crate::child_run::ChildRunContext>,
166 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
167) {
168 register_task_with_mcp_managers(
169 registry,
170 llm_client,
171 agent_registry,
172 workspace,
173 mcp_manager.into_iter().collect(),
174 parent_context,
175 subagent_tracker,
176 );
177}
178
179pub fn register_task_with_mcp_managers(
184 registry: &Arc<ToolRegistry>,
185 llm_client: Arc<dyn crate::llm::LlmClient>,
186 agent_registry: Arc<crate::subagent::AgentRegistry>,
187 workspace: String,
188 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
189 parent_context: Option<crate::child_run::ChildRunContext>,
190 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
191) {
192 register_task_internal(
193 registry,
194 llm_client,
195 agent_registry,
196 workspace,
197 mcp_managers,
198 Vec::new(),
199 parent_context,
200 subagent_tracker,
201 None,
202 );
203}
204
205#[allow(clippy::too_many_arguments)]
207pub(crate) fn register_task_with_mcp_managers_and_scheduler(
208 registry: &Arc<ToolRegistry>,
209 llm_client: Arc<dyn crate::llm::LlmClient>,
210 agent_registry: Arc<crate::subagent::AgentRegistry>,
211 workspace: String,
212 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
213 parent_context: Option<crate::child_run::ChildRunContext>,
214 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
215 task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
216) {
217 register_task_with_mcp_sources_and_scheduler(
218 registry,
219 llm_client,
220 agent_registry,
221 workspace,
222 mcp_managers,
223 Vec::new(),
224 parent_context,
225 subagent_tracker,
226 task_scheduler,
227 );
228}
229
230#[allow(clippy::too_many_arguments)]
233pub(crate) fn register_task_with_mcp_sources_and_scheduler(
234 registry: &Arc<ToolRegistry>,
235 llm_client: Arc<dyn crate::llm::LlmClient>,
236 agent_registry: Arc<crate::subagent::AgentRegistry>,
237 workspace: String,
238 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
239 mcp_bindings: Vec<Arc<crate::mcp::McpBinding>>,
240 parent_context: Option<crate::child_run::ChildRunContext>,
241 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
242 task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
243) {
244 register_task_internal(
245 registry,
246 llm_client,
247 agent_registry,
248 workspace,
249 mcp_managers,
250 mcp_bindings,
251 parent_context,
252 subagent_tracker,
253 Some(task_scheduler),
254 );
255}
256
257#[allow(clippy::too_many_arguments)]
258fn register_task_internal(
259 registry: &Arc<ToolRegistry>,
260 llm_client: Arc<dyn crate::llm::LlmClient>,
261 agent_registry: Arc<crate::subagent::AgentRegistry>,
262 workspace: String,
263 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
264 mcp_bindings: Vec<Arc<crate::mcp::McpBinding>>,
265 parent_context: Option<crate::child_run::ChildRunContext>,
266 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
267 task_scheduler: Option<Arc<crate::task_scheduler::TaskScheduler>>,
268) {
269 use crate::tools::task::{ParallelTaskTool, TaskExecutor, TaskTool};
270 let mut executor =
271 TaskExecutor::with_mcp_managers(agent_registry, llm_client, workspace, mcp_managers)
272 .with_projected_mcp_bindings(mcp_bindings);
273 if let Some(ctx) = parent_context {
274 executor = executor.with_parent_context(ctx);
275 }
276 if let Some(tracker) = subagent_tracker {
277 executor = executor.with_subagent_tracker(tracker);
278 }
279 if let Some(task_scheduler) = task_scheduler {
280 executor = executor.with_task_scheduler(task_scheduler, false);
283 }
284 let executor = Arc::new(executor);
285 registry.register_builtin(Arc::new(TaskTool::new(Arc::clone(&executor))));
286 registry.register_builtin(Arc::new(ParallelTaskTool::new(Arc::clone(&executor))));
287}
288
289pub(crate) fn register_skill(
291 registry: &Arc<ToolRegistry>,
292 llm_client: Arc<dyn crate::llm::LlmClient>,
293 skill_registry: Arc<crate::skills::SkillRegistry>,
294 tool_executor: Arc<crate::tools::ToolExecutor>,
295 base_config: crate::agent::AgentConfig,
296) {
297 use crate::tools::skill::{SearchSkillsTool, SkillTool};
298 registry.register_builtin(Arc::new(SearchSkillsTool::new(Arc::clone(&skill_registry))));
299 registry.register_builtin(Arc::new(SkillTool::new(
300 skill_registry,
301 llm_client,
302 tool_executor,
303 base_config,
304 )));
305}
306
307pub fn register_generate_object(
312 registry: &Arc<ToolRegistry>,
313 llm_client: Arc<dyn crate::llm::LlmClient>,
314) {
315 registry.register_builtin(Arc::new(generate_object::GenerateObjectTool::new(
316 llm_client,
317 )));
318}
319
320#[cfg(test)]
321mod tests {
322 use super::safe_http_source_url;
323
324 #[test]
325 fn safe_source_url_removes_credentials_query_and_fragment() {
326 assert_eq!(
327 safe_http_source_url(
328 "HTTPS://user:password@Example.COM/report?access_token=secret#section"
329 )
330 .as_deref(),
331 Some("https://example.com/report")
332 );
333 assert!(safe_http_source_url("file:///tmp/source").is_none());
334 }
335}