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 ls;
17mod patch;
18mod read;
19mod safe_http;
20mod search;
21mod web_fetch;
22mod web_search;
23mod write;
24
25use super::registry::ToolRegistry;
26use std::sync::Arc;
27
28pub(crate) fn safe_http_source_url(value: &str) -> Option<String> {
31 let mut url = reqwest::Url::parse(value.trim()).ok()?;
32 if !matches!(url.scheme(), "http" | "https") || url.host_str()?.is_empty() {
33 return None;
34 }
35 url.set_username("").ok()?;
36 url.set_password(None).ok()?;
37 url.set_query(None);
38 url.set_fragment(None);
39 Some(url.to_string())
40}
41
42pub fn register_builtins(
52 registry: &ToolRegistry,
53 workspace_services: &crate::workspace::WorkspaceServices,
54) {
55 let capabilities = workspace_services.capabilities();
56 if capabilities.read {
57 registry.register_builtin(Arc::new(read::ReadTool));
58 registry.register_builtin(Arc::new(ls::LsTool));
59 }
60 if capabilities.write {
61 registry.register_builtin(Arc::new(write::WriteTool));
62 if workspace_services.local_root().is_some() {
63 registry.register_builtin(Arc::new(download::DownloadTool));
64 }
65 }
66 if capabilities.read && capabilities.write {
67 registry.register_builtin(Arc::new(edit::EditTool));
68 registry.register_builtin(Arc::new(patch::PatchTool));
69 }
70 if capabilities.exec {
71 registry.register_builtin(Arc::new(bash::BashTool));
72 }
73 if capabilities.search {
74 registry.register_builtin(Arc::new(search::SearchTool::new(capabilities.read)));
75 }
76 if workspace_services.code_intelligence().is_some() {
77 code_intelligence::register(registry);
78 }
79 if capabilities.git {
80 registry.register_builtin(Arc::new(git::GitTool));
81 }
82 registry.register_builtin(Arc::new(web_fetch::WebFetchTool));
83 registry.register_builtin(Arc::new(web_search::WebSearchTool::new()));
84}
85
86#[cfg(test)]
87pub(crate) fn repository_tool_parameter_schemas() -> Vec<(String, serde_json::Value)> {
88 use crate::tools::Tool;
89
90 let read = read::ReadTool;
91 let search = search::SearchTool::new(true);
92 let edit = edit::EditTool;
93 vec![
94 (read.name().to_string(), read.parameters()),
95 (search.name().to_string(), search.parameters()),
96 (edit.name().to_string(), edit.parameters()),
97 ]
98}
99
100pub fn register_batch(registry: &Arc<ToolRegistry>) {
102 registry.register_builtin(Arc::new(batch::BatchTool::new(Arc::clone(registry))));
103}
104
105pub fn register_program(registry: &Arc<ToolRegistry>) {
107 register_program_with_catalog(
108 registry,
109 crate::program::ProgramCatalog::with_builtin_programs(),
110 );
111}
112
113pub fn register_program_with_catalog(
115 registry: &Arc<ToolRegistry>,
116 catalog: crate::program::ProgramCatalog,
117) {
118 registry.register_builtin(Arc::new(crate::tools::ProgramTool::with_catalog(
119 Arc::clone(registry),
120 catalog,
121 )));
122}
123
124pub fn register_task(
130 registry: &Arc<ToolRegistry>,
131 llm_client: Arc<dyn crate::llm::LlmClient>,
132 agent_registry: Arc<crate::subagent::AgentRegistry>,
133 workspace: String,
134) {
135 register_task_with_mcp(
136 registry,
137 llm_client,
138 agent_registry,
139 workspace,
140 None,
141 None,
142 None,
143 );
144}
145
146pub fn register_task_with_mcp(
154 registry: &Arc<ToolRegistry>,
155 llm_client: Arc<dyn crate::llm::LlmClient>,
156 agent_registry: Arc<crate::subagent::AgentRegistry>,
157 workspace: String,
158 mcp_manager: Option<Arc<crate::mcp::manager::McpManager>>,
159 parent_context: Option<crate::child_run::ChildRunContext>,
160 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
161) {
162 register_task_with_mcp_managers(
163 registry,
164 llm_client,
165 agent_registry,
166 workspace,
167 mcp_manager.into_iter().collect(),
168 parent_context,
169 subagent_tracker,
170 );
171}
172
173pub fn register_task_with_mcp_managers(
178 registry: &Arc<ToolRegistry>,
179 llm_client: Arc<dyn crate::llm::LlmClient>,
180 agent_registry: Arc<crate::subagent::AgentRegistry>,
181 workspace: String,
182 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
183 parent_context: Option<crate::child_run::ChildRunContext>,
184 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
185) {
186 register_task_internal(
187 registry,
188 llm_client,
189 agent_registry,
190 workspace,
191 mcp_managers,
192 parent_context,
193 subagent_tracker,
194 None,
195 );
196}
197
198#[allow(clippy::too_many_arguments)]
200pub(crate) fn register_task_with_mcp_managers_and_scheduler(
201 registry: &Arc<ToolRegistry>,
202 llm_client: Arc<dyn crate::llm::LlmClient>,
203 agent_registry: Arc<crate::subagent::AgentRegistry>,
204 workspace: String,
205 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
206 parent_context: Option<crate::child_run::ChildRunContext>,
207 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
208 task_scheduler: Arc<crate::task_scheduler::TaskScheduler>,
209) {
210 register_task_internal(
211 registry,
212 llm_client,
213 agent_registry,
214 workspace,
215 mcp_managers,
216 parent_context,
217 subagent_tracker,
218 Some(task_scheduler),
219 );
220}
221
222#[allow(clippy::too_many_arguments)]
223fn register_task_internal(
224 registry: &Arc<ToolRegistry>,
225 llm_client: Arc<dyn crate::llm::LlmClient>,
226 agent_registry: Arc<crate::subagent::AgentRegistry>,
227 workspace: String,
228 mcp_managers: Vec<Arc<crate::mcp::manager::McpManager>>,
229 parent_context: Option<crate::child_run::ChildRunContext>,
230 subagent_tracker: Option<Arc<crate::subagent_task_tracker::InMemorySubagentTaskTracker>>,
231 task_scheduler: Option<Arc<crate::task_scheduler::TaskScheduler>>,
232) {
233 use crate::tools::task::{ParallelTaskTool, TaskExecutor, TaskTool};
234 let mut executor =
235 TaskExecutor::with_mcp_managers(agent_registry, llm_client, workspace, mcp_managers);
236 if let Some(ctx) = parent_context {
237 executor = executor.with_parent_context(ctx);
238 }
239 if let Some(tracker) = subagent_tracker {
240 executor = executor.with_subagent_tracker(tracker);
241 }
242 if let Some(task_scheduler) = task_scheduler {
243 executor = executor.with_task_scheduler(task_scheduler, false);
246 }
247 let executor = Arc::new(executor);
248 registry.register_builtin(Arc::new(TaskTool::new(Arc::clone(&executor))));
249 registry.register_builtin(Arc::new(ParallelTaskTool::new(Arc::clone(&executor))));
250}
251
252pub(crate) fn register_skill(
254 registry: &Arc<ToolRegistry>,
255 llm_client: Arc<dyn crate::llm::LlmClient>,
256 skill_registry: Arc<crate::skills::SkillRegistry>,
257 tool_executor: Arc<crate::tools::ToolExecutor>,
258 base_config: crate::agent::AgentConfig,
259) {
260 use crate::tools::skill::{SearchSkillsTool, SkillTool};
261 registry.register_builtin(Arc::new(SearchSkillsTool::new(Arc::clone(&skill_registry))));
262 registry.register_builtin(Arc::new(SkillTool::new(
263 skill_registry,
264 llm_client,
265 tool_executor,
266 base_config,
267 )));
268}
269
270pub fn register_generate_object(
275 registry: &Arc<ToolRegistry>,
276 llm_client: Arc<dyn crate::llm::LlmClient>,
277) {
278 registry.register_builtin(Arc::new(generate_object::GenerateObjectTool::new(
279 llm_client,
280 )));
281}
282
283#[cfg(test)]
284mod tests {
285 use super::safe_http_source_url;
286
287 #[test]
288 fn safe_source_url_removes_credentials_query_and_fragment() {
289 assert_eq!(
290 safe_http_source_url(
291 "HTTPS://user:password@Example.COM/report?access_token=secret#section"
292 )
293 .as_deref(),
294 Some("https://example.com/report")
295 );
296 assert!(safe_http_source_url("file:///tmp/source").is_none());
297 }
298}