use super::*; use agent_settings::AgentSettings; use gpui::{App, SharedString, Task}; use std::future; use std::sync::atomic::{AtomicBool, Ordering}; /// A tool that echoes its input #[derive(JsonSchema, Serialize, Deserialize)] pub struct EchoToolInput { /// The text to echo. pub text: String, } pub struct EchoTool; impl AgentTool for EchoTool { type Input = EchoToolInput; type Output = String; const NAME: &'static str = "echo"; fn kind() -> acp::ToolKind { acp::ToolKind::Other } fn initial_title( &self, _input: Result, _cx: &mut App, ) -> SharedString { "Echo".into() } fn run( self: Arc, input: Self::Input, _event_stream: ToolCallEventStream, _cx: &mut App, ) -> Task> { Task::ready(Ok(input.text)) } } /// A tool that waits for a specified delay #[derive(JsonSchema, Serialize, Deserialize)] pub struct DelayToolInput { /// The delay in milliseconds. ms: u64, } pub struct DelayTool; impl AgentTool for DelayTool { type Input = DelayToolInput; type Output = String; const NAME: &'static str = "delay"; fn initial_title( &self, input: Result, _cx: &mut App, ) -> SharedString { if let Ok(input) = input { format!("Delay {}ms", input.ms).into() } else { "Delay".into() } } fn kind() -> acp::ToolKind { acp::ToolKind::Other } fn run( self: Arc, input: Self::Input, _event_stream: ToolCallEventStream, cx: &mut App, ) -> Task> where Self: Sized, { let executor = cx.background_executor().clone(); cx.foreground_executor().spawn(async move { executor.timer(Duration::from_millis(input.ms)).await; Ok("Ding".to_string()) }) } } #[derive(JsonSchema, Serialize, Deserialize)] pub struct ToolRequiringPermissionInput {} pub struct ToolRequiringPermission; impl AgentTool for ToolRequiringPermission { type Input = ToolRequiringPermissionInput; type Output = String; const NAME: &'static str = "tool_requiring_permission"; fn kind() -> acp::ToolKind { acp::ToolKind::Other } fn initial_title( &self, _input: Result, _cx: &mut App, ) -> SharedString { "This tool requires permission".into() } fn run( self: Arc, _input: Self::Input, event_stream: ToolCallEventStream, cx: &mut App, ) -> Task> { let settings = AgentSettings::get_global(cx); let decision = decide_permission_from_settings(Self::NAME, &[String::new()], settings); let authorize = match decision { ToolPermissionDecision::Allow => None, ToolPermissionDecision::Deny(reason) => { return Task::ready(Err(reason)); } ToolPermissionDecision::Confirm => { let context = crate::ToolPermissionContext::new( "tool_requiring_permission", vec![String::new()], ); Some(event_stream.authorize("Authorize?", context, cx)) } }; cx.foreground_executor().spawn(async move { if let Some(authorize) = authorize { authorize.await.map_err(|e| e.to_string())?; } Ok("Allowed".to_string()) }) } } #[derive(JsonSchema, Serialize, Deserialize)] pub struct InfiniteToolInput {} pub struct InfiniteTool; impl AgentTool for InfiniteTool { type Input = InfiniteToolInput; type Output = String; const NAME: &'static str = "infinite"; fn kind() -> acp::ToolKind { acp::ToolKind::Other } fn initial_title( &self, _input: Result, _cx: &mut App, ) -> SharedString { "Infinite Tool".into() } fn run( self: Arc, _input: Self::Input, _event_stream: ToolCallEventStream, cx: &mut App, ) -> Task> { cx.foreground_executor().spawn(async move { future::pending::<()>().await; unreachable!() }) } } /// A tool that loops forever but properly handles cancellation via `select!`, /// similar to how edit_file_tool handles cancellation. #[derive(JsonSchema, Serialize, Deserialize)] pub struct CancellationAwareToolInput {} pub struct CancellationAwareTool { pub was_cancelled: Arc, } impl CancellationAwareTool { pub fn new() -> (Self, Arc) { let was_cancelled = Arc::new(AtomicBool::new(false)); ( Self { was_cancelled: was_cancelled.clone(), }, was_cancelled, ) } } impl AgentTool for CancellationAwareTool { type Input = CancellationAwareToolInput; type Output = String; const NAME: &'static str = "cancellation_aware"; fn kind() -> acp::ToolKind { acp::ToolKind::Other } fn initial_title( &self, _input: Result, _cx: &mut App, ) -> SharedString { "Cancellation Aware Tool".into() } fn run( self: Arc, _input: Self::Input, event_stream: ToolCallEventStream, cx: &mut App, ) -> Task> { cx.foreground_executor().spawn(async move { // Wait for cancellation - this tool does nothing but wait to be cancelled event_stream.cancelled_by_user().await; self.was_cancelled.store(true, Ordering::SeqCst); Err("Tool cancelled by user".to_string()) }) } } /// A tool that takes an object with map from letters to random words starting with that letter. /// All fiealds are required! Pass a word for every letter! #[derive(JsonSchema, Serialize, Deserialize)] pub struct WordListInput { /// Provide a random word that starts with A. a: Option, /// Provide a random word that starts with B. b: Option, /// Provide a random word that starts with C. c: Option, /// Provide a random word that starts with D. d: Option, /// Provide a random word that starts with E. e: Option, /// Provide a random word that starts with F. f: Option, /// Provide a random word that starts with G. g: Option, } pub struct WordListTool; impl AgentTool for WordListTool { type Input = WordListInput; type Output = String; const NAME: &'static str = "word_list"; fn kind() -> acp::ToolKind { acp::ToolKind::Other } fn initial_title( &self, _input: Result, _cx: &mut App, ) -> SharedString { "List of random words".into() } fn run( self: Arc, _input: Self::Input, _event_stream: ToolCallEventStream, _cx: &mut App, ) -> Task> { Task::ready(Ok("ok".to_string())) } }