Skip to main content

agent_engine/tools/
mod.rs

1//! Tool system — trait, registry, and built-in tool implementations.
2//!
3//! All tools implement the `Tool` trait and are registered in `ToolRegistry`.
4//! Subagents get `ToolRegistry::without_subagent()` to prevent recursion.
5use crate::Result;
6use serde_json::Value;
7use std::path::PathBuf;
8use std::sync::{Arc, Mutex};
9
10// ── Module declarations ──────────────────────────────────────────────────────────
11
12mod bash;
13mod edit;
14mod extension;
15mod find;
16mod grep;
17mod ls;
18pub mod memory;
19mod memory_context;
20mod read;
21mod secret_prompt;
22mod subagent;
23mod write;
24
25pub mod activation;
26mod agent;
27pub mod catalog;
28pub mod discovery;
29pub mod ledger;
30pub mod output;
31mod registry;
32pub mod respond;
33pub mod send_channel;
34pub mod shell;
35pub(crate) mod util;
36pub mod watcher_exit;
37
38// ── Re-exports ──────────────────────────────────────────────────────────────────
39
40pub use crate::runtime::subagent::{
41    SubagentDisplayRow, SubagentHandle, SubagentRegistry, SubagentResult, SubagentState,
42    SubagentStatus,
43};
44pub use agent::resolve_agent_prompt;
45pub use bash::{bash_intermediary_snapshot, BashIntermediarySnapshot, BashTool};
46pub use discovery::{ActivateToolsTool, SearchToolsTool};
47pub use edit::EditTool;
48pub use extension::ExtensionTool;
49pub use find::FindTool;
50pub use grep::GrepTool;
51pub use ls::LsTool;
52pub use memory_context::MemoryContextTool;
53pub use read::ReadTool;
54pub use registry::ToolRegistry;
55pub use respond::RespondTool;
56pub use secret_prompt::SecretPromptQueue;
57pub use secret_prompt::{SecretPromptHandle, SecretPromptRequest};
58pub use send_channel::SendChannelTool;
59pub use shell::{ShellEndTool, ShellSendTool, ShellStartTool};
60pub use subagent::{
61    SubagentCollectTool, SubagentModelAuthorizeTool, SubagentModelsTool, SubagentResumeTool,
62    SubagentStartTool, SubagentStatusTool, SubagentSteerTool, SubagentTool,
63};
64pub use watcher_exit::WatcherExitTool;
65pub use write::WriteTool;
66
67// Re-export util items used by sibling tool modules via `super::`
68pub(crate) use util::{expand_path, strip_ansi, NEXT_SUBAGENT_ID};
69
70// Facade: expose finalize internals for integration tests without making the
71// subagent module pub. Tests import `agent_engine::tools::{build_completion_event, finalize_subagent}`.
72#[doc(hidden)]
73pub use subagent::finalize::{build_completion_event, finalize_subagent};
74
75// ── Tool Trait ──────────────────────────────────────────────────────────────────
76
77/// Streaming channels — carry partial tool output and stream events.
78///
79/// `tx_delta` is the Task 26 bounded delta lane (spec §8.4): a non-blocking
80/// coalesce-then-drop producer handle whose consumer enforces the UI-preview
81/// byte budget. It replaced the previous unbounded `mpsc` sender so a fast
82/// producer with a slow consumer can no longer grow memory without bound.
83pub struct ToolChannels {
84    pub tx_delta: Option<crate::tools::output::DeltaSender>,
85    pub tx_events: Option<tokio::sync::mpsc::UnboundedSender<crate::StreamEvent>>,
86}
87
88/// Runtime capability handles — shared services a tool may require.
89pub struct ToolCapabilities {
90    pub watcher_exit_path: Option<PathBuf>,
91    pub tool_register_tx: Option<tokio::sync::mpsc::UnboundedSender<Vec<Arc<dyn Tool>>>>,
92    pub session_manager: Option<std::sync::Arc<crate::tools::shell::SessionManager>>,
93    pub subagent_registry: Option<Arc<Mutex<SubagentRegistry>>>,
94    pub event_queue: Option<Arc<crate::events::EventQueue>>,
95    /// Current worker handle when this context belongs to a delegated
96    /// runtime. `None` denotes the foreground root.
97    pub delegation_parent: Option<String>,
98    pub secret_prompt: Option<SecretPromptHandle>,
99    /// Runtime-enforced delegation/lifecycle policy. When present, every spawn
100    /// path must authorize before creating channels, threads, or provider runtimes.
101    pub orchestration: Option<Arc<crate::orchestration::OrchestrationRuntime>>,
102    /// Discovery/activation capability context (Task 17): passive catalog
103    /// snapshot + retained session-set handle + host-supplied activation
104    /// authority. `None` (default for manual fixtures and non-stream
105    /// contexts) means the discovery/activation builtins fail typed and no
106    /// model-initiated activation is possible.
107    pub tool_activation: Option<crate::tools::discovery::ActivationCapability>,
108    /// Session-scoped exact MCP lease capability (Task 19): exact session
109    /// identity + shared runtime manager. `None` (default for non-stream
110    /// contexts) means deferred MCP tools fail typed and start nothing.
111    pub mcp_leases: Option<crate::mcp::McpLeaseCapability>,
112    /// Session-scoped exact EXTENSION lease capability (Task 20): exact
113    /// session identity + shared extension runtime manager. `None`
114    /// (default for non-stream contexts) means deferred extension tools
115    /// fail typed and start nothing.
116    pub extension_leases: Option<crate::extensions::lease::ExtensionLeaseCapability>,
117    /// Session-scoped continuous-memory context capability (task A4, spec
118    /// §7.2): shared per-session memory state + host-owned grant scope.
119    /// `None` (default for non-stream contexts) means the `memory_context`
120    /// builtin can commit nothing — lease-granting actions fail typed,
121    /// while `status`/`disable` still answer deterministically `Off`
122    /// (memory off requires no infrastructure).
123    pub memory_context: Option<crate::runtime::memory_context::MemoryContextCapability>,
124}
125
126/// Configuration limits and timeouts.
127pub struct ToolLimits {
128    /// Context-budget ceiling — final tool output entering history is truncated
129    /// to this. Applied AFTER the `after_tool_call` hook so compression
130    /// extensions see the FULL buffered output and can decide what to keep.
131    pub max_tool_output: usize,
132    /// Memory/pipe safety bound — the maximum bytes a tool may buffer
133    /// internally before truncating with a marker. Sized well above
134    /// `max_tool_output` so transform extensions are never starved.
135    pub max_tool_buffer: usize,
136    pub bash_timeout: u64,
137    pub bash_max_timeout: u64,
138    pub subagent_timeout: u64,
139}
140
141/// Context passed to tool execution — composition of channels, capabilities, and limits.
142pub struct ToolContext {
143    pub channels: ToolChannels,
144    pub capabilities: ToolCapabilities,
145    pub limits: ToolLimits,
146}
147
148/// Explicit runtime-origin identity of a registered tool implementation.
149///
150/// This is the metadata boundary the capability catalog trusts for
151/// provenance. The default is conservative: a tool that declares nothing is
152/// [`ToolOrigin::Unknown`] (or [`ToolOrigin::Extension`] when it already
153/// declares an owning extension via [`Tool::extension_id`]) — never builtin.
154#[derive(Clone, Debug, PartialEq, Eq)]
155pub enum ToolOrigin {
156    /// Compiled into this binary.
157    Builtin,
158    /// Registered by a locally installed extension (`<extension_id>:<tool>`
159    /// runtime names).
160    Extension { extension_id: String },
161    /// Bridged from an MCP server; identifies the server and the tool name
162    /// as the server knows it (not the prefixed runtime name).
163    Mcp {
164        server_id: String,
165        server_tool_name: String,
166    },
167    /// Declared by a plugin definition.
168    Plugin {
169        plugin_id: String,
170        tool_name: String,
171    },
172    /// No declared origin. Cataloged conservatively, never as builtin.
173    Unknown,
174}
175
176#[derive(Debug, Clone, PartialEq, Eq)]
177pub enum ConcurrencyKey {
178    Key(String),
179    Serialize,
180}
181
182/// The core trait for all tools. Implement this to add a new tool.
183#[async_trait::async_trait]
184pub trait Tool: Send + Sync {
185    /// Tool name as it appears in the API (e.g. "bash", "read").
186    fn name(&self) -> &str;
187
188    /// Human-readable description sent to the model.
189    fn description(&self) -> &str;
190
191    /// JSON Schema for the tool's parameters.
192    fn parameters(&self) -> Value;
193
194    /// Execute the tool with the given parameters.
195    async fn execute(&self, params: Value, ctx: ToolContext) -> Result<String>;
196
197    /// Owning extension id for tools registered by an extension. Built-in tools return `None`.
198    fn extension_id(&self) -> Option<&str> {
199        None
200    }
201
202    /// Runtime-origin identity used for catalog provenance. Conservative by
203    /// default: tools that declare nothing are `Unknown` (fail-closed source
204    /// trust), and tools that declare an owning extension classify as
205    /// extension-provided. Built-in implementations override this explicitly.
206    fn origin(&self) -> ToolOrigin {
207        match self.extension_id() {
208            Some(extension_id) => ToolOrigin::Extension {
209                extension_id: extension_id.to_string(),
210            },
211            None => ToolOrigin::Unknown,
212        }
213    }
214
215    /// Conservative effect class (Task 24, spec §8.2). The default keeps
216    /// unknown/dynamic tools `NonIdempotent` — serialized execution;
217    /// implementations opt IN to weaker classes explicitly.
218    fn effect(&self) -> crate::tools::catalog::ToolEffect {
219        crate::tools::catalog::ToolEffect::NonIdempotent
220    }
221
222    /// Key resolution derived from validated input. `Serialize` explicitly
223    /// fails closed into the global mutation lane; `None` is the conservative
224    /// default for tools with no key support.
225    fn concurrency_key(&self, _validated_input: &Value) -> Option<ConcurrencyKey> {
226        None
227    }
228}
229
230#[cfg(test)]
231mod test_helpers;