agent_code_lib/tools/mod.rs
1//! Tool system.
2//!
3//! Tools are the primary way the agent interacts with the environment.
4//! Each tool implements the `Tool` trait and is registered in the
5//! `ToolRegistry` for dispatch by name.
6//!
7//! # Architecture
8//!
9//! - `Tool` trait — defines the interface for all tools
10//! - `ToolRegistry` — collects tools and dispatches by name
11//! - `ToolExecutor` — manages concurrent/serial tool execution
12//! - Individual tool modules — concrete implementations
13//!
14//! # Tool execution flow
15//!
16//! 1. Input validation (schema check)
17//! 2. Permission check (allow/ask/deny)
18//! 3. Tool execution (`call`)
19//! 4. Result mapping (to API format)
20
21pub mod agent;
22pub mod ask_user;
23pub mod bash;
24pub mod bash_parse;
25pub mod executor;
26pub mod file_edit;
27pub mod file_read;
28pub mod file_write;
29pub mod glob;
30pub mod grep;
31pub mod lsp_tool;
32pub mod mcp_proxy;
33pub mod mcp_resources;
34pub mod multi_edit;
35pub mod notebook_edit;
36pub mod plan_mode;
37pub mod plugin_exec;
38pub mod powershell;
39pub mod registry;
40pub mod repl_tool;
41pub mod send_message;
42pub mod skill_tool;
43pub mod sleep_tool;
44pub mod tasks;
45pub mod todo_write;
46pub mod tool_search;
47pub mod web_fetch;
48pub mod web_search;
49pub mod worktree;
50
51use async_trait::async_trait;
52use serde::{Deserialize, Serialize};
53use std::path::PathBuf;
54use std::sync::Arc;
55use tokio_util::sync::CancellationToken;
56
57use crate::permissions::{PermissionChecker, PermissionDecision};
58
59/// The core trait that all tools must implement.
60///
61/// Tools are the bridge between the LLM's intentions and the local
62/// environment. Each tool defines its input schema (for the LLM),
63/// permission requirements, concurrency behavior, and execution logic.
64#[async_trait]
65pub trait Tool: Send + Sync {
66 /// Unique tool name used in API tool_use blocks.
67 fn name(&self) -> &'static str;
68
69 /// Human-readable description sent to the LLM.
70 fn description(&self) -> &'static str;
71
72 /// System prompt instructions for this tool.
73 fn prompt(&self) -> String {
74 self.description().to_string()
75 }
76
77 /// JSON Schema for the tool's input parameters.
78 fn input_schema(&self) -> serde_json::Value;
79
80 /// Execute the tool with validated input.
81 async fn call(
82 &self,
83 input: serde_json::Value,
84 ctx: &ToolContext,
85 ) -> Result<ToolResult, crate::error::ToolError>;
86
87 /// Whether this tool only reads state (no mutations).
88 fn is_read_only(&self) -> bool {
89 false
90 }
91
92 /// Whether this tool can safely run concurrently with other tools.
93 /// Read-only tools are typically concurrency-safe.
94 fn is_concurrency_safe(&self) -> bool {
95 self.is_read_only()
96 }
97
98 /// Whether this tool is destructive (deletes data, force-pushes, etc.).
99 fn is_destructive(&self) -> bool {
100 false
101 }
102
103 /// Whether this tool is currently enabled in the environment.
104 fn is_enabled(&self) -> bool {
105 true
106 }
107
108 /// Maximum result size in characters before truncation.
109 fn max_result_size_chars(&self) -> usize {
110 100_000
111 }
112
113 /// Check permissions for executing this tool with the given input.
114 async fn check_permissions(
115 &self,
116 input: &serde_json::Value,
117 checker: &PermissionChecker,
118 ) -> PermissionDecision {
119 if self.is_read_only() {
120 PermissionDecision::Allow
121 } else {
122 checker.check(self.name(), input)
123 }
124 }
125
126 /// Validate tool input before execution.
127 fn validate_input(&self, _input: &serde_json::Value) -> Result<(), String> {
128 Ok(())
129 }
130
131 /// Extract a file path from the input, if applicable (for permission matching).
132 fn get_path(&self, _input: &serde_json::Value) -> Option<PathBuf> {
133 None
134 }
135}
136
137/// Permission prompt response from the UI layer.
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub enum PermissionResponse {
140 AllowOnce,
141 AllowSession,
142 Deny,
143}
144
145/// Trait for prompting the user for permission decisions.
146/// Implemented by the CLI's UI layer; the lib engine uses this abstraction.
147pub trait PermissionPrompter: Send + Sync {
148 fn ask(
149 &self,
150 tool_name: &str,
151 description: &str,
152 input_preview: Option<&str>,
153 ) -> PermissionResponse;
154}
155
156/// Default prompter that always allows (for non-interactive/testing).
157pub struct AutoAllowPrompter;
158impl PermissionPrompter for AutoAllowPrompter {
159 fn ask(&self, _: &str, _: &str, _: Option<&str>) -> PermissionResponse {
160 PermissionResponse::AllowOnce
161 }
162}
163
164/// Context passed to every tool during execution.
165///
166/// Provides the working directory, cancellation token, permission
167/// checker, file cache, and other shared state. Created by the
168/// executor before each tool call.
169pub struct ToolContext {
170 /// Current working directory.
171 pub cwd: PathBuf,
172 /// Cancellation token for cooperative cancellation.
173 pub cancel: CancellationToken,
174 /// Permission checker instance.
175 pub permission_checker: Arc<PermissionChecker>,
176 /// Whether to produce verbose output.
177 pub verbose: bool,
178 /// Plan mode: only read-only tools allowed.
179 pub plan_mode: bool,
180 /// File content cache for avoiding redundant reads.
181 pub file_cache: Option<Arc<tokio::sync::Mutex<crate::services::file_cache::FileCache>>>,
182 /// Permission denial tracker for reporting.
183 pub denial_tracker:
184 Option<Arc<tokio::sync::Mutex<crate::permissions::tracking::DenialTracker>>>,
185 /// Shared background task manager.
186 pub task_manager: Option<Arc<crate::services::background::TaskManager>>,
187 /// Tools allowed for the rest of the session (via "Allow for session" prompt).
188 pub session_allows: Option<Arc<tokio::sync::Mutex<std::collections::HashSet<String>>>>,
189 /// Permission prompter for interactive approval.
190 pub permission_prompter: Option<Arc<dyn PermissionPrompter>>,
191 /// Process-level sandbox executor.
192 ///
193 /// `None` means sandboxing is unavailable for this context
194 /// (e.g. parallel read-only retry paths); subprocess-spawning tools
195 /// should treat `None` as "pass through unchanged". The main query
196 /// loop populates this from [`crate::config::SandboxConfig`].
197 pub sandbox: Option<Arc<crate::sandbox::SandboxExecutor>>,
198}
199
200/// Result of a tool execution.
201///
202/// Contains the output text and whether it represents an error.
203/// Injected into the conversation as a `ToolResult` content block.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct ToolResult {
206 /// The main output content.
207 pub content: String,
208 /// Whether the result represents an error.
209 pub is_error: bool,
210}
211
212impl ToolResult {
213 /// Create a successful result.
214 pub fn success(content: impl Into<String>) -> Self {
215 Self {
216 content: content.into(),
217 is_error: false,
218 }
219 }
220
221 /// Create an error result.
222 pub fn error(content: impl Into<String>) -> Self {
223 Self {
224 content: content.into(),
225 is_error: true,
226 }
227 }
228}
229
230/// Schema information for a tool, used when building API requests.
231#[derive(Debug, Clone, Serialize)]
232pub struct ToolSchema {
233 pub name: &'static str,
234 pub description: &'static str,
235 pub input_schema: serde_json::Value,
236}
237
238impl<T: Tool + ?Sized> From<&T> for ToolSchema {
239 fn from(tool: &T) -> Self {
240 Self {
241 name: tool.name(),
242 description: tool.description(),
243 input_schema: tool.input_schema(),
244 }
245 }
246}