Skip to main content

ai_agent/
tool.rs

1// Source: src/Tool.ts
2
3//! Tool type definitions, traits, and utilities translated from the TypeScript SDK.
4//!
5//! This module defines the core tool system including:
6//! - The `Tool` trait with all optional methods (mirroring the TS `Tool` type)
7//! - Tool permission context and validation types
8//! - Tool use context for runtime execution
9//! - Progress tracking types
10//! - Utility functions for tool lookup and matching
11//! - The `build_tool` pattern for constructing tools with safe defaults
12
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::sync::Arc;
16
17// ---------------------------------------------------------------------------
18// Re-exports from sibling modules (avoid cycles, match TS re-export pattern)
19// ---------------------------------------------------------------------------
20pub use crate::types::hooks::HookProgress;
21pub use crate::types::message::{
22    AssistantMessage, AttachmentMessage, Message, ProgressMessage, SystemLocalCommandMessage,
23    SystemMessage, UserMessage,
24};
25pub use crate::types::tools::ToolProgressData;
26pub use crate::types::{ToolDefinition, ToolInputSchema};
27
28// ---------------------------------------------------------------------------
29// Tool input JSON schema
30// ---------------------------------------------------------------------------
31
32/// JSON schema for tool input, used when a tool specifies its schema directly
33/// in JSON Schema format rather than converting from a Zod schema.
34pub type ToolInputJsonSchema = serde_json::Value;
35
36// ---------------------------------------------------------------------------
37// Query chain tracking
38// ---------------------------------------------------------------------------
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct QueryChainTracking {
42    #[serde(rename = "chainId")]
43    pub chain_id: String,
44    pub depth: u32,
45}
46
47// ---------------------------------------------------------------------------
48// Validation result
49// ---------------------------------------------------------------------------
50
51/// Result of validating tool input.
52#[derive(Debug, Clone)]
53pub enum ValidationResult {
54    /// Input is valid.
55    Valid,
56    /// Input is invalid with an error message and code.
57    Invalid { message: String, error_code: i64 },
58}
59
60// ---------------------------------------------------------------------------
61// SetToolJSX callback
62// ---------------------------------------------------------------------------
63
64/// Callback to update tool JSX/UI rendering state.
65/// In Rust this is a generic boxed async callback.
66pub type SetToolJsxFn = Arc<
67    dyn Fn(Option<SetToolJsxArgs>) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + 'static>>
68        + Send
69        + Sync,
70>;
71
72#[derive(Debug, Clone)]
73pub struct SetToolJsxArgs {
74    pub should_hide_prompt_input: bool,
75    pub should_continue_animation: bool,
76    pub show_spinner: bool,
77    pub is_local_jsx: bool,
78    pub is_immediate: bool,
79    /// Set to true to clear a local JSX command (e.g., from its onDone callback)
80    pub clear_local_jsx: bool,
81}
82
83// ---------------------------------------------------------------------------
84// Compact progress events (re-exported from types)
85// ---------------------------------------------------------------------------
86
87pub use crate::types::CompactProgressEvent;
88
89// ---------------------------------------------------------------------------
90// Tool permission context (re-export + extension)
91// ---------------------------------------------------------------------------
92
93pub use crate::types::permissions::ToolPermissionContext;
94
95/// Creates an empty tool permission context (matches TS `getEmptyToolPermissionContext`).
96pub fn get_empty_tool_permission_context() -> ToolPermissionContext {
97    ToolPermissionContext {
98        mode: "default".to_string(),
99        additional_working_directories: HashMap::new(),
100        always_allow_rules: crate::types::permissions::ToolPermissionRulesBySource {
101            user_settings: None,
102            project_settings: None,
103            local_settings: None,
104            flag_settings: None,
105            policy_settings: None,
106            cli_arg: None,
107            command: None,
108            session: None,
109        },
110        always_deny_rules: crate::types::permissions::ToolPermissionRulesBySource {
111            user_settings: None,
112            project_settings: None,
113            local_settings: None,
114            flag_settings: None,
115            policy_settings: None,
116            cli_arg: None,
117            command: None,
118            session: None,
119        },
120        always_ask_rules: crate::types::permissions::ToolPermissionRulesBySource {
121            user_settings: None,
122            project_settings: None,
123            local_settings: None,
124            flag_settings: None,
125            policy_settings: None,
126            cli_arg: None,
127            command: None,
128            session: None,
129        },
130        is_bypass_permissions_mode_available: false,
131        stripped_dangerous_rules: None,
132        should_avoid_permission_prompts: None,
133        await_automated_checks_before_dialog: None,
134        pre_plan_mode: None,
135    }
136}
137
138// ---------------------------------------------------------------------------
139// Tool use context
140// ---------------------------------------------------------------------------
141
142/// Context passed to tool calls during execution, providing access to
143/// configuration, state, callbacks, and other runtime infrastructure.
144///
145/// This mirrors the TypeScript `ToolUseContext` type.
146pub struct ToolUseContext {
147    pub options: ToolUseContextOptions,
148    /// Abort signal (placeholder - wired to the consumer's AbortController).
149    pub abort_signal: Option<()>,
150    pub read_file_state: Option<Arc<dyn std::any::Any + Send + Sync>>,
151    pub get_app_state: Box<dyn Fn() -> Box<dyn std::any::Any> + Send + Sync>,
152    pub set_app_state:
153        Box<dyn Fn(Box<dyn Fn(Box<dyn std::any::Any>) -> Box<dyn std::any::Any>>) + Send + Sync>,
154    pub set_app_state_for_tasks: Option<
155        Box<dyn Fn(Box<dyn Fn(Box<dyn std::any::Any>) -> Box<dyn std::any::Any>>) + Send + Sync>,
156    >,
157    pub handle_elicitation: Option<
158        Arc<
159            dyn Fn(
160                    String,
161                    serde_json::Value,
162                    (),
163                )
164                    -> std::pin::Pin<Box<dyn Future<Output = serde_json::Value> + Send>>
165                + Send
166                + Sync,
167        >,
168    >,
169    pub set_tool_jsx: Option<SetToolJsxFn>,
170    pub add_notification: Option<Arc<dyn Fn(serde_json::Value) + Send + Sync>>,
171    pub append_system_message: Option<Box<dyn Fn(SystemMessage) + Send + Sync>>,
172    pub send_os_notification: Option<Box<dyn Fn(String, String) + Send + Sync>>,
173    pub nested_memory_attachment_triggers:
174        Option<Arc<std::sync::Mutex<std::collections::HashSet<String>>>>,
175    pub loaded_nested_memory_paths:
176        Option<Arc<std::sync::Mutex<std::collections::HashSet<String>>>>,
177    pub dynamic_skill_dir_triggers:
178        Option<Arc<std::sync::Mutex<std::collections::HashSet<String>>>>,
179    pub discovered_skill_names: Option<Arc<std::sync::Mutex<std::collections::HashSet<String>>>>,
180    pub user_modified: bool,
181    pub set_in_progress_tool_use_ids: Box<
182        dyn Fn(Box<dyn Fn(&std::collections::HashSet<String>) -> std::collections::HashSet<String>>)
183            + Send
184            + Sync,
185    >,
186    pub set_has_interruptible_tool_in_progress: Option<Box<dyn Fn(bool) + Send + Sync>>,
187    pub set_response_length: Box<dyn Fn(Box<dyn Fn(usize) -> usize>) + Send + Sync>,
188    pub push_api_metrics_entry: Option<Box<dyn Fn(u64) + Send + Sync>>,
189    pub set_stream_mode: Option<Box<dyn Fn(String) + Send + Sync>>,
190    pub on_compact_progress: Option<Box<dyn Fn(CompactProgressEvent) + Send + Sync>>,
191    pub set_sdk_status: Option<Box<dyn Fn(String) + Send + Sync>>,
192    pub open_message_selector: Option<Box<dyn Fn() + Send + Sync>>,
193    pub update_file_history_state:
194        Box<dyn Fn(Box<dyn Fn(Box<dyn std::any::Any>) -> Box<dyn std::any::Any>>) + Send + Sync>,
195    pub update_attribution_state:
196        Box<dyn Fn(Box<dyn Fn(Box<dyn std::any::Any>) -> Box<dyn std::any::Any>>) + Send + Sync>,
197    pub set_conversation_id: Option<Box<dyn Fn(String) + Send + Sync>>,
198    pub agent_id: Option<String>,
199    pub agent_type: Option<String>,
200    pub require_can_use_tool: bool,
201    pub messages: Vec<Message>,
202    pub file_reading_limits: Option<FileReadingLimits>,
203    pub glob_limits: Option<GlobLimits>,
204    pub tool_decisions: Option<Arc<std::sync::Mutex<HashMap<String, ToolDecisionEntry>>>>,
205    pub query_tracking: Option<QueryChainTracking>,
206    pub request_prompt: Option<
207        Arc<
208            dyn Fn(
209                    String,
210                    Option<String>,
211                ) -> Box<
212                    dyn Fn(
213                            serde_json::Value,
214                        )
215                            -> std::pin::Pin<Box<dyn Future<Output = serde_json::Value> + Send>>
216                        + Send,
217                > + Send
218                + Sync,
219        >,
220    >,
221    pub tool_use_id: Option<String>,
222    pub critical_system_reminder_experimental: Option<String>,
223    pub preserve_tool_use_results: bool,
224    pub local_denial_tracking: Option<Arc<std::sync::Mutex<DenialTrackingState>>>,
225    pub content_replacement_state: Option<Arc<dyn std::any::Any + Send + Sync>>,
226    pub rendered_system_prompt: Option<Arc<dyn std::any::Any + Send + Sync>>,
227}
228
229impl ToolUseContext {
230    /// Create a minimal stub ToolUseContext for callers (like query engine)
231    /// that don't have a real one but need one for memory extraction.
232    pub fn stub() -> Self {
233        use std::collections::HashSet;
234        use crate::types::message::Message;
235        Self {
236            options: ToolUseContextOptions {
237                commands: Vec::new(),
238                debug: false,
239                main_loop_model: String::new(),
240                tools: Vec::new(),
241                verbose: false,
242                thinking_config: None,
243                mcp_clients: Vec::new(),
244                mcp_resources: HashMap::new(),
245                is_non_interactive_session: false,
246                agent_definitions: AgentDefinitionsResult {
247                    active_agents: Vec::new(),
248                    all_agents: Vec::new(),
249                },
250                max_budget_usd: None,
251                custom_system_prompt: None,
252                append_system_prompt: None,
253                query_source: None,
254                refresh_tools: None,
255            },
256            abort_signal: None,
257            read_file_state: None,
258            get_app_state: Box::new(|| Box::new(()) as Box<dyn std::any::Any>),
259            set_app_state: Box::new(|_| {}),
260            set_app_state_for_tasks: None,
261            handle_elicitation: None,
262            set_tool_jsx: None,
263            add_notification: None,
264            append_system_message: None,
265            send_os_notification: None,
266            nested_memory_attachment_triggers: None,
267            loaded_nested_memory_paths: None,
268            dynamic_skill_dir_triggers: None,
269            discovered_skill_names: None,
270            user_modified: false,
271            set_in_progress_tool_use_ids: Box::new(|_| ()),
272            set_has_interruptible_tool_in_progress: None,
273            set_response_length: Box::new(|_| {}),
274            push_api_metrics_entry: None,
275            set_stream_mode: None,
276            on_compact_progress: None,
277            set_sdk_status: None,
278            open_message_selector: None,
279            update_file_history_state: Box::new(|_| ()),
280            update_attribution_state: Box::new(|_| ()),
281            set_conversation_id: None,
282            agent_id: None,
283            agent_type: None,
284            require_can_use_tool: false,
285            messages: Vec::<Message>::new(),
286            file_reading_limits: None,
287            glob_limits: None,
288            tool_decisions: None,
289            query_tracking: None,
290            request_prompt: None,
291            tool_use_id: None,
292            critical_system_reminder_experimental: None,
293            preserve_tool_use_results: false,
294            local_denial_tracking: None,
295            content_replacement_state: None,
296            rendered_system_prompt: None,
297        }
298    }
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct FileReadingLimits {
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub max_tokens: Option<u64>,
305    #[serde(rename = "maxSizeBytes", skip_serializing_if = "Option::is_none")]
306    pub max_size_bytes: Option<u64>,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct GlobLimits {
311    #[serde(rename = "maxResults", skip_serializing_if = "Option::is_none")]
312    pub max_results: Option<u64>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct ToolDecisionEntry {
317    pub source: String,
318    pub decision: String, // "accept" | "reject"
319    pub timestamp: u64,
320}
321
322#[derive(Debug, Clone, Default, Serialize, Deserialize)]
323pub struct DenialTrackingState {
324    pub count: u32,
325}
326
327#[derive(Clone)]
328pub struct ToolUseContextOptions {
329    pub commands: Vec<serde_json::Value>,
330    pub debug: bool,
331    pub main_loop_model: String,
332    pub tools: Vec<ToolDefinition>,
333    pub verbose: bool,
334    pub thinking_config: Option<serde_json::Value>,
335    pub mcp_clients: Vec<serde_json::Value>,
336    pub mcp_resources: HashMap<String, Vec<serde_json::Value>>,
337    pub is_non_interactive_session: bool,
338    pub agent_definitions: AgentDefinitionsResult,
339    pub max_budget_usd: Option<f64>,
340    pub custom_system_prompt: Option<String>,
341    pub append_system_prompt: Option<String>,
342    pub query_source: Option<String>,
343    #[allow(clippy::type_complexity)]
344    pub refresh_tools: Option<Arc<dyn Fn() -> Vec<ToolDefinition> + Send + Sync>>,
345}
346
347#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct AgentDefinitionsResult {
349    #[serde(rename = "activeAgents")]
350    pub active_agents: Vec<serde_json::Value>,
351    #[serde(rename = "allAgents")]
352    pub all_agents: Vec<serde_json::Value>,
353}
354
355// ---------------------------------------------------------------------------
356// Tool progress
357// ---------------------------------------------------------------------------
358
359/// Progress associated with a specific tool use.
360#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct ToolProgress<P = ToolProgressData> {
362    #[serde(rename = "toolUseID")]
363    pub tool_use_id: String,
364    pub data: P,
365}
366
367/// Filter out hook_progress messages from a list of progress messages.
368///
369/// Mirrors `filterToolProgressMessages` from the TypeScript SDK.
370pub fn filter_tool_progress_messages(
371    progress_messages: &[ProgressMessage],
372) -> Vec<ProgressMessage> {
373    progress_messages
374        .iter()
375        .filter(|msg| {
376            msg.progress
377                .as_ref()
378                .and_then(|d| d.get("kind"))
379                .and_then(|k| k.as_str())
380                != Some("hook_progress")
381        })
382        .cloned()
383        .collect()
384}
385
386// ---------------------------------------------------------------------------
387// Tool result
388// ---------------------------------------------------------------------------
389
390/// Result returned by a tool call.
391///
392/// Mirrors the TypeScript `ToolResult<T>` type.
393pub struct ToolResult<T = serde_json::Value> {
394    pub data: T,
395    pub new_messages: Option<Vec<ToolResultMessage>>,
396    /// context_modifier is only honored for tools that aren't concurrency safe.
397    pub context_modifier: Option<Arc<dyn Fn(&ToolUseContext) -> ToolUseContext + Send + Sync>>,
398    /// MCP protocol metadata (structuredContent, _meta) to pass through to SDK consumers
399    pub mcp_meta: Option<McpMeta>,
400}
401
402/// Messages that can be produced as part of a tool result.
403pub enum ToolResultMessage {
404    User(UserMessage),
405    Assistant(AssistantMessage),
406    Attachment(AttachmentMessage),
407    System(SystemMessage),
408}
409
410/// MCP metadata passed through to SDK consumers.
411#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct McpMeta {
413    #[serde(skip_serializing_if = "Option::is_none")]
414    pub meta: Option<HashMap<String, serde_json::Value>>,
415    #[serde(rename = "structuredContent", skip_serializing_if = "Option::is_none")]
416    pub structured_content: Option<HashMap<String, serde_json::Value>>,
417}
418
419// ---------------------------------------------------------------------------
420// Tool call progress callback
421// ---------------------------------------------------------------------------
422
423/// Callback type for reporting progress during tool execution.
424pub type ToolCallProgressFn<P = ToolProgressData> = Arc<
425    dyn Fn(ToolProgress<P>) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync,
426>;
427
428// ---------------------------------------------------------------------------
429// Tool matching utilities
430// ---------------------------------------------------------------------------
431
432/// Checks if a tool matches the given name (primary name or alias).
433///
434/// Mirrors `toolMatchesName` from the TypeScript SDK.
435pub fn tool_matches_name(name: &str, aliases: Option<&[String]>, target: &str) -> bool {
436    name == target || aliases.is_some_and(|a| a.iter().any(|alias| alias == target))
437}
438
439/// Finds a tool by name or alias from a list of tool definitions.
440///
441/// Mirrors `findToolByName` from the TypeScript SDK.
442pub fn find_tool_by_name<'a>(
443    tools: &'a [ToolDefinition],
444    name: &str,
445) -> Option<&'a ToolDefinition> {
446    tools
447        .iter()
448        .find(|t| tool_matches_name(&t.name, None, name))
449}
450
451// ---------------------------------------------------------------------------
452// Tool result block param
453// ---------------------------------------------------------------------------
454
455/// Maps a tool result to the Anthropic SDK ToolResultBlockParam shape.
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct ToolResultBlockParam {
458    #[serde(rename = "type")]
459    pub block_type: String,
460    #[serde(rename = "tool_use_id")]
461    pub tool_use_id: String,
462    pub content: Vec<ContentBlockParam>,
463    #[serde(rename = "is_error", skip_serializing_if = "Option::is_none")]
464    pub is_error: Option<bool>,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
468#[serde(tag = "type")]
469pub enum ContentBlockParam {
470    #[serde(rename = "text")]
471    Text { text: String },
472    #[serde(rename = "image")]
473    Image { source: ImageSource },
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct ImageSource {
478    #[serde(rename = "type")]
479    pub source_type: String,
480    pub media_type: String,
481    pub data: String,
482}
483
484// ---------------------------------------------------------------------------
485// The Tool trait
486// ---------------------------------------------------------------------------
487
488/// A tool that can be called by the agent.
489///
490/// This trait mirrors the TypeScript `Tool` type with all its optional methods.
491/// Default implementations are provided for all optional methods so that tool
492/// implementations only need to define the core methods.
493///
494/// # Required methods
495/// - `name` - The tool's name
496/// - `input_schema` - JSON schema for tool input
497/// - `call` - Execute the tool
498/// - `description` - Generate a description for the tool
499/// - `prompt` - Generate the system prompt fragment for this tool
500/// - `user_facing_name` - Human-readable name
501/// - `to_auto_classifier_input` - Compact representation for the security classifier
502/// - `map_tool_result_to_tool_result_block_param` - Convert result to SDK format
503/// - `render_tool_use_message` - Render the tool use message (text representation)
504///
505/// # Optional methods (with defaults)
506/// All other methods have sensible defaults and can be omitted.
507pub trait Tool: Send + Sync {
508    // ---- Core required methods ----
509
510    /// The tool's primary name.
511    fn name(&self) -> &str;
512
513    /// Optional aliases for backwards compatibility when a tool is renamed.
514    fn aliases(&self) -> Option<&[String]> {
515        None
516    }
517
518    /// One-line capability phrase used by ToolSearch for keyword matching.
519    /// 3-10 words, no trailing period.
520    fn search_hint(&self) -> Option<&str> {
521        None
522    }
523
524    /// JSON schema for tool input.
525    fn input_schema(&self) -> ToolInputSchema;
526
527    /// Optional JSON schema (used directly instead of converting from Zod).
528    fn input_json_schema(&self) -> Option<ToolInputJsonSchema> {
529        None
530    }
531
532    /// Output schema for the tool result.
533    fn output_schema(&self) -> Option<serde_json::Value> {
534        None
535    }
536
537    /// Execute the tool with the given input and context.
538    fn call(
539        &self,
540        args: serde_json::Value,
541        context: Arc<ToolUseContext>,
542        can_use_tool: Arc<
543            dyn Fn(
544                    &ToolDefinition,
545                    &serde_json::Value,
546                    Arc<ToolUseContext>,
547                    Arc<AssistantMessage>,
548                    &str,
549                    bool,
550                ) -> std::pin::Pin<
551                    Box<
552                        dyn Future<Output = Result<PermissionDecision, crate::error::AgentError>>
553                            + Send,
554                    >,
555                > + Send
556                + Sync,
557        >,
558        parent_message: Arc<AssistantMessage>,
559        on_progress: Option<Arc<dyn Fn(ToolProgress) + Send + Sync>>,
560    ) -> std::pin::Pin<
561        Box<dyn Future<Output = Result<ToolResult, crate::error::AgentError>> + Send + '_>,
562    >;
563
564    /// Generate a description for this tool given specific input.
565    fn description(
566        &self,
567        input: serde_json::Value,
568        is_non_interactive_session: bool,
569        tool_permission_context: &ToolPermissionContext,
570        tools: &[ToolDefinition],
571    ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send + '_>>;
572
573    // ---- Permission / validation ----
574
575    /// Validates tool input before execution.
576    /// Returns `ValidationResult::Valid` or `ValidationResult::Invalid`.
577    fn validate_input(
578        &self,
579        _input: serde_json::Value,
580        _context: Arc<ToolUseContext>,
581    ) -> std::pin::Pin<Box<dyn Future<Output = ValidationResult> + Send + '_>> {
582        Box::pin(async { ValidationResult::Valid })
583    }
584
585    /// Determines if the user is asked for permission. Only called after
586    /// `validate_input` passes.
587    fn check_permissions(
588        &self,
589        input: serde_json::Value,
590        _context: Arc<ToolUseContext>,
591    ) -> std::pin::Pin<Box<dyn Future<Output = PermissionResult> + Send + '_>> {
592        Box::pin(async move {
593            let updated_input = input.as_object().map(|o| {
594                o.clone()
595                    .into_iter()
596                    .collect::<HashMap<String, serde_json::Value>>()
597            });
598            PermissionResult::Allow {
599                updated_input,
600                user_modified: None,
601            }
602        })
603    }
604
605    /// Prepare a matcher for hook `if` conditions (permission-rule patterns).
606    /// Called once per hook-input pair; returns a closure called per hook pattern.
607    fn prepare_permission_matcher(
608        &self,
609        _input: serde_json::Value,
610    ) -> Option<Arc<dyn Fn(&str) -> bool + Send + Sync>> {
611        None
612    }
613
614    // ---- Tool properties ----
615
616    /// Whether the tool is currently enabled. Defaults to `true`.
617    fn is_enabled(&self) -> bool {
618        true
619    }
620
621    /// Whether the tool is safe to run concurrently. Defaults to `false` (fail-closed).
622    fn is_concurrency_safe(&self, _input: serde_json::Value) -> bool {
623        false
624    }
625
626    /// Whether the tool is read-only. Defaults to `false` (assume writes).
627    fn is_read_only(&self, _input: serde_json::Value) -> bool {
628        false
629    }
630
631    /// Whether the tool performs irreversible operations (delete, overwrite, send).
632    /// Defaults to `false`.
633    fn is_destructive(&self, _input: serde_json::Value) -> bool {
634        false
635    }
636
637    /// Whether two inputs are equivalent (for deduplication).
638    fn inputs_equivalent(&self, _a: serde_json::Value, _b: serde_json::Value) -> bool {
639        false
640    }
641
642    /// Maximum size in characters for tool result before it gets persisted to disk.
643    fn max_result_size_chars(&self) -> usize {
644        usize::MAX
645    }
646
647    /// When true, enables strict mode for this tool.
648    fn strict(&self) -> bool {
649        false
650    }
651
652    /// Whether this tool is deferred (sent with defer_loading: true) and requires
653    /// ToolSearch to be used before it can be called.
654    fn should_defer(&self) -> bool {
655        false
656    }
657
658    /// When true, this tool is never deferred.
659    fn always_load(&self) -> bool {
660        false
661    }
662
663    /// For MCP tools: the server and tool names.
664    fn mcp_info(&self) -> Option<McpToolInfo> {
665        None
666    }
667
668    /// Whether this is an MCP tool.
669    fn is_mcp(&self) -> bool {
670        false
671    }
672
673    /// Whether this is an LSP tool.
674    fn is_lsp(&self) -> bool {
675        false
676    }
677
678    // ---- Interrupt behavior ----
679
680    /// What should happen when the user submits a new message while this tool
681    /// is running.
682    /// - `"cancel"` - stop the tool and discard its result
683    /// - `"block"` - keep running; the new message waits
684    ///
685    /// Defaults to `"block"` when not implemented.
686    fn interrupt_behavior(&self) -> &str {
687        "block"
688    }
689
690    // ---- UI rendering methods ----
691
692    /// Returns information about whether this tool use is a search or read
693    /// operation that should be collapsed into a condensed display in the UI.
694    fn is_search_or_read_command(&self, _input: serde_json::Value) -> SearchOrReadInfo {
695        SearchOrReadInfo {
696            is_search: false,
697            is_read: false,
698            is_list: false,
699        }
700    }
701
702    /// Whether this tool operates in an open-world context.
703    fn is_open_world(&self, _input: serde_json::Value) -> bool {
704        false
705    }
706
707    /// Whether this tool requires user interaction.
708    fn requires_user_interaction(&self) -> bool {
709        false
710    }
711
712    /// Called on copies of tool_use input before observers see it.
713    /// Mutate in place to add legacy/derived fields. Must be idempotent.
714    fn backfill_observable_input(&self, _input: &mut serde_json::Value) {}
715
716    /// Get the file path this tool operates on (for tools that work on files).
717    fn get_path(&self, _input: serde_json::Value) -> Option<String> {
718        None
719    }
720
721    /// Human-readable name for display.
722    fn user_facing_name(&self, _input: Option<&serde_json::Value>) -> String {
723        self.name().to_string()
724    }
725
726    /// Background color key for the theme.
727    fn user_facing_name_background_color(
728        &self,
729        _input: Option<&serde_json::Value>,
730    ) -> Option<String> {
731        None
732    }
733
734    /// Whether this tool is a transparent wrapper (delegates rendering to progress handler).
735    fn is_transparent_wrapper(&self) -> bool {
736        false
737    }
738
739    /// Returns a short string summary for compact views.
740    fn get_tool_use_summary(&self, _input: Option<&serde_json::Value>) -> Option<String> {
741        None
742    }
743
744    /// Returns a present-tense activity description for spinner display.
745    fn get_activity_description(&self, _input: Option<&serde_json::Value>) -> Option<String> {
746        None
747    }
748
749    /// Compact representation for the auto-mode security classifier.
750    fn to_auto_classifier_input(&self, _input: serde_json::Value) -> serde_json::Value {
751        serde_json::Value::String(String::new())
752    }
753
754    /// Convert tool result to SDK ToolResultBlockParam.
755    fn map_tool_result_to_tool_result_block_param(
756        &self,
757        content: serde_json::Value,
758        tool_use_id: &str,
759    ) -> ToolResultBlockParam;
760
761    /// Render the tool result message. Optional - omitted means nothing renders
762    /// (same as returning null).
763    fn render_tool_result_message(
764        &self,
765        _content: serde_json::Value,
766        _progress_messages: &[ProgressMessage],
767        _options: ToolResultRenderOptions,
768    ) -> Option<String> {
769        None
770    }
771
772    /// Flattened text of what render_tool_result_message shows in transcript mode.
773    fn extract_search_text(&self, _out: serde_json::Value) -> String {
774        String::new()
775    }
776
777    /// Render the tool use message.
778    fn render_tool_use_message(
779        &self,
780        input: serde_json::Value,
781        options: ToolUseRenderOptions,
782    ) -> String;
783
784    /// Returns true when the non-verbose rendering is truncated.
785    fn is_result_truncated(&self, _output: serde_json::Value) -> bool {
786        false
787    }
788
789    /// Render an optional tag after the tool use message.
790    fn render_tool_use_tag(&self, _input: serde_json::Value) -> Option<String> {
791        None
792    }
793
794    /// Render progress message while the tool runs.
795    fn render_tool_use_progress_message(
796        &self,
797        _progress_messages: &[ProgressMessage],
798        _options: ToolProgressRenderOptions,
799    ) -> Option<String> {
800        None
801    }
802
803    /// Render queued message.
804    fn render_tool_use_queued_message(&self) -> Option<String> {
805        None
806    }
807
808    /// Render rejection message. Falls back to default if omitted.
809    fn render_tool_use_rejected_message(
810        &self,
811        _input: serde_json::Value,
812        _options: ToolRejectedRenderOptions,
813    ) -> Option<String> {
814        None
815    }
816
817    /// Render error message. Falls back to default if omitted.
818    fn render_tool_use_error_message(
819        &self,
820        _result: serde_json::Value,
821        _options: ToolErrorRenderOptions,
822    ) -> Option<String> {
823        None
824    }
825
826    /// Render multiple parallel instances as a group.
827    fn render_grouped_tool_uses(
828        &self,
829        _tool_uses: Vec<GroupedToolUse>,
830        _options: GroupedToolUseRenderOptions,
831    ) -> Option<String> {
832        None
833    }
834
835    /// Render a single tool use within a group.
836    fn render_grouped_tool_use(
837        &self,
838        _param: serde_json::Value,
839        _is_resolved: bool,
840        _is_error: bool,
841        _is_in_progress: bool,
842        _progress_messages: &[ProgressMessage],
843        _result: Option<serde_json::Value>,
844        _options: GroupedToolUseRenderOptions,
845    ) -> Option<String> {
846        None
847    }
848
849    /// Render multiple tool uses as a group (non-verbose mode only).
850    fn render_grouped_tool_use_fallback(
851        &self,
852        _tool_uses: Vec<GroupedToolUse>,
853        _options: GroupedToolUseRenderOptions,
854    ) -> Option<String> {
855        None
856    }
857
858    /// Generate the system prompt fragment for this tool.
859    fn prompt(
860        &self,
861        get_tool_permission_context: Arc<
862            dyn Fn() -> std::pin::Pin<Box<dyn Future<Output = ToolPermissionContext> + Send>>
863                + Send
864                + Sync,
865        >,
866        tools: &[ToolDefinition],
867        agents: &[serde_json::Value],
868        allowed_agent_types: Option<&[String]>,
869    ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send + '_>>;
870}
871
872// ---------------------------------------------------------------------------
873// Supporting types for UI rendering
874// ---------------------------------------------------------------------------
875
876#[derive(Debug, Clone)]
877pub struct SearchOrReadInfo {
878    pub is_search: bool,
879    pub is_read: bool,
880    pub is_list: bool,
881}
882
883#[derive(Debug, Clone)]
884pub struct McpToolInfo {
885    pub server_name: String,
886    pub tool_name: String,
887}
888
889#[derive(Debug, Clone)]
890pub struct ToolResultRenderOptions {
891    pub style: Option<String>,
892    pub theme: String,
893    pub tools: Vec<ToolDefinition>,
894    pub verbose: bool,
895    pub is_transcript_mode: bool,
896    pub is_brief_only: bool,
897    pub input: Option<serde_json::Value>,
898}
899
900#[derive(Debug, Clone)]
901pub struct ToolUseRenderOptions {
902    pub theme: String,
903    pub verbose: bool,
904    pub commands: Option<Vec<serde_json::Value>>,
905}
906
907#[derive(Debug, Clone)]
908pub struct ToolProgressRenderOptions {
909    pub tools: Vec<ToolDefinition>,
910    pub verbose: bool,
911    pub terminal_size: Option<TerminalSize>,
912    pub in_progress_tool_call_count: Option<usize>,
913    pub is_transcript_mode: bool,
914}
915
916#[derive(Debug, Clone)]
917pub struct TerminalSize {
918    pub columns: usize,
919    pub rows: usize,
920}
921
922#[derive(Debug, Clone)]
923pub struct ToolRejectedRenderOptions {
924    pub columns: usize,
925    pub messages: Vec<Message>,
926    pub style: Option<String>,
927    pub theme: String,
928    pub tools: Vec<ToolDefinition>,
929    pub verbose: bool,
930    pub progress_messages_for_message: Vec<ProgressMessage>,
931    pub is_transcript_mode: bool,
932}
933
934#[derive(Debug, Clone)]
935pub struct ToolErrorRenderOptions {
936    pub progress_messages_for_message: Vec<ProgressMessage>,
937    pub tools: Vec<ToolDefinition>,
938    pub verbose: bool,
939    pub is_transcript_mode: bool,
940}
941
942#[derive(Debug, Clone)]
943pub struct GroupedToolUse {
944    pub param: serde_json::Value,
945    pub is_resolved: bool,
946    pub is_error: bool,
947    pub is_in_progress: bool,
948    pub progress_messages: Vec<ProgressMessage>,
949    pub result: Option<GroupedToolUseResult>,
950}
951
952#[derive(Debug, Clone)]
953pub struct GroupedToolUseResult {
954    pub param: serde_json::Value,
955    pub output: serde_json::Value,
956}
957
958#[derive(Debug, Clone)]
959pub struct GroupedToolUseRenderOptions {
960    pub should_animate: bool,
961    pub tools: Vec<ToolDefinition>,
962}
963
964// ---------------------------------------------------------------------------
965// Permission result (mirrors TypeScript PermissionResult shape used by tools)
966// ---------------------------------------------------------------------------
967
968#[derive(Debug, Clone, Serialize, Deserialize)]
969#[serde(tag = "behavior", rename_all = "lowercase")]
970pub enum PermissionResult {
971    Allow {
972        #[serde(rename = "updatedInput", skip_serializing_if = "Option::is_none")]
973        updated_input: Option<HashMap<String, serde_json::Value>>,
974        #[serde(rename = "userModified", skip_serializing_if = "Option::is_none")]
975        user_modified: Option<bool>,
976    },
977    Deny {
978        message: String,
979        #[serde(rename = "toolUseID", skip_serializing_if = "Option::is_none")]
980        tool_use_id: Option<String>,
981    },
982    Ask {
983        message: String,
984        #[serde(rename = "updatedInput", skip_serializing_if = "Option::is_none")]
985        updated_input: Option<HashMap<String, serde_json::Value>>,
986    },
987    Passthrough {
988        message: String,
989    },
990}
991
992impl PermissionResult {
993    /// Helper to create an allow result with the given input.
994    pub fn allow(input: HashMap<String, serde_json::Value>) -> Self {
995        PermissionResult::Allow {
996            updated_input: Some(input),
997            user_modified: None,
998        }
999    }
1000}
1001
1002// ---------------------------------------------------------------------------
1003// Permission decision (used by can_use_tool callback)
1004// ---------------------------------------------------------------------------
1005
1006#[derive(Debug, Clone, Serialize, Deserialize)]
1007#[serde(tag = "behavior", rename_all = "lowercase")]
1008pub enum PermissionDecision {
1009    Allow {
1010        #[serde(rename = "updatedInput", skip_serializing_if = "Option::is_none")]
1011        updated_input: Option<HashMap<String, serde_json::Value>>,
1012    },
1013    Deny {
1014        message: String,
1015    },
1016    Ask {
1017        message: String,
1018    },
1019}
1020
1021// ---------------------------------------------------------------------------
1022// Tools type alias
1023// ---------------------------------------------------------------------------
1024
1025/// A collection of tools. Use this type instead of `Vec<ToolDefinition>` to
1026/// make it easier to track where tool sets are assembled, passed, and filtered
1027/// across the codebase.
1028pub type Tools = Vec<ToolDefinition>;
1029
1030// ---------------------------------------------------------------------------
1031// build_tool pattern (translated from TypeScript `buildTool`)
1032// ---------------------------------------------------------------------------
1033
1034/// Default values for tool methods, mirroring the TypeScript `TOOL_DEFAULTS`.
1035///
1036/// Defaults (fail-closed where it matters):
1037/// - `is_enabled` -> `true`
1038/// - `is_concurrency_safe` -> `false` (assume not safe)
1039/// - `is_read_only` -> `false` (assume writes)
1040/// - `is_destructive` -> `false`
1041/// - `check_permissions` -> `{ behavior: "allow", updatedInput }` (defer to general permission system)
1042/// - `to_auto_classifier_input` -> `""` (skip classifier - security-relevant tools must override)
1043/// - `user_facing_name` -> tool's name
1044pub struct ToolDefaults {
1045    pub is_enabled: bool,
1046    pub is_concurrency_safe: bool,
1047    pub is_read_only: bool,
1048    pub is_destructive: bool,
1049    pub check_permissions: Arc<
1050        dyn Fn(
1051                serde_json::Value,
1052            ) -> std::pin::Pin<Box<dyn Future<Output = PermissionResult> + Send>>
1053            + Send
1054            + Sync,
1055    >,
1056    pub to_auto_classifier_input: Arc<dyn Fn(serde_json::Value) -> serde_json::Value + Send + Sync>,
1057    pub user_facing_name: Arc<dyn Fn() -> String + Send + Sync>,
1058}
1059
1060impl Default for ToolDefaults {
1061    fn default() -> Self {
1062        ToolDefaults {
1063            is_enabled: true,
1064            is_concurrency_safe: false,
1065            is_read_only: false,
1066            is_destructive: false,
1067            check_permissions: Arc::new(|input: serde_json::Value| {
1068                Box::pin(async move {
1069                    PermissionResult::Allow {
1070                        updated_input: Some(
1071                            input
1072                                .as_object()
1073                                .cloned()
1074                                .unwrap_or_default()
1075                                .into_iter()
1076                                .collect(),
1077                        ),
1078                        user_modified: None,
1079                    }
1080                })
1081            }),
1082            to_auto_classifier_input: Arc::new(|_input: serde_json::Value| {
1083                serde_json::Value::String(String::new())
1084            }),
1085            user_facing_name: Arc::new(|| String::new()),
1086        }
1087    }
1088}
1089
1090/// Builder for constructing a `Tool` implementation with default values filled in.
1091///
1092/// This mirrors the TypeScript `buildTool` function. Tool implementations should
1093/// use this builder so that defaults live in one place and callers never need to
1094/// handle missing methods.
1095///
1096/// # Example
1097/// ```ignore
1098/// let tool = ToolBuilder::new("my_tool")
1099///     .input_schema(ToolInputSchema { ... })
1100///     .description_fn(|input, _, _, _| {
1101///         Box::pin(async move { format!("My tool with input: {:?}", input) })
1102///     })
1103///     .call_fn(|args, ctx, can_use, parent, on_progress| {
1104///         Box::pin(async move {
1105///             Ok(ToolResult {
1106///                 data: serde_json::json!({ "result": "ok" }),
1107///                 new_messages: None,
1108///                 context_modifier: None,
1109///                 mcp_meta: None,
1110///             })
1111///         })
1112///     })
1113///     .is_read_only(true)
1114///     .build();
1115/// ```
1116pub struct ToolBuilder {
1117    name: String,
1118    aliases: Option<Vec<String>>,
1119    search_hint: Option<String>,
1120    input_schema: Option<ToolInputSchema>,
1121    input_json_schema: Option<ToolInputJsonSchema>,
1122    output_schema: Option<serde_json::Value>,
1123    call_fn: Option<
1124        Arc<
1125            dyn Fn(
1126                    serde_json::Value,
1127                    Arc<ToolUseContext>,
1128                    Arc<
1129                        dyn Fn(
1130                                &ToolDefinition,
1131                                &serde_json::Value,
1132                                Arc<ToolUseContext>,
1133                                Arc<AssistantMessage>,
1134                                &str,
1135                                bool,
1136                            ) -> std::pin::Pin<
1137                                Box<
1138                                    dyn Future<
1139                                            Output = Result<
1140                                                PermissionDecision,
1141                                                crate::error::AgentError,
1142                                            >,
1143                                        > + Send,
1144                                >,
1145                            > + Send
1146                            + Sync,
1147                    >,
1148                    Arc<AssistantMessage>,
1149                    Option<Arc<dyn Fn(ToolProgress) + Send + Sync>>,
1150                ) -> std::pin::Pin<
1151                    Box<dyn Future<Output = Result<ToolResult, crate::error::AgentError>> + Send>,
1152                > + Send
1153                + Sync,
1154        >,
1155    >,
1156    description_fn: Option<
1157        Arc<
1158            dyn Fn(
1159                    serde_json::Value,
1160                    bool,
1161                    &ToolPermissionContext,
1162                    &[ToolDefinition],
1163                ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send>>
1164                + Send
1165                + Sync,
1166        >,
1167    >,
1168    prompt_fn: Option<
1169        Arc<
1170            dyn Fn(
1171                    Arc<
1172                        dyn Fn() -> std::pin::Pin<
1173                                Box<dyn Future<Output = ToolPermissionContext> + Send>,
1174                            > + Send
1175                            + Sync,
1176                    >,
1177                    &[ToolDefinition],
1178                    &[serde_json::Value],
1179                    Option<&[String]>,
1180                ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send>>
1181                + Send
1182                + Sync,
1183        >,
1184    >,
1185    validate_input_fn: Option<
1186        Arc<
1187            dyn Fn(
1188                    serde_json::Value,
1189                    Arc<ToolUseContext>,
1190                )
1191                    -> std::pin::Pin<Box<dyn Future<Output = ValidationResult> + Send>>
1192                + Send
1193                + Sync,
1194        >,
1195    >,
1196    check_permissions_fn: Option<
1197        Arc<
1198            dyn Fn(
1199                    serde_json::Value,
1200                    Arc<ToolUseContext>,
1201                )
1202                    -> std::pin::Pin<Box<dyn Future<Output = PermissionResult> + Send>>
1203                + Send
1204                + Sync,
1205        >,
1206    >,
1207    prepare_permission_matcher_fn: Option<
1208        Arc<dyn Fn(serde_json::Value) -> Arc<dyn Fn(&str) -> bool + Send + Sync> + Send + Sync>,
1209    >,
1210    is_enabled: bool,
1211    is_concurrency_safe_fn: Option<Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>>,
1212    is_read_only_fn: Option<Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>>,
1213    is_destructive_fn: Option<Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>>,
1214    inputs_equivalent_fn:
1215        Option<Arc<dyn Fn(serde_json::Value, serde_json::Value) -> bool + Send + Sync>>,
1216    max_result_size_chars: usize,
1217    strict: bool,
1218    should_defer: bool,
1219    always_load: bool,
1220    mcp_info: Option<McpToolInfo>,
1221    is_mcp: bool,
1222    is_lsp: bool,
1223    interrupt_behavior: String,
1224    is_search_or_read_fn: Option<Arc<dyn Fn(serde_json::Value) -> SearchOrReadInfo + Send + Sync>>,
1225    is_open_world_fn: Option<Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>>,
1226    requires_user_interaction: bool,
1227    backfill_observable_input_fn: Option<Arc<dyn Fn(&mut serde_json::Value) + Send + Sync>>,
1228    get_path_fn: Option<Arc<dyn Fn(serde_json::Value) -> Option<String> + Send + Sync>>,
1229    user_facing_name_fn: Option<Arc<dyn Fn(Option<&serde_json::Value>) -> String + Send + Sync>>,
1230    user_facing_name_background_color_fn:
1231        Option<Arc<dyn Fn(Option<&serde_json::Value>) -> Option<String> + Send + Sync>>,
1232    is_transparent_wrapper: bool,
1233    get_tool_use_summary_fn:
1234        Option<Arc<dyn Fn(Option<&serde_json::Value>) -> Option<String> + Send + Sync>>,
1235    get_activity_description_fn:
1236        Option<Arc<dyn Fn(Option<&serde_json::Value>) -> Option<String> + Send + Sync>>,
1237    to_auto_classifier_input_fn:
1238        Option<Arc<dyn Fn(serde_json::Value) -> serde_json::Value + Send + Sync>>,
1239    map_tool_result_fn:
1240        Option<Arc<dyn Fn(serde_json::Value, &str) -> ToolResultBlockParam + Send + Sync>>,
1241    render_tool_result_message_fn: Option<
1242        Arc<
1243            dyn Fn(serde_json::Value, &[ProgressMessage], ToolResultRenderOptions) -> Option<String>
1244                + Send
1245                + Sync,
1246        >,
1247    >,
1248    extract_search_text_fn: Option<Arc<dyn Fn(serde_json::Value) -> String + Send + Sync>>,
1249    render_tool_use_message_fn:
1250        Option<Arc<dyn Fn(serde_json::Value, ToolUseRenderOptions) -> String + Send + Sync>>,
1251    is_result_truncated_fn: Option<Arc<dyn Fn(serde_json::Value) -> bool + Send + Sync>>,
1252    render_tool_use_tag_fn: Option<Arc<dyn Fn(serde_json::Value) -> Option<String> + Send + Sync>>,
1253    render_tool_use_progress_message_fn: Option<
1254        Arc<dyn Fn(&[ProgressMessage], ToolProgressRenderOptions) -> Option<String> + Send + Sync>,
1255    >,
1256    render_tool_use_queued_message_fn: Option<Arc<dyn Fn() -> Option<String> + Send + Sync>>,
1257    render_tool_use_rejected_message_fn: Option<
1258        Arc<dyn Fn(serde_json::Value, ToolRejectedRenderOptions) -> Option<String> + Send + Sync>,
1259    >,
1260    render_tool_use_error_message_fn: Option<
1261        Arc<dyn Fn(serde_json::Value, ToolErrorRenderOptions) -> Option<String> + Send + Sync>,
1262    >,
1263    render_grouped_tool_uses_fn: Option<
1264        Arc<
1265            dyn Fn(Vec<GroupedToolUse>, GroupedToolUseRenderOptions) -> Option<String>
1266                + Send
1267                + Sync,
1268        >,
1269    >,
1270    render_grouped_tool_use_fn: Option<
1271        Arc<
1272            dyn Fn(
1273                    serde_json::Value,
1274                    bool,
1275                    bool,
1276                    bool,
1277                    &[ProgressMessage],
1278                    Option<serde_json::Value>,
1279                    GroupedToolUseRenderOptions,
1280                ) -> Option<String>
1281                + Send
1282                + Sync,
1283        >,
1284    >,
1285    render_grouped_tool_use_fallback_fn: Option<
1286        Arc<
1287            dyn Fn(Vec<GroupedToolUse>, GroupedToolUseRenderOptions) -> Option<String>
1288                + Send
1289                + Sync,
1290        >,
1291    >,
1292}
1293
1294impl ToolBuilder {
1295    /// Create a new builder with the tool name (required).
1296    pub fn new(name: &str) -> Self {
1297        ToolBuilder {
1298            name: name.to_string(),
1299            aliases: None,
1300            search_hint: None,
1301            input_schema: None,
1302            input_json_schema: None,
1303            output_schema: None,
1304            call_fn: None,
1305            description_fn: None,
1306            prompt_fn: None,
1307            validate_input_fn: None,
1308            check_permissions_fn: None,
1309            prepare_permission_matcher_fn: None,
1310            is_enabled: true,
1311            is_concurrency_safe_fn: None,
1312            is_read_only_fn: None,
1313            is_destructive_fn: None,
1314            inputs_equivalent_fn: None,
1315            max_result_size_chars: usize::MAX,
1316            strict: false,
1317            should_defer: false,
1318            always_load: false,
1319            mcp_info: None,
1320            is_mcp: false,
1321            is_lsp: false,
1322            interrupt_behavior: "block".to_string(),
1323            is_search_or_read_fn: None,
1324            is_open_world_fn: None,
1325            requires_user_interaction: false,
1326            backfill_observable_input_fn: None,
1327            get_path_fn: None,
1328            user_facing_name_fn: None,
1329            user_facing_name_background_color_fn: None,
1330            is_transparent_wrapper: false,
1331            get_tool_use_summary_fn: None,
1332            get_activity_description_fn: None,
1333            to_auto_classifier_input_fn: None,
1334            map_tool_result_fn: None,
1335            render_tool_result_message_fn: None,
1336            extract_search_text_fn: None,
1337            render_tool_use_message_fn: None,
1338            is_result_truncated_fn: None,
1339            render_tool_use_tag_fn: None,
1340            render_tool_use_progress_message_fn: None,
1341            render_tool_use_queued_message_fn: None,
1342            render_tool_use_rejected_message_fn: None,
1343            render_tool_use_error_message_fn: None,
1344            render_grouped_tool_uses_fn: None,
1345            render_grouped_tool_use_fn: None,
1346            render_grouped_tool_use_fallback_fn: None,
1347        }
1348    }
1349
1350    pub fn aliases(mut self, aliases: Vec<String>) -> Self {
1351        self.aliases = Some(aliases);
1352        self
1353    }
1354
1355    pub fn search_hint(mut self, hint: &str) -> Self {
1356        self.search_hint = Some(hint.to_string());
1357        self
1358    }
1359
1360    pub fn input_schema(mut self, schema: ToolInputSchema) -> Self {
1361        self.input_schema = Some(schema);
1362        self
1363    }
1364
1365    pub fn input_json_schema(mut self, schema: ToolInputJsonSchema) -> Self {
1366        self.input_json_schema = Some(schema);
1367        self
1368    }
1369
1370    pub fn output_schema(mut self, schema: serde_json::Value) -> Self {
1371        self.output_schema = Some(schema);
1372        self
1373    }
1374
1375    pub fn call_fn<F>(mut self, f: F) -> Self
1376    where
1377        F: Fn(
1378                serde_json::Value,
1379                Arc<ToolUseContext>,
1380                Arc<
1381                    dyn Fn(
1382                            &ToolDefinition,
1383                            &serde_json::Value,
1384                            Arc<ToolUseContext>,
1385                            Arc<AssistantMessage>,
1386                            &str,
1387                            bool,
1388                        ) -> std::pin::Pin<
1389                            Box<
1390                                dyn Future<
1391                                        Output = Result<
1392                                            PermissionDecision,
1393                                            crate::error::AgentError,
1394                                        >,
1395                                    > + Send,
1396                            >,
1397                        > + Send
1398                        + Sync,
1399                >,
1400                Arc<AssistantMessage>,
1401                Option<Arc<dyn Fn(ToolProgress) + Send + Sync>>,
1402            ) -> std::pin::Pin<
1403                Box<dyn Future<Output = Result<ToolResult, crate::error::AgentError>> + Send>,
1404            > + Send
1405            + Sync
1406            + 'static,
1407    {
1408        self.call_fn = Some(Arc::new(f));
1409        self
1410    }
1411
1412    pub fn description_fn<F>(mut self, f: F) -> Self
1413    where
1414        F: Fn(
1415                serde_json::Value,
1416                bool,
1417                &ToolPermissionContext,
1418                &[ToolDefinition],
1419            ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send>>
1420            + Send
1421            + Sync
1422            + 'static,
1423    {
1424        self.description_fn = Some(Arc::new(f));
1425        self
1426    }
1427
1428    pub fn prompt_fn<F>(mut self, f: F) -> Self
1429    where
1430        F: Fn(
1431                Arc<
1432                    dyn Fn()
1433                            -> std::pin::Pin<Box<dyn Future<Output = ToolPermissionContext> + Send>>
1434                        + Send
1435                        + Sync,
1436                >,
1437                &[ToolDefinition],
1438                &[serde_json::Value],
1439                Option<&[String]>,
1440            ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send>>
1441            + Send
1442            + Sync
1443            + 'static,
1444    {
1445        self.prompt_fn = Some(Arc::new(f));
1446        self
1447    }
1448
1449    pub fn validate_input_fn<F>(mut self, f: F) -> Self
1450    where
1451        F: Fn(
1452                serde_json::Value,
1453                Arc<ToolUseContext>,
1454            ) -> std::pin::Pin<Box<dyn Future<Output = ValidationResult> + Send>>
1455            + Send
1456            + Sync
1457            + 'static,
1458    {
1459        self.validate_input_fn = Some(Arc::new(f));
1460        self
1461    }
1462
1463    pub fn check_permissions_fn<F>(mut self, f: F) -> Self
1464    where
1465        F: Fn(
1466                serde_json::Value,
1467                Arc<ToolUseContext>,
1468            ) -> std::pin::Pin<Box<dyn Future<Output = PermissionResult> + Send>>
1469            + Send
1470            + Sync
1471            + 'static,
1472    {
1473        self.check_permissions_fn = Some(Arc::new(f));
1474        self
1475    }
1476
1477    pub fn prepare_permission_matcher_fn<F>(mut self, f: F) -> Self
1478    where
1479        F: Fn(serde_json::Value) -> Arc<dyn Fn(&str) -> bool + Send + Sync> + Send + Sync + 'static,
1480    {
1481        self.prepare_permission_matcher_fn = Some(Arc::new(f));
1482        self
1483    }
1484
1485    pub fn is_enabled(mut self, enabled: bool) -> Self {
1486        self.is_enabled = enabled;
1487        self
1488    }
1489
1490    pub fn is_concurrency_safe_fn<F>(mut self, f: F) -> Self
1491    where
1492        F: Fn(serde_json::Value) -> bool + Send + Sync + 'static,
1493    {
1494        self.is_concurrency_safe_fn = Some(Arc::new(f));
1495        self
1496    }
1497
1498    pub fn is_read_only_fn<F>(mut self, f: F) -> Self
1499    where
1500        F: Fn(serde_json::Value) -> bool + Send + Sync + 'static,
1501    {
1502        self.is_read_only_fn = Some(Arc::new(f));
1503        self
1504    }
1505
1506    pub fn is_destructive_fn<F>(mut self, f: F) -> Self
1507    where
1508        F: Fn(serde_json::Value) -> bool + Send + Sync + 'static,
1509    {
1510        self.is_destructive_fn = Some(Arc::new(f));
1511        self
1512    }
1513
1514    pub fn inputs_equivalent_fn<F>(mut self, f: F) -> Self
1515    where
1516        F: Fn(serde_json::Value, serde_json::Value) -> bool + Send + Sync + 'static,
1517    {
1518        self.inputs_equivalent_fn = Some(Arc::new(f));
1519        self
1520    }
1521
1522    pub fn max_result_size_chars(mut self, size: usize) -> Self {
1523        self.max_result_size_chars = size;
1524        self
1525    }
1526
1527    pub fn strict(mut self, strict: bool) -> Self {
1528        self.strict = strict;
1529        self
1530    }
1531
1532    pub fn should_defer(mut self, defer: bool) -> Self {
1533        self.should_defer = defer;
1534        self
1535    }
1536
1537    pub fn always_load(mut self, always: bool) -> Self {
1538        self.always_load = always;
1539        self
1540    }
1541
1542    pub fn mcp_info(mut self, server_name: &str, tool_name: &str) -> Self {
1543        self.mcp_info = Some(McpToolInfo {
1544            server_name: server_name.to_string(),
1545            tool_name: tool_name.to_string(),
1546        });
1547        self
1548    }
1549
1550    pub fn is_mcp(mut self, is_mcp: bool) -> Self {
1551        self.is_mcp = is_mcp;
1552        self
1553    }
1554
1555    pub fn is_lsp(mut self, is_lsp: bool) -> Self {
1556        self.is_lsp = is_lsp;
1557        self
1558    }
1559
1560    pub fn interrupt_behavior(mut self, behavior: &str) -> Self {
1561        self.interrupt_behavior = behavior.to_string();
1562        self
1563    }
1564
1565    pub fn is_search_or_read_fn<F>(mut self, f: F) -> Self
1566    where
1567        F: Fn(serde_json::Value) -> SearchOrReadInfo + Send + Sync + 'static,
1568    {
1569        self.is_search_or_read_fn = Some(Arc::new(f));
1570        self
1571    }
1572
1573    pub fn is_open_world_fn<F>(mut self, f: F) -> Self
1574    where
1575        F: Fn(serde_json::Value) -> bool + Send + Sync + 'static,
1576    {
1577        self.is_open_world_fn = Some(Arc::new(f));
1578        self
1579    }
1580
1581    pub fn requires_user_interaction(mut self, requires: bool) -> Self {
1582        self.requires_user_interaction = requires;
1583        self
1584    }
1585
1586    pub fn backfill_observable_input_fn<F>(mut self, f: F) -> Self
1587    where
1588        F: Fn(&mut serde_json::Value) + Send + Sync + 'static,
1589    {
1590        self.backfill_observable_input_fn = Some(Arc::new(f));
1591        self
1592    }
1593
1594    pub fn get_path_fn<F>(mut self, f: F) -> Self
1595    where
1596        F: Fn(serde_json::Value) -> Option<String> + Send + Sync + 'static,
1597    {
1598        self.get_path_fn = Some(Arc::new(f));
1599        self
1600    }
1601
1602    pub fn user_facing_name_fn<F>(mut self, f: F) -> Self
1603    where
1604        F: Fn(Option<&serde_json::Value>) -> String + Send + Sync + 'static,
1605    {
1606        self.user_facing_name_fn = Some(Arc::new(f));
1607        self
1608    }
1609
1610    pub fn user_facing_name_background_color_fn<F>(mut self, f: F) -> Self
1611    where
1612        F: Fn(Option<&serde_json::Value>) -> Option<String> + Send + Sync + 'static,
1613    {
1614        self.user_facing_name_background_color_fn = Some(Arc::new(f));
1615        self
1616    }
1617
1618    pub fn is_transparent_wrapper(mut self, is_transparent: bool) -> Self {
1619        self.is_transparent_wrapper = is_transparent;
1620        self
1621    }
1622
1623    pub fn get_tool_use_summary_fn<F>(mut self, f: F) -> Self
1624    where
1625        F: Fn(Option<&serde_json::Value>) -> Option<String> + Send + Sync + 'static,
1626    {
1627        self.get_tool_use_summary_fn = Some(Arc::new(f));
1628        self
1629    }
1630
1631    pub fn get_activity_description_fn<F>(mut self, f: F) -> Self
1632    where
1633        F: Fn(Option<&serde_json::Value>) -> Option<String> + Send + Sync + 'static,
1634    {
1635        self.get_activity_description_fn = Some(Arc::new(f));
1636        self
1637    }
1638
1639    pub fn to_auto_classifier_input_fn<F>(mut self, f: F) -> Self
1640    where
1641        F: Fn(serde_json::Value) -> serde_json::Value + Send + Sync + 'static,
1642    {
1643        self.to_auto_classifier_input_fn = Some(Arc::new(f));
1644        self
1645    }
1646
1647    pub fn map_tool_result_fn<F>(mut self, f: F) -> Self
1648    where
1649        F: Fn(serde_json::Value, &str) -> ToolResultBlockParam + Send + Sync + 'static,
1650    {
1651        self.map_tool_result_fn = Some(Arc::new(f));
1652        self
1653    }
1654
1655    pub fn render_tool_result_message_fn<F>(mut self, f: F) -> Self
1656    where
1657        F: Fn(serde_json::Value, &[ProgressMessage], ToolResultRenderOptions) -> Option<String>
1658            + Send
1659            + Sync
1660            + 'static,
1661    {
1662        self.render_tool_result_message_fn = Some(Arc::new(f));
1663        self
1664    }
1665
1666    pub fn extract_search_text_fn<F>(mut self, f: F) -> Self
1667    where
1668        F: Fn(serde_json::Value) -> String + Send + Sync + 'static,
1669    {
1670        self.extract_search_text_fn = Some(Arc::new(f));
1671        self
1672    }
1673
1674    pub fn render_tool_use_message_fn<F>(mut self, f: F) -> Self
1675    where
1676        F: Fn(serde_json::Value, ToolUseRenderOptions) -> String + Send + Sync + 'static,
1677    {
1678        self.render_tool_use_message_fn = Some(Arc::new(f));
1679        self
1680    }
1681
1682    pub fn is_result_truncated_fn<F>(mut self, f: F) -> Self
1683    where
1684        F: Fn(serde_json::Value) -> bool + Send + Sync + 'static,
1685    {
1686        self.is_result_truncated_fn = Some(Arc::new(f));
1687        self
1688    }
1689
1690    pub fn render_tool_use_tag_fn<F>(mut self, f: F) -> Self
1691    where
1692        F: Fn(serde_json::Value) -> Option<String> + Send + Sync + 'static,
1693    {
1694        self.render_tool_use_tag_fn = Some(Arc::new(f));
1695        self
1696    }
1697
1698    pub fn render_tool_use_progress_message_fn<F>(mut self, f: F) -> Self
1699    where
1700        F: Fn(&[ProgressMessage], ToolProgressRenderOptions) -> Option<String>
1701            + Send
1702            + Sync
1703            + 'static,
1704    {
1705        self.render_tool_use_progress_message_fn = Some(Arc::new(f));
1706        self
1707    }
1708
1709    pub fn render_tool_use_queued_message_fn<F>(mut self, f: F) -> Self
1710    where
1711        F: Fn() -> Option<String> + Send + Sync + 'static,
1712    {
1713        self.render_tool_use_queued_message_fn = Some(Arc::new(f));
1714        self
1715    }
1716
1717    pub fn render_tool_use_rejected_message_fn<F>(mut self, f: F) -> Self
1718    where
1719        F: Fn(serde_json::Value, ToolRejectedRenderOptions) -> Option<String>
1720            + Send
1721            + Sync
1722            + 'static,
1723    {
1724        self.render_tool_use_rejected_message_fn = Some(Arc::new(f));
1725        self
1726    }
1727
1728    pub fn render_tool_use_error_message_fn<F>(mut self, f: F) -> Self
1729    where
1730        F: Fn(serde_json::Value, ToolErrorRenderOptions) -> Option<String> + Send + Sync + 'static,
1731    {
1732        self.render_tool_use_error_message_fn = Some(Arc::new(f));
1733        self
1734    }
1735
1736    pub fn render_grouped_tool_uses_fn<F>(mut self, f: F) -> Self
1737    where
1738        F: Fn(Vec<GroupedToolUse>, GroupedToolUseRenderOptions) -> Option<String>
1739            + Send
1740            + Sync
1741            + 'static,
1742    {
1743        self.render_grouped_tool_uses_fn = Some(Arc::new(f));
1744        self
1745    }
1746
1747    pub fn render_grouped_tool_use_fn<F>(mut self, f: F) -> Self
1748    where
1749        F: Fn(
1750                serde_json::Value,
1751                bool,
1752                bool,
1753                bool,
1754                &[ProgressMessage],
1755                Option<serde_json::Value>,
1756                GroupedToolUseRenderOptions,
1757            ) -> Option<String>
1758            + Send
1759            + Sync
1760            + 'static,
1761    {
1762        self.render_grouped_tool_use_fn = Some(Arc::new(f));
1763        self
1764    }
1765
1766    pub fn render_grouped_tool_use_fallback_fn<F>(mut self, f: F) -> Self
1767    where
1768        F: Fn(Vec<GroupedToolUse>, GroupedToolUseRenderOptions) -> Option<String>
1769            + Send
1770            + Sync
1771            + 'static,
1772    {
1773        self.render_grouped_tool_use_fallback_fn = Some(Arc::new(f));
1774        self
1775    }
1776
1777    /// Build the tool, applying defaults for any unset fields.
1778    ///
1779    /// This mirrors the TypeScript runtime behavior:
1780    /// `{ ...TOOL_DEFAULTS, userFacingName: () => def.name, ...def }`
1781    pub fn build(self) -> Box<dyn Tool> {
1782        let name = self.name.clone();
1783        Box::new(BuiltTool { inner: self })
1784    }
1785}
1786
1787/// A fully constructed tool with all defaults applied.
1788/// Returned by `ToolBuilder::build()`.
1789struct BuiltTool {
1790    inner: ToolBuilder,
1791}
1792
1793impl Tool for BuiltTool {
1794    fn name(&self) -> &str {
1795        &self.inner.name
1796    }
1797
1798    fn aliases(&self) -> Option<&[String]> {
1799        self.inner.aliases.as_deref()
1800    }
1801
1802    fn search_hint(&self) -> Option<&str> {
1803        self.inner.search_hint.as_deref()
1804    }
1805
1806    fn input_schema(&self) -> ToolInputSchema {
1807        self.inner
1808            .input_schema
1809            .clone()
1810            .unwrap_or_else(|| ToolInputSchema {
1811                schema_type: "object".to_string(),
1812                properties: serde_json::json!({}),
1813                required: None,
1814            })
1815    }
1816
1817    fn input_json_schema(&self) -> Option<ToolInputJsonSchema> {
1818        self.inner.input_json_schema.clone()
1819    }
1820
1821    fn output_schema(&self) -> Option<serde_json::Value> {
1822        self.inner.output_schema.clone()
1823    }
1824
1825    fn call(
1826        &self,
1827        args: serde_json::Value,
1828        context: Arc<ToolUseContext>,
1829        can_use_tool: Arc<
1830            dyn Fn(
1831                    &ToolDefinition,
1832                    &serde_json::Value,
1833                    Arc<ToolUseContext>,
1834                    Arc<AssistantMessage>,
1835                    &str,
1836                    bool,
1837                ) -> std::pin::Pin<
1838                    Box<
1839                        dyn Future<Output = Result<PermissionDecision, crate::error::AgentError>>
1840                            + Send,
1841                    >,
1842                > + Send
1843                + Sync,
1844        >,
1845        parent_message: Arc<AssistantMessage>,
1846        on_progress: Option<Arc<dyn Fn(ToolProgress) + Send + Sync>>,
1847    ) -> std::pin::Pin<
1848        Box<dyn Future<Output = Result<ToolResult, crate::error::AgentError>> + Send + '_>,
1849    > {
1850        if let Some(f) = &self.inner.call_fn {
1851            let f = Arc::clone(f);
1852            let can_use_tool = Arc::clone(&can_use_tool);
1853            let context = Arc::clone(&context);
1854            let parent_message = Arc::clone(&parent_message);
1855            let on_progress = on_progress.clone();
1856            Box::pin(
1857                async move { f(args, context, can_use_tool, parent_message, on_progress).await },
1858            )
1859        } else {
1860            Box::pin(async {
1861                Err(crate::error::AgentError::Tool(format!(
1862                    "Tool '{}' has no call implementation",
1863                    self.inner.name
1864                )))
1865            })
1866        }
1867    }
1868
1869    fn description(
1870        &self,
1871        input: serde_json::Value,
1872        is_non_interactive_session: bool,
1873        tool_permission_context: &ToolPermissionContext,
1874        tools: &[ToolDefinition],
1875    ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send + '_>> {
1876        if let Some(f) = &self.inner.description_fn {
1877            let f = Arc::clone(f);
1878            let tools = tools.to_vec();
1879            let tpc = tool_permission_context.clone();
1880            Box::pin(async move { f(input, is_non_interactive_session, &tpc, &tools).await })
1881        } else {
1882            Box::pin(async move { format!("Tool: {}", self.inner.name) })
1883        }
1884    }
1885
1886    fn validate_input(
1887        &self,
1888        input: serde_json::Value,
1889        context: Arc<ToolUseContext>,
1890    ) -> std::pin::Pin<Box<dyn Future<Output = ValidationResult> + Send + '_>> {
1891        if let Some(f) = &self.inner.validate_input_fn {
1892            let f = Arc::clone(f);
1893            let context = Arc::clone(&context);
1894            Box::pin(async move { f(input, context).await })
1895        } else {
1896            Box::pin(async { ValidationResult::Valid })
1897        }
1898    }
1899
1900    fn check_permissions(
1901        &self,
1902        input: serde_json::Value,
1903        context: Arc<ToolUseContext>,
1904    ) -> std::pin::Pin<Box<dyn Future<Output = PermissionResult> + Send + '_>> {
1905        if let Some(f) = &self.inner.check_permissions_fn {
1906            let f = Arc::clone(f);
1907            let context = Arc::clone(&context);
1908            Box::pin(async move { f(input, context).await })
1909        } else {
1910            Box::pin(async move {
1911                PermissionResult::Allow {
1912                    updated_input: input.as_object().map(|o| o.clone().into_iter().collect()),
1913                    user_modified: None,
1914                }
1915            })
1916        }
1917    }
1918
1919    fn prepare_permission_matcher(
1920        &self,
1921        input: serde_json::Value,
1922    ) -> Option<Arc<dyn Fn(&str) -> bool + Send + Sync>> {
1923        self.inner
1924            .prepare_permission_matcher_fn
1925            .as_ref()
1926            .map(|f| f(input))
1927    }
1928
1929    fn is_enabled(&self) -> bool {
1930        self.inner.is_enabled
1931    }
1932
1933    fn is_concurrency_safe(&self, input: serde_json::Value) -> bool {
1934        self.inner
1935            .is_concurrency_safe_fn
1936            .as_ref()
1937            .map_or(false, |f| f(input))
1938    }
1939
1940    fn is_read_only(&self, input: serde_json::Value) -> bool {
1941        self.inner
1942            .is_read_only_fn
1943            .as_ref()
1944            .map_or(false, |f| f(input))
1945    }
1946
1947    fn is_destructive(&self, input: serde_json::Value) -> bool {
1948        self.inner
1949            .is_destructive_fn
1950            .as_ref()
1951            .map_or(false, |f| f(input))
1952    }
1953
1954    fn inputs_equivalent(&self, a: serde_json::Value, b: serde_json::Value) -> bool {
1955        self.inner
1956            .inputs_equivalent_fn
1957            .as_ref()
1958            .map_or(false, |f| f(a, b))
1959    }
1960
1961    fn max_result_size_chars(&self) -> usize {
1962        self.inner.max_result_size_chars
1963    }
1964
1965    fn strict(&self) -> bool {
1966        self.inner.strict
1967    }
1968
1969    fn should_defer(&self) -> bool {
1970        self.inner.should_defer
1971    }
1972
1973    fn always_load(&self) -> bool {
1974        self.inner.always_load
1975    }
1976
1977    fn mcp_info(&self) -> Option<McpToolInfo> {
1978        self.inner.mcp_info.clone()
1979    }
1980
1981    fn is_mcp(&self) -> bool {
1982        self.inner.is_mcp
1983    }
1984
1985    fn is_lsp(&self) -> bool {
1986        self.inner.is_lsp
1987    }
1988
1989    fn interrupt_behavior(&self) -> &str {
1990        &self.inner.interrupt_behavior
1991    }
1992
1993    fn is_search_or_read_command(&self, input: serde_json::Value) -> SearchOrReadInfo {
1994        self.inner.is_search_or_read_fn.as_ref().map_or(
1995            SearchOrReadInfo {
1996                is_search: false,
1997                is_read: false,
1998                is_list: false,
1999            },
2000            |f| f(input),
2001        )
2002    }
2003
2004    fn is_open_world(&self, input: serde_json::Value) -> bool {
2005        self.inner
2006            .is_open_world_fn
2007            .as_ref()
2008            .map_or(false, |f| f(input))
2009    }
2010
2011    fn requires_user_interaction(&self) -> bool {
2012        self.inner.requires_user_interaction
2013    }
2014
2015    fn backfill_observable_input(&self, input: &mut serde_json::Value) {
2016        if let Some(f) = &self.inner.backfill_observable_input_fn {
2017            f(input);
2018        }
2019    }
2020
2021    fn get_path(&self, input: serde_json::Value) -> Option<String> {
2022        self.inner.get_path_fn.as_ref().and_then(|f| f(input))
2023    }
2024
2025    fn user_facing_name(&self, input: Option<&serde_json::Value>) -> String {
2026        self.inner
2027            .user_facing_name_fn
2028            .as_ref()
2029            .map(|f| f(input))
2030            .unwrap_or_else(|| self.inner.name.clone())
2031    }
2032
2033    fn user_facing_name_background_color(
2034        &self,
2035        input: Option<&serde_json::Value>,
2036    ) -> Option<String> {
2037        self.inner
2038            .user_facing_name_background_color_fn
2039            .as_ref()
2040            .and_then(|f| f(input))
2041    }
2042
2043    fn is_transparent_wrapper(&self) -> bool {
2044        self.inner.is_transparent_wrapper
2045    }
2046
2047    fn get_tool_use_summary(&self, input: Option<&serde_json::Value>) -> Option<String> {
2048        self.inner
2049            .get_tool_use_summary_fn
2050            .as_ref()
2051            .and_then(|f| f(input))
2052    }
2053
2054    fn get_activity_description(&self, input: Option<&serde_json::Value>) -> Option<String> {
2055        self.inner
2056            .get_activity_description_fn
2057            .as_ref()
2058            .and_then(|f| f(input))
2059    }
2060
2061    fn to_auto_classifier_input(&self, input: serde_json::Value) -> serde_json::Value {
2062        self.inner
2063            .to_auto_classifier_input_fn
2064            .as_ref()
2065            .map_or(serde_json::Value::String(String::new()), |f| f(input))
2066    }
2067
2068    fn map_tool_result_to_tool_result_block_param(
2069        &self,
2070        content: serde_json::Value,
2071        tool_use_id: &str,
2072    ) -> ToolResultBlockParam {
2073        if let Some(f) = &self.inner.map_tool_result_fn {
2074            f(content, tool_use_id)
2075        } else {
2076            ToolResultBlockParam {
2077                block_type: "tool_result".to_string(),
2078                tool_use_id: tool_use_id.to_string(),
2079                content: vec![ContentBlockParam::Text {
2080                    text: content.to_string(),
2081                }],
2082                is_error: None,
2083            }
2084        }
2085    }
2086
2087    fn render_tool_result_message(
2088        &self,
2089        content: serde_json::Value,
2090        progress_messages: &[ProgressMessage],
2091        options: ToolResultRenderOptions,
2092    ) -> Option<String> {
2093        self.inner
2094            .render_tool_result_message_fn
2095            .as_ref()
2096            .and_then(|f| f(content, progress_messages, options))
2097    }
2098
2099    fn extract_search_text(&self, out: serde_json::Value) -> String {
2100        self.inner
2101            .extract_search_text_fn
2102            .as_ref()
2103            .map_or(String::new(), |f| f(out))
2104    }
2105
2106    fn render_tool_use_message(
2107        &self,
2108        input: serde_json::Value,
2109        options: ToolUseRenderOptions,
2110    ) -> String {
2111        if let Some(f) = &self.inner.render_tool_use_message_fn {
2112            f(input, options)
2113        } else {
2114            format!("[Tool: {}]", self.inner.name)
2115        }
2116    }
2117
2118    fn is_result_truncated(&self, output: serde_json::Value) -> bool {
2119        self.inner
2120            .is_result_truncated_fn
2121            .as_ref()
2122            .map_or(false, |f| f(output))
2123    }
2124
2125    fn render_tool_use_tag(&self, input: serde_json::Value) -> Option<String> {
2126        self.inner
2127            .render_tool_use_tag_fn
2128            .as_ref()
2129            .and_then(|f| f(input))
2130    }
2131
2132    fn render_tool_use_progress_message(
2133        &self,
2134        progress_messages: &[ProgressMessage],
2135        options: ToolProgressRenderOptions,
2136    ) -> Option<String> {
2137        self.inner
2138            .render_tool_use_progress_message_fn
2139            .as_ref()
2140            .and_then(|f| f(progress_messages, options))
2141    }
2142
2143    fn render_tool_use_queued_message(&self) -> Option<String> {
2144        self.inner
2145            .render_tool_use_queued_message_fn
2146            .as_ref()
2147            .and_then(|f| f())
2148    }
2149
2150    fn render_tool_use_rejected_message(
2151        &self,
2152        input: serde_json::Value,
2153        options: ToolRejectedRenderOptions,
2154    ) -> Option<String> {
2155        self.inner
2156            .render_tool_use_rejected_message_fn
2157            .as_ref()
2158            .and_then(|f| f(input, options))
2159    }
2160
2161    fn render_tool_use_error_message(
2162        &self,
2163        result: serde_json::Value,
2164        options: ToolErrorRenderOptions,
2165    ) -> Option<String> {
2166        self.inner
2167            .render_tool_use_error_message_fn
2168            .as_ref()
2169            .and_then(|f| f(result, options))
2170    }
2171
2172    fn render_grouped_tool_uses(
2173        &self,
2174        tool_uses: Vec<GroupedToolUse>,
2175        options: GroupedToolUseRenderOptions,
2176    ) -> Option<String> {
2177        self.inner
2178            .render_grouped_tool_uses_fn
2179            .as_ref()
2180            .and_then(|f| f(tool_uses, options))
2181    }
2182
2183    fn render_grouped_tool_use(
2184        &self,
2185        param: serde_json::Value,
2186        is_resolved: bool,
2187        is_error: bool,
2188        is_in_progress: bool,
2189        progress_messages: &[ProgressMessage],
2190        result: Option<serde_json::Value>,
2191        options: GroupedToolUseRenderOptions,
2192    ) -> Option<String> {
2193        self.inner
2194            .render_grouped_tool_use_fn
2195            .as_ref()
2196            .and_then(|f| {
2197                f(
2198                    param,
2199                    is_resolved,
2200                    is_error,
2201                    is_in_progress,
2202                    progress_messages,
2203                    result,
2204                    options,
2205                )
2206            })
2207    }
2208
2209    fn render_grouped_tool_use_fallback(
2210        &self,
2211        tool_uses: Vec<GroupedToolUse>,
2212        options: GroupedToolUseRenderOptions,
2213    ) -> Option<String> {
2214        self.inner
2215            .render_grouped_tool_use_fallback_fn
2216            .as_ref()
2217            .and_then(|f| f(tool_uses, options))
2218    }
2219
2220    fn prompt(
2221        &self,
2222        get_tool_permission_context: Arc<
2223            dyn Fn() -> std::pin::Pin<Box<dyn Future<Output = ToolPermissionContext> + Send>>
2224                + Send
2225                + Sync,
2226        >,
2227        tools: &[ToolDefinition],
2228        agents: &[serde_json::Value],
2229        allowed_agent_types: Option<&[String]>,
2230    ) -> std::pin::Pin<Box<dyn Future<Output = String> + Send + '_>> {
2231        if let Some(f) = &self.inner.prompt_fn {
2232            let f = Arc::clone(f);
2233            let tools = tools.to_vec();
2234            let agents = agents.to_vec();
2235            let allowed_agent_types = allowed_agent_types.map(|s| s.to_vec());
2236            Box::pin(async move {
2237                f(
2238                    get_tool_permission_context,
2239                    &tools,
2240                    &agents,
2241                    allowed_agent_types.as_deref(),
2242                )
2243                .await
2244            })
2245        } else {
2246            Box::pin(async move { format!("Use the {} tool.", self.inner.name) })
2247        }
2248    }
2249}
2250
2251// ---------------------------------------------------------------------------
2252// Tests
2253// ---------------------------------------------------------------------------