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