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_registry_bound(Arc::clone(
109 registry,
110 ))));
111}
112
113pub fn register_program(registry: &Arc<ToolRegistry>) {
115 register_program_with_catalog(
116 registry,
117 crate::program::ProgramCatalog::with_builtin_programs(),
118 );
119}
120
121pub fn register_program_with_catalog(
123 registry: &Arc<ToolRegistry>,
124 catalog: crate::program::ProgramCatalog,
125) {
126 registry.register_builtin(Arc::new(
127 crate::tools::ProgramTool::with_catalog_registry_bound(Arc::clone(registry), catalog),
128 ));
129}
130
131pub fn register_task(
137 registry: &Arc<ToolRegistry>,
138 llm_client: Arc<dyn crate::llm::LlmClient>,
139 agent_registry: Arc<crate::subagent::AgentRegistry>,
140 workspace: String,
141) {
142 register_task_with_mcp(
143 registry,
144 llm_client,
145 agent_registry,
146 workspace,
147 None,
148 None,
149 None,
150 );
151}
152
153pub fn register_task_with_mcp(
161 registry: &Arc<ToolRegistry>,
162 llm_client: Arc<dyn crate::llm::LlmClient>,
163 agent_registry: Arc<crate::subagent::AgentRegistry>,
164 workspace: String,
165 mcp_manager: Option<Arc<crate::mcp::manager::McpManager>>,
166 parent_context: Option<crate::child_run::ChildRunContext>,
167 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
168) {
169 register_task_with_mcp_managers(
170 registry,
171 llm_client,
172 agent_registry,
173 workspace,
174 mcp_manager.into_iter().collect(),
175 parent_context,
176 subagent_tracker,
177 );
178}
179
180pub fn register_task_with_mcp_managers(
185 registry: &Arc<ToolRegistry>,
186 llm_client: Arc<dyn crate::llm::LlmClient>,
187 agent_registry: Arc<crate::subagent::AgentRegistry>,
188 workspace: String,
189 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
190 parent_context: Option<crate::child_run::ChildRunContext>,
191 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
192) {
193 register_task_internal(
194 registry,
195 llm_client,
196 agent_registry,
197 workspace,
198 mcp_managers,
199 Vec::new(),
200 parent_context,
201 subagent_tracker,
202 None,
203 );
204}
205
206#[allow(clippy::too_many_arguments)]
208pub(crate) fn register_task_with_mcp_managers_and_scheduler(
209 registry: &Arc<ToolRegistry>,
210 llm_client: Arc<dyn crate::llm::LlmClient>,
211 agent_registry: Arc<crate::subagent::AgentRegistry>,
212 workspace: String,
213 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
214 parent_context: Option<crate::child_run::ChildRunContext>,
215 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
216 task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
217) {
218 register_task_with_mcp_sources_and_scheduler(
219 registry,
220 llm_client,
221 agent_registry,
222 workspace,
223 mcp_managers,
224 Vec::new(),
225 parent_context,
226 subagent_tracker,
227 task_scheduler,
228 );
229}
230
231#[allow(clippy::too_many_arguments)]
234pub(crate) fn register_task_with_mcp_sources_and_scheduler(
235 registry: &Arc<ToolRegistry>,
236 llm_client: Arc<dyn crate::llm::LlmClient>,
237 agent_registry: Arc<crate::subagent::AgentRegistry>,
238 workspace: String,
239 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
240 mcp_bindings: Vec<Arc<crate::mcp::McpBinding>>,
241 parent_context: Option<crate::child_run::ChildRunContext>,
242 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
243 task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
244) {
245 register_task_internal(
246 registry,
247 llm_client,
248 agent_registry,
249 workspace,
250 mcp_managers,
251 mcp_bindings,
252 parent_context,
253 subagent_tracker,
254 Some(task_scheduler),
255 );
256}
257
258#[allow(clippy::too_many_arguments)]
259fn register_task_internal(
260 registry: &Arc<ToolRegistry>,
261 llm_client: Arc<dyn crate::llm::LlmClient>,
262 agent_registry: Arc<crate::subagent::AgentRegistry>,
263 workspace: String,
264 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
265 mcp_bindings: Vec<Arc<crate::mcp::McpBinding>>,
266 parent_context: Option<crate::child_run::ChildRunContext>,
267 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
268 task_scheduler: Option<Arc<crate::task_scheduler::TaskScheduler>>,
269) {
270 use crate::tools::task::{ParallelTaskTool, TaskExecutor, TaskTool};
271 let mut executor =
272 TaskExecutor::with_mcp_managers(agent_registry, llm_client, workspace, mcp_managers)
273 .with_projected_mcp_bindings(mcp_bindings);
274 if let Some(ctx) = parent_context {
275 executor = executor.with_parent_context(ctx);
276 }
277 if let Some(tracker) = subagent_tracker {
278 executor = executor.with_subagent_tracker(tracker);
279 }
280 if let Some(task_scheduler) = task_scheduler {
281 executor = executor.with_task_scheduler(task_scheduler, false);
284 }
285 let executor = Arc::new(executor);
286 registry.register_builtin(Arc::new(TaskTool::new(Arc::clone(&executor))));
287 registry.register_builtin(Arc::new(ParallelTaskTool::new(Arc::clone(&executor))));
288}
289
290pub(crate) fn register_skill(
292 registry: &Arc<ToolRegistry>,
293 llm_client: Arc<dyn crate::llm::LlmClient>,
294 skill_registry: Arc<crate::skills::SkillRegistry>,
295 tool_executor: Arc<crate::tools::ToolExecutor>,
296 base_config: crate::agent::AgentConfig,
297) {
298 use crate::tools::skill::{SearchSkillsTool, SkillTool};
299 registry.register_builtin(Arc::new(SearchSkillsTool::new(Arc::clone(&skill_registry))));
300 registry.register_builtin(Arc::new(SkillTool::new_registry_bound(
301 skill_registry,
302 llm_client,
303 tool_executor,
304 base_config,
305 )));
306}
307
308pub fn register_generate_object(
313 registry: &Arc<ToolRegistry>,
314 llm_client: Arc<dyn crate::llm::LlmClient>,
315) {
316 registry.register_builtin(Arc::new(generate_object::GenerateObjectTool::new(
317 llm_client,
318 )));
319}
320
321#[cfg(test)]
322mod tests {
323 use super::safe_http_source_url;
324
325 #[test]
326 fn safe_source_url_removes_credentials_query_and_fragment() {
327 assert_eq!(
328 safe_http_source_url(
329 "HTTPS://user:password@Example.COM/report?access_token=secret#section"
330 )
331 .as_deref(),
332 Some("https://example.com/report")
333 );
334 assert!(safe_http_source_url("file:///tmp/source").is_none());
335 }
336}