Skip to main content

codetether_agent/tool/
mod.rs

1//! Tool system
2//!
3//! Tools are the executable capabilities available to agents.
4
5pub mod advanced_edit;
6pub mod agent;
7pub mod avatar;
8pub mod bash;
9pub mod batch;
10pub mod codesearch;
11pub mod confirm_edit;
12pub mod confirm_multiedit;
13pub mod edit;
14pub mod file;
15pub mod file_extras;
16pub mod go;
17pub mod image;
18pub mod invalid;
19pub mod lsp;
20pub mod mcp_bridge;
21pub mod mcp_tools;
22pub mod memory;
23pub mod multiedit;
24pub mod okr;
25pub mod patch;
26pub mod plan;
27pub mod podcast;
28pub mod prd;
29pub mod question;
30pub mod ralph;
31pub mod relay_autochat;
32pub mod rlm;
33pub mod sandbox;
34pub mod search;
35pub mod skill;
36pub mod swarm_execute;
37pub mod swarm_share;
38pub mod task;
39pub mod todo;
40pub mod undo;
41pub mod voice;
42pub mod webfetch;
43pub mod websearch;
44pub mod youtube;
45
46use anyhow::Result;
47use async_trait::async_trait;
48use serde::{Deserialize, Serialize};
49use serde_json::Value;
50use std::collections::HashMap;
51use std::sync::Arc;
52
53use crate::provider::Provider;
54pub use mcp_tools::{McpToolManager, McpToolWrapper};
55pub use sandbox::{PluginManifest, PluginRegistry, SigningKey, hash_bytes, hash_file};
56
57/// A tool that can be executed by an agent
58#[async_trait]
59pub trait Tool: Send + Sync {
60    /// Tool identifier
61    fn id(&self) -> &str;
62
63    /// Human-readable name
64    fn name(&self) -> &str;
65
66    /// Description for the LLM
67    fn description(&self) -> &str;
68
69    /// JSON Schema for parameters
70    fn parameters(&self) -> Value;
71
72    /// Execute the tool with given arguments
73    async fn execute(&self, args: Value) -> Result<ToolResult>;
74}
75
76/// Result from tool execution
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ToolResult {
79    pub output: String,
80    pub success: bool,
81    #[serde(default)]
82    pub metadata: HashMap<String, Value>,
83}
84
85impl ToolResult {
86    pub fn success(output: impl Into<String>) -> Self {
87        Self {
88            output: output.into(),
89            success: true,
90            metadata: HashMap::new(),
91        }
92    }
93
94    pub fn error(message: impl Into<String>) -> Self {
95        Self {
96            output: message.into(),
97            success: false,
98            metadata: HashMap::new(),
99        }
100    }
101
102    /// Create a structured error with code, tool name, missing fields, and example
103    ///
104    /// This helps LLMs self-correct by providing actionable information about what went wrong.
105    pub fn structured_error(
106        code: &str,
107        tool: &str,
108        message: &str,
109        missing_fields: Option<Vec<&str>>,
110        example: Option<Value>,
111    ) -> Self {
112        let mut error_obj = serde_json::json!({
113            "code": code,
114            "tool": tool,
115            "message": message,
116        });
117
118        if let Some(fields) = missing_fields {
119            error_obj["missing_fields"] = serde_json::json!(fields);
120        }
121
122        if let Some(ex) = example {
123            error_obj["example"] = ex;
124        }
125
126        let output = serde_json::to_string_pretty(&serde_json::json!({
127            "error": error_obj
128        }))
129        .unwrap_or_else(|_| format!("Error: {}", message));
130
131        let mut metadata = HashMap::new();
132        metadata.insert("error_code".to_string(), serde_json::json!(code));
133        metadata.insert("tool".to_string(), serde_json::json!(tool));
134
135        Self {
136            output,
137            success: false,
138            metadata,
139        }
140    }
141
142    pub fn with_metadata(mut self, key: impl Into<String>, value: Value) -> Self {
143        self.metadata.insert(key.into(), value);
144        self
145    }
146}
147
148/// Registry of available tools
149pub struct ToolRegistry {
150    tools: HashMap<String, Arc<dyn Tool>>,
151    plugin_registry: PluginRegistry,
152}
153
154impl std::fmt::Debug for ToolRegistry {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        f.debug_struct("ToolRegistry")
157            .field("tools", &self.tools.keys().collect::<Vec<_>>())
158            .finish()
159    }
160}
161
162impl ToolRegistry {
163    pub fn new() -> Self {
164        let _ = std::any::TypeId::of::<McpToolManager>();
165        let _ = std::any::TypeId::of::<McpToolWrapper>();
166        Self {
167            tools: HashMap::new(),
168            plugin_registry: PluginRegistry::from_env(),
169        }
170    }
171
172    /// Get a reference to the plugin registry for managing signed plugins.
173    pub fn plugins(&self) -> &PluginRegistry {
174        &self.plugin_registry
175    }
176
177    /// Register a tool
178    pub fn register(&mut self, tool: Arc<dyn Tool>) {
179        self.tools.insert(tool.id().to_string(), tool);
180    }
181
182    /// Get a tool by ID
183    pub fn get(&self, id: &str) -> Option<Arc<dyn Tool>> {
184        self.tools.get(id).cloned()
185    }
186
187    /// List all tool IDs
188    pub fn list(&self) -> Vec<&str> {
189        self.tools.keys().map(|s| s.as_str()).collect()
190    }
191
192    /// Get tool definitions for LLM
193    pub fn definitions(&self) -> Vec<crate::provider::ToolDefinition> {
194        self.tools
195            .values()
196            .map(|t| crate::provider::ToolDefinition {
197                name: t.id().to_string(),
198                description: t.description().to_string(),
199                parameters: t.parameters(),
200            })
201            .collect()
202    }
203
204    /// Register multiple tools at once
205    pub fn register_all(&mut self, tools: Vec<Arc<dyn Tool>>) {
206        for tool in tools {
207            self.register(tool);
208        }
209    }
210
211    /// Remove a tool by ID
212    pub fn unregister(&mut self, id: &str) -> Option<Arc<dyn Tool>> {
213        self.tools.remove(id)
214    }
215
216    /// Check if a tool exists
217    pub fn contains(&self, id: &str) -> bool {
218        self.tools.contains_key(id)
219    }
220
221    /// Get the number of registered tools
222    pub fn len(&self) -> usize {
223        self.tools.len()
224    }
225
226    /// Check if the registry is empty
227    pub fn is_empty(&self) -> bool {
228        self.tools.is_empty()
229    }
230
231    /// Create registry with all default tools (without batch)
232    pub fn with_defaults() -> Self {
233        let mut registry = Self::new();
234
235        registry.register(Arc::new(file::ReadTool::new()));
236        registry.register(Arc::new(file::WriteTool::new()));
237        registry.register(Arc::new(file::ListTool::new()));
238        registry.register(Arc::new(file::GlobTool::new()));
239        registry.register(Arc::new(file_extras::TreeTool::new()));
240        registry.register(Arc::new(file_extras::FileInfoTool::new()));
241        registry.register(Arc::new(file_extras::HeadTailTool::new()));
242        registry.register(Arc::new(file_extras::DiffTool::new()));
243        registry.register(Arc::new(search::GrepTool::new()));
244        registry.register(Arc::new(advanced_edit::AdvancedEditTool::new()));
245        registry.register(Arc::new(bash::BashTool::new()));
246        registry.register(Arc::new(lsp::LspTool::with_root(
247            std::env::current_dir()
248                .map(|p| format!("file://{}", p.display()))
249                .unwrap_or_default(),
250        )));
251        registry.register(Arc::new(webfetch::WebFetchTool::new()));
252        registry.register(Arc::new(multiedit::MultiEditTool::new()));
253        registry.register(Arc::new(websearch::WebSearchTool::new()));
254        registry.register(Arc::new(codesearch::CodeSearchTool::new()));
255        registry.register(Arc::new(patch::ApplyPatchTool::new()));
256        registry.register(Arc::new(todo::TodoReadTool::new()));
257        registry.register(Arc::new(todo::TodoWriteTool::new()));
258        registry.register(Arc::new(question::QuestionTool::new()));
259        registry.register(Arc::new(task::TaskTool::new()));
260        registry.register(Arc::new(plan::PlanEnterTool::new()));
261        registry.register(Arc::new(plan::PlanExitTool::new()));
262        registry.register(Arc::new(skill::SkillTool::new()));
263        registry.register(Arc::new(memory::MemoryTool::new()));
264        registry.register(Arc::new(ralph::RalphTool::new()));
265        registry.register(Arc::new(prd::PrdTool::new()));
266        registry.register(Arc::new(undo::UndoTool));
267        registry.register(Arc::new(voice::VoiceTool::new()));
268        registry.register(Arc::new(podcast::PodcastTool::new()));
269        registry.register(Arc::new(youtube::YouTubeTool::new()));
270        registry.register(Arc::new(avatar::AvatarTool::new()));
271        registry.register(Arc::new(image::ImageTool::new()));
272        registry.register(Arc::new(mcp_bridge::McpBridgeTool::new()));
273        registry.register(Arc::new(okr::OkrTool::new()));
274        // Register the invalid tool handler for graceful error handling
275        registry.register(Arc::new(invalid::InvalidTool::new()));
276        // Agent orchestration tool
277        registry.register(Arc::new(agent::AgentTool::new()));
278        // Swarm execution tool for parallel task execution
279        registry.register(Arc::new(swarm_execute::SwarmExecuteTool::new()));
280        // Relay autochat tool for autonomous agent communication
281        registry.register(Arc::new(relay_autochat::RelayAutoChatTool::new()));
282        // Go tool for autonomous OKR→PRD→Ralph pipeline
283        registry.register(Arc::new(go::GoTool::new()));
284
285        registry
286    }
287
288    /// Create registry with provider for tools that need it (like RalphTool)
289    pub fn with_provider(provider: Arc<dyn Provider>, model: String) -> Self {
290        let mut registry = Self::new();
291
292        registry.register(Arc::new(file::ReadTool::new()));
293        registry.register(Arc::new(file::WriteTool::new()));
294        registry.register(Arc::new(file::ListTool::new()));
295        registry.register(Arc::new(file::GlobTool::new()));
296        registry.register(Arc::new(file_extras::TreeTool::new()));
297        registry.register(Arc::new(file_extras::FileInfoTool::new()));
298        registry.register(Arc::new(file_extras::HeadTailTool::new()));
299        registry.register(Arc::new(file_extras::DiffTool::new()));
300        registry.register(Arc::new(search::GrepTool::new()));
301        registry.register(Arc::new(advanced_edit::AdvancedEditTool::new()));
302        registry.register(Arc::new(bash::BashTool::new()));
303        registry.register(Arc::new(lsp::LspTool::with_root(
304            std::env::current_dir()
305                .map(|p| format!("file://{}", p.display()))
306                .unwrap_or_default(),
307        )));
308        registry.register(Arc::new(webfetch::WebFetchTool::new()));
309        registry.register(Arc::new(multiedit::MultiEditTool::new()));
310        registry.register(Arc::new(websearch::WebSearchTool::new()));
311        registry.register(Arc::new(codesearch::CodeSearchTool::new()));
312        registry.register(Arc::new(patch::ApplyPatchTool::new()));
313        registry.register(Arc::new(todo::TodoReadTool::new()));
314        registry.register(Arc::new(todo::TodoWriteTool::new()));
315        registry.register(Arc::new(question::QuestionTool::new()));
316        registry.register(Arc::new(task::TaskTool::new()));
317        registry.register(Arc::new(plan::PlanEnterTool::new()));
318        registry.register(Arc::new(plan::PlanExitTool::new()));
319        registry.register(Arc::new(skill::SkillTool::new()));
320        registry.register(Arc::new(memory::MemoryTool::new()));
321        registry.register(Arc::new(rlm::RlmTool::new(
322            Arc::clone(&provider),
323            model.clone(),
324        )));
325        // RalphTool with provider for autonomous execution
326        registry.register(Arc::new(ralph::RalphTool::with_provider(provider, model)));
327        registry.register(Arc::new(prd::PrdTool::new()));
328        registry.register(Arc::new(undo::UndoTool));
329        registry.register(Arc::new(voice::VoiceTool::new()));
330        registry.register(Arc::new(podcast::PodcastTool::new()));
331        registry.register(Arc::new(youtube::YouTubeTool::new()));
332        registry.register(Arc::new(avatar::AvatarTool::new()));
333        registry.register(Arc::new(image::ImageTool::new()));
334        registry.register(Arc::new(mcp_bridge::McpBridgeTool::new()));
335        registry.register(Arc::new(okr::OkrTool::new()));
336        // Register the invalid tool handler for graceful error handling
337        registry.register(Arc::new(invalid::InvalidTool::new()));
338        // Agent orchestration tool
339        registry.register(Arc::new(agent::AgentTool::new()));
340        // Swarm execution tool for parallel task execution
341        registry.register(Arc::new(swarm_execute::SwarmExecuteTool::new()));
342        // Relay autochat tool for autonomous agent communication
343        registry.register(Arc::new(relay_autochat::RelayAutoChatTool::new()));
344        // Go tool for autonomous OKR→PRD→Ralph pipeline
345        registry.register(Arc::new(go::GoTool::new()));
346
347        registry
348    }
349
350    /// Create Arc-wrapped registry with batch tool properly initialized.
351    /// The batch tool needs a weak reference to the registry, so we use
352    /// a two-phase initialization pattern.
353    pub fn with_defaults_arc() -> Arc<Self> {
354        let mut registry = Self::with_defaults();
355
356        // Create batch tool without registry reference
357        let batch_tool = Arc::new(batch::BatchTool::new());
358        registry.register(batch_tool.clone());
359
360        // Wrap registry in Arc
361        let registry = Arc::new(registry);
362
363        // Now give batch tool a weak reference to the registry
364        batch_tool.set_registry(Arc::downgrade(&registry));
365
366        registry
367    }
368
369    /// Create Arc-wrapped registry with provider and batch tool properly initialized.
370    /// The batch tool needs a weak reference to the registry, so we use
371    /// a two-phase initialization pattern.
372    #[allow(dead_code)]
373    pub fn with_provider_arc(provider: Arc<dyn Provider>, model: String) -> Arc<Self> {
374        let mut registry = Self::with_provider(provider, model);
375
376        // Create batch tool without registry reference
377        let batch_tool = Arc::new(batch::BatchTool::new());
378        registry.register(batch_tool.clone());
379
380        // Wrap registry in Arc
381        let registry = Arc::new(registry);
382
383        // Now give batch tool a weak reference to the registry
384        batch_tool.set_registry(Arc::downgrade(&registry));
385
386        registry
387    }
388}
389
390impl Default for ToolRegistry {
391    fn default() -> Self {
392        Self::with_defaults()
393    }
394}