thread.rs

  1use crate::templates::{SystemPromptTemplate, Template, Templates};
  2use agent_client_protocol as acp;
  3use anyhow::{anyhow, Context as _, Result};
  4use assistant_tool::ActionLog;
  5use cloud_llm_client::{CompletionIntent, CompletionMode};
  6use collections::HashMap;
  7use futures::{
  8    channel::{mpsc, oneshot},
  9    stream::FuturesUnordered,
 10};
 11use gpui::{App, Context, Entity, ImageFormat, SharedString, Task};
 12use language_model::{
 13    LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelImage,
 14    LanguageModelRequest, LanguageModelRequestMessage, LanguageModelRequestTool,
 15    LanguageModelToolResult, LanguageModelToolResultContent, LanguageModelToolSchemaFormat,
 16    LanguageModelToolUse, LanguageModelToolUseId, MessageContent, Role, StopReason,
 17};
 18use log;
 19use project::Project;
 20use prompt_store::ProjectContext;
 21use schemars::{JsonSchema, Schema};
 22use serde::Deserialize;
 23use smol::stream::StreamExt;
 24use std::{cell::RefCell, collections::BTreeMap, fmt::Write, future::Future, rc::Rc, sync::Arc};
 25use util::{markdown::MarkdownCodeBlock, ResultExt};
 26
 27#[derive(Debug, Clone)]
 28pub struct AgentMessage {
 29    pub role: Role,
 30    pub content: Vec<MessageContent>,
 31}
 32
 33impl AgentMessage {
 34    pub fn to_markdown(&self) -> String {
 35        let mut markdown = format!("## {}\n", self.role);
 36
 37        for content in &self.content {
 38            match content {
 39                MessageContent::Text(text) => {
 40                    markdown.push_str(text);
 41                    markdown.push('\n');
 42                }
 43                MessageContent::Thinking { text, .. } => {
 44                    markdown.push_str("<think>");
 45                    markdown.push_str(text);
 46                    markdown.push_str("</think>\n");
 47                }
 48                MessageContent::RedactedThinking(_) => markdown.push_str("<redacted_thinking />\n"),
 49                MessageContent::Image(_) => {
 50                    markdown.push_str("<image />\n");
 51                }
 52                MessageContent::ToolUse(tool_use) => {
 53                    markdown.push_str(&format!(
 54                        "**Tool Use**: {} (ID: {})\n",
 55                        tool_use.name, tool_use.id
 56                    ));
 57                    markdown.push_str(&format!(
 58                        "{}\n",
 59                        MarkdownCodeBlock {
 60                            tag: "json",
 61                            text: &format!("{:#}", tool_use.input)
 62                        }
 63                    ));
 64                }
 65                MessageContent::ToolResult(tool_result) => {
 66                    markdown.push_str(&format!(
 67                        "**Tool Result**: {} (ID: {})\n\n",
 68                        tool_result.tool_name, tool_result.tool_use_id
 69                    ));
 70                    if tool_result.is_error {
 71                        markdown.push_str("**ERROR:**\n");
 72                    }
 73
 74                    match &tool_result.content {
 75                        LanguageModelToolResultContent::Text(text) => {
 76                            writeln!(markdown, "{text}\n").ok();
 77                        }
 78                        LanguageModelToolResultContent::Image(_) => {
 79                            writeln!(markdown, "<image />\n").ok();
 80                        }
 81                    }
 82
 83                    if let Some(output) = tool_result.output.as_ref() {
 84                        writeln!(
 85                            markdown,
 86                            "**Debug Output**:\n\n```json\n{}\n```\n",
 87                            serde_json::to_string_pretty(output).unwrap()
 88                        )
 89                        .unwrap();
 90                    }
 91                }
 92            }
 93        }
 94
 95        markdown
 96    }
 97}
 98
 99#[derive(Debug)]
100pub enum AgentResponseEvent {
101    Text(String),
102    Thinking(String),
103    ToolCall(acp::ToolCall),
104    ToolCallUpdate(acp::ToolCallUpdate),
105    ToolCallAuthorization(ToolCallAuthorization),
106    Stop(acp::StopReason),
107}
108
109#[derive(Debug)]
110pub struct ToolCallAuthorization {
111    pub tool_call: acp::ToolCall,
112    pub options: Vec<acp::PermissionOption>,
113    pub response: oneshot::Sender<acp::PermissionOptionId>,
114}
115
116pub struct Thread {
117    messages: Vec<AgentMessage>,
118    completion_mode: CompletionMode,
119    /// Holds the task that handles agent interaction until the end of the turn.
120    /// Survives across multiple requests as the model performs tool calls and
121    /// we run tools, report their results.
122    running_turn: Option<Task<()>>,
123    pending_tool_uses: HashMap<LanguageModelToolUseId, LanguageModelToolUse>,
124    tools: BTreeMap<SharedString, Arc<dyn AnyAgentTool>>,
125    project_context: Rc<RefCell<ProjectContext>>,
126    templates: Arc<Templates>,
127    pub selected_model: Arc<dyn LanguageModel>,
128    _action_log: Entity<ActionLog>,
129}
130
131impl Thread {
132    pub fn new(
133        _project: Entity<Project>,
134        project_context: Rc<RefCell<ProjectContext>>,
135        action_log: Entity<ActionLog>,
136        templates: Arc<Templates>,
137        default_model: Arc<dyn LanguageModel>,
138    ) -> Self {
139        Self {
140            messages: Vec::new(),
141            completion_mode: CompletionMode::Normal,
142            running_turn: None,
143            pending_tool_uses: HashMap::default(),
144            tools: BTreeMap::default(),
145            project_context,
146            templates,
147            selected_model: default_model,
148            _action_log: action_log,
149        }
150    }
151
152    pub fn set_mode(&mut self, mode: CompletionMode) {
153        self.completion_mode = mode;
154    }
155
156    pub fn messages(&self) -> &[AgentMessage] {
157        &self.messages
158    }
159
160    pub fn add_tool(&mut self, tool: impl AgentTool) {
161        self.tools.insert(tool.name(), tool.erase());
162    }
163
164    pub fn remove_tool(&mut self, name: &str) -> bool {
165        self.tools.remove(name).is_some()
166    }
167
168    pub fn cancel(&mut self) {
169        self.running_turn.take();
170
171        let tool_results = self
172            .pending_tool_uses
173            .drain()
174            .map(|(tool_use_id, tool_use)| {
175                MessageContent::ToolResult(LanguageModelToolResult {
176                    tool_use_id,
177                    tool_name: tool_use.name.clone(),
178                    is_error: true,
179                    content: LanguageModelToolResultContent::Text("Tool canceled by user".into()),
180                    output: None,
181                })
182            })
183            .collect::<Vec<_>>();
184        self.last_user_message().content.extend(tool_results);
185    }
186
187    /// Sending a message results in the model streaming a response, which could include tool calls.
188    /// After calling tools, the model will stops and waits for any outstanding tool calls to be completed and their results sent.
189    /// The returned channel will report all the occurrences in which the model stops before erroring or ending its turn.
190    pub fn send(
191        &mut self,
192        model: Arc<dyn LanguageModel>,
193        content: impl Into<MessageContent>,
194        cx: &mut Context<Self>,
195    ) -> mpsc::UnboundedReceiver<Result<AgentResponseEvent, LanguageModelCompletionError>> {
196        let content = content.into();
197        log::info!("Thread::send called with model: {:?}", model.name());
198        log::debug!("Thread::send content: {:?}", content);
199
200        cx.notify();
201        let (events_tx, events_rx) =
202            mpsc::unbounded::<Result<AgentResponseEvent, LanguageModelCompletionError>>();
203        let event_stream = AgentResponseEventStream(events_tx);
204
205        let user_message_ix = self.messages.len();
206        self.messages.push(AgentMessage {
207            role: Role::User,
208            content: vec![content],
209        });
210        log::info!("Total messages in thread: {}", self.messages.len());
211        self.running_turn = Some(cx.spawn(async move |thread, cx| {
212            log::info!("Starting agent turn execution");
213            let turn_result = async {
214                // Perform one request, then keep looping if the model makes tool calls.
215                let mut completion_intent = CompletionIntent::UserPrompt;
216                'outer: loop {
217                    log::debug!(
218                        "Building completion request with intent: {:?}",
219                        completion_intent
220                    );
221                    let request = thread.update(cx, |thread, cx| {
222                        thread.build_completion_request(completion_intent, cx)
223                    })?;
224
225                    // println!(
226                    //     "request: {}",
227                    //     serde_json::to_string_pretty(&request).unwrap()
228                    // );
229
230                    // Stream events, appending to messages and collecting up tool uses.
231                    log::info!("Calling model.stream_completion");
232                    let mut events = model.stream_completion(request, cx).await?;
233                    log::debug!("Stream completion started successfully");
234                    let mut tool_uses = FuturesUnordered::new();
235                    while let Some(event) = events.next().await {
236                        match event {
237                            Ok(LanguageModelCompletionEvent::Stop(reason)) => {
238                                event_stream.send_stop(reason);
239                                if reason == StopReason::Refusal {
240                                    thread.update(cx, |thread, _cx| {
241                                        thread.messages.truncate(user_message_ix);
242                                    })?;
243                                    break 'outer;
244                                }
245                            }
246                            Ok(event) => {
247                                log::trace!("Received completion event: {:?}", event);
248                                thread
249                                    .update(cx, |thread, cx| {
250                                        tool_uses.extend(thread.handle_streamed_completion_event(
251                                            event,
252                                            &event_stream,
253                                            cx,
254                                        ));
255                                    })
256                                    .ok();
257                            }
258                            Err(error) => {
259                                log::error!("Error in completion stream: {:?}", error);
260                                event_stream.send_error(error);
261                                break;
262                            }
263                        }
264                    }
265
266                    // If there are no tool uses, the turn is done.
267                    if tool_uses.is_empty() {
268                        log::info!("No tool uses found, completing turn");
269                        break;
270                    }
271                    log::info!("Found {} tool uses to execute", tool_uses.len());
272
273                    // As tool results trickle in, insert them in the last user
274                    // message so that they can be sent on the next tick of the
275                    // agentic loop.
276                    while let Some(tool_result) = tool_uses.next().await {
277                        log::info!("Tool finished {:?}", tool_result);
278
279                        event_stream.send_tool_call_result(&tool_result);
280                        thread
281                            .update(cx, |thread, _cx| {
282                                thread.pending_tool_uses.remove(&tool_result.tool_use_id);
283                                thread
284                                    .last_user_message()
285                                    .content
286                                    .push(MessageContent::ToolResult(tool_result));
287                            })
288                            .ok();
289                    }
290
291                    completion_intent = CompletionIntent::ToolResults;
292                }
293
294                Ok(())
295            }
296            .await;
297
298            if let Err(error) = turn_result {
299                log::error!("Turn execution failed: {:?}", error);
300                event_stream.send_error(error);
301            } else {
302                log::info!("Turn execution completed successfully");
303            }
304        }));
305        events_rx
306    }
307
308    pub fn build_system_message(&self) -> AgentMessage {
309        log::debug!("Building system message");
310        let prompt = SystemPromptTemplate {
311            project: &self.project_context.borrow(),
312            available_tools: self.tools.keys().cloned().collect(),
313        }
314        .render(&self.templates)
315        .context("failed to build system prompt")
316        .expect("Invalid template");
317        log::debug!("System message built");
318        AgentMessage {
319            role: Role::System,
320            content: vec![prompt.into()],
321        }
322    }
323
324    /// A helper method that's called on every streamed completion event.
325    /// Returns an optional tool result task, which the main agentic loop in
326    /// send will send back to the model when it resolves.
327    fn handle_streamed_completion_event(
328        &mut self,
329        event: LanguageModelCompletionEvent,
330        event_stream: &AgentResponseEventStream,
331        cx: &mut Context<Self>,
332    ) -> Option<Task<LanguageModelToolResult>> {
333        log::trace!("Handling streamed completion event: {:?}", event);
334        use LanguageModelCompletionEvent::*;
335
336        match event {
337            StartMessage { .. } => {
338                self.messages.push(AgentMessage {
339                    role: Role::Assistant,
340                    content: Vec::new(),
341                });
342            }
343            Text(new_text) => self.handle_text_event(new_text, event_stream, cx),
344            Thinking { text, signature } => {
345                self.handle_thinking_event(text, signature, event_stream, cx)
346            }
347            RedactedThinking { data } => self.handle_redacted_thinking_event(data, cx),
348            ToolUse(tool_use) => {
349                return self.handle_tool_use_event(tool_use, event_stream, cx);
350            }
351            ToolUseJsonParseError {
352                id,
353                tool_name,
354                raw_input,
355                json_parse_error,
356            } => {
357                return Some(Task::ready(self.handle_tool_use_json_parse_error_event(
358                    id,
359                    tool_name,
360                    raw_input,
361                    json_parse_error,
362                )));
363            }
364            UsageUpdate(_) | StatusUpdate(_) => {}
365            Stop(_) => unreachable!(),
366        }
367
368        None
369    }
370
371    fn handle_text_event(
372        &mut self,
373        new_text: String,
374        events_stream: &AgentResponseEventStream,
375        cx: &mut Context<Self>,
376    ) {
377        events_stream.send_text(&new_text);
378
379        let last_message = self.last_assistant_message();
380        if let Some(MessageContent::Text(text)) = last_message.content.last_mut() {
381            text.push_str(&new_text);
382        } else {
383            last_message.content.push(MessageContent::Text(new_text));
384        }
385
386        cx.notify();
387    }
388
389    fn handle_thinking_event(
390        &mut self,
391        new_text: String,
392        new_signature: Option<String>,
393        event_stream: &AgentResponseEventStream,
394        cx: &mut Context<Self>,
395    ) {
396        event_stream.send_thinking(&new_text);
397
398        let last_message = self.last_assistant_message();
399        if let Some(MessageContent::Thinking { text, signature }) = last_message.content.last_mut()
400        {
401            text.push_str(&new_text);
402            *signature = new_signature.or(signature.take());
403        } else {
404            last_message.content.push(MessageContent::Thinking {
405                text: new_text,
406                signature: new_signature,
407            });
408        }
409
410        cx.notify();
411    }
412
413    fn handle_redacted_thinking_event(&mut self, data: String, cx: &mut Context<Self>) {
414        let last_message = self.last_assistant_message();
415        last_message
416            .content
417            .push(MessageContent::RedactedThinking(data));
418        cx.notify();
419    }
420
421    fn handle_tool_use_event(
422        &mut self,
423        tool_use: LanguageModelToolUse,
424        event_stream: &AgentResponseEventStream,
425        cx: &mut Context<Self>,
426    ) -> Option<Task<LanguageModelToolResult>> {
427        cx.notify();
428
429        self.pending_tool_uses
430            .insert(tool_use.id.clone(), tool_use.clone());
431        let last_message = self.last_assistant_message();
432
433        // Ensure the last message ends in the current tool use
434        let push_new_tool_use = last_message.content.last_mut().map_or(true, |content| {
435            if let MessageContent::ToolUse(last_tool_use) = content {
436                if last_tool_use.id == tool_use.id {
437                    *last_tool_use = tool_use.clone();
438                    false
439                } else {
440                    true
441                }
442            } else {
443                true
444            }
445        });
446        if push_new_tool_use {
447            event_stream.send_tool_call(&tool_use);
448            last_message
449                .content
450                .push(MessageContent::ToolUse(tool_use.clone()));
451        } else {
452            event_stream.send_tool_call_update(
453                &tool_use.id,
454                acp::ToolCallUpdateFields {
455                    raw_input: Some(tool_use.input.clone()),
456                    ..Default::default()
457                },
458            );
459        }
460
461        if !tool_use.is_input_complete {
462            return None;
463        }
464
465        if let Some(tool) = self.tools.get(tool_use.name.as_ref()) {
466            let tool_result =
467                self.run_tool(tool.clone(), tool_use.clone(), event_stream.clone(), cx);
468            Some(cx.foreground_executor().spawn(async move {
469                match tool_result.await {
470                    Ok(tool_output) => LanguageModelToolResult {
471                        tool_use_id: tool_use.id,
472                        tool_name: tool_use.name,
473                        is_error: false,
474                        content: LanguageModelToolResultContent::Text(Arc::from(tool_output)),
475                        output: None,
476                    },
477                    Err(error) => LanguageModelToolResult {
478                        tool_use_id: tool_use.id,
479                        tool_name: tool_use.name,
480                        is_error: true,
481                        content: LanguageModelToolResultContent::Text(Arc::from(error.to_string())),
482                        output: None,
483                    },
484                }
485            }))
486        } else {
487            let content = format!("No tool named {} exists", tool_use.name);
488            Some(Task::ready(LanguageModelToolResult {
489                content: LanguageModelToolResultContent::Text(Arc::from(content)),
490                tool_use_id: tool_use.id,
491                tool_name: tool_use.name,
492                is_error: true,
493                output: None,
494            }))
495        }
496    }
497
498    fn run_tool(
499        &self,
500        tool: Arc<dyn AnyAgentTool>,
501        tool_use: LanguageModelToolUse,
502        event_stream: AgentResponseEventStream,
503        cx: &mut Context<Self>,
504    ) -> Task<Result<String>> {
505        let needs_authorization = tool.needs_authorization(tool_use.input.clone(), cx);
506        cx.spawn(async move |_this, cx| {
507            if needs_authorization? {
508                event_stream.authorize_tool_call(&tool_use).await?;
509            }
510
511            event_stream.send_tool_call_update(
512                &tool_use.id,
513                acp::ToolCallUpdateFields {
514                    status: Some(acp::ToolCallStatus::InProgress),
515                    ..Default::default()
516                },
517            );
518            cx.update(|cx| tool.run(tool_use.input, cx))?.await
519        })
520    }
521
522    fn handle_tool_use_json_parse_error_event(
523        &mut self,
524        tool_use_id: LanguageModelToolUseId,
525        tool_name: Arc<str>,
526        raw_input: Arc<str>,
527        json_parse_error: String,
528    ) -> LanguageModelToolResult {
529        let tool_output = format!("Error parsing input JSON: {json_parse_error}");
530        LanguageModelToolResult {
531            tool_use_id,
532            tool_name,
533            is_error: true,
534            content: LanguageModelToolResultContent::Text(tool_output.into()),
535            output: Some(serde_json::Value::String(raw_input.to_string())),
536        }
537    }
538
539    /// Guarantees the last message is from the assistant and returns a mutable reference.
540    fn last_assistant_message(&mut self) -> &mut AgentMessage {
541        if self
542            .messages
543            .last()
544            .map_or(true, |m| m.role != Role::Assistant)
545        {
546            self.messages.push(AgentMessage {
547                role: Role::Assistant,
548                content: Vec::new(),
549            });
550        }
551        self.messages.last_mut().unwrap()
552    }
553
554    /// Guarantees the last message is from the user and returns a mutable reference.
555    fn last_user_message(&mut self) -> &mut AgentMessage {
556        if self.messages.last().map_or(true, |m| m.role != Role::User) {
557            self.messages.push(AgentMessage {
558                role: Role::User,
559                content: Vec::new(),
560            });
561        }
562        self.messages.last_mut().unwrap()
563    }
564
565    fn build_completion_request(
566        &self,
567        completion_intent: CompletionIntent,
568        cx: &mut App,
569    ) -> LanguageModelRequest {
570        log::debug!("Building completion request");
571        log::debug!("Completion intent: {:?}", completion_intent);
572        log::debug!("Completion mode: {:?}", self.completion_mode);
573
574        let messages = self.build_request_messages();
575        log::info!("Request will include {} messages", messages.len());
576
577        let tools: Vec<LanguageModelRequestTool> = self
578            .tools
579            .values()
580            .filter_map(|tool| {
581                let tool_name = tool.name().to_string();
582                log::trace!("Including tool: {}", tool_name);
583                Some(LanguageModelRequestTool {
584                    name: tool_name,
585                    description: tool.description(cx).to_string(),
586                    input_schema: tool
587                        .input_schema(LanguageModelToolSchemaFormat::JsonSchema)
588                        .log_err()?,
589                })
590            })
591            .collect();
592
593        log::info!("Request includes {} tools", tools.len());
594
595        let request = LanguageModelRequest {
596            thread_id: None,
597            prompt_id: None,
598            intent: Some(completion_intent),
599            mode: Some(self.completion_mode),
600            messages,
601            tools,
602            tool_choice: None,
603            stop: Vec::new(),
604            temperature: None,
605            thinking_allowed: true,
606        };
607
608        log::debug!("Completion request built successfully");
609        request
610    }
611
612    fn build_request_messages(&self) -> Vec<LanguageModelRequestMessage> {
613        log::trace!(
614            "Building request messages from {} thread messages",
615            self.messages.len()
616        );
617
618        let messages = Some(self.build_system_message())
619            .iter()
620            .chain(self.messages.iter())
621            .map(|message| {
622                log::trace!(
623                    "  - {} message with {} content items",
624                    match message.role {
625                        Role::System => "System",
626                        Role::User => "User",
627                        Role::Assistant => "Assistant",
628                    },
629                    message.content.len()
630                );
631                LanguageModelRequestMessage {
632                    role: message.role,
633                    content: message.content.clone(),
634                    cache: false,
635                }
636            })
637            .collect();
638        messages
639    }
640
641    pub fn to_markdown(&self) -> String {
642        let mut markdown = String::new();
643        for message in &self.messages {
644            markdown.push_str(&message.to_markdown());
645        }
646        markdown
647    }
648}
649
650pub trait AgentTool
651where
652    Self: 'static + Sized,
653{
654    type Input: for<'de> Deserialize<'de> + JsonSchema;
655
656    fn name(&self) -> SharedString;
657    fn description(&self, _cx: &mut App) -> SharedString {
658        let schema = schemars::schema_for!(Self::Input);
659        SharedString::new(
660            schema
661                .get("description")
662                .and_then(|description| description.as_str())
663                .unwrap_or_default(),
664        )
665    }
666
667    /// Returns the JSON schema that describes the tool's input.
668    fn input_schema(&self, _format: LanguageModelToolSchemaFormat) -> Schema {
669        schemars::schema_for!(Self::Input)
670    }
671
672    /// Returns true if the tool needs the users's authorization
673    /// before running.
674    fn needs_authorization(&self, input: Self::Input, cx: &App) -> bool;
675
676    /// Runs the tool with the provided input.
677    fn run(self: Arc<Self>, input: Self::Input, cx: &mut App) -> Task<Result<String>>;
678
679    fn erase(self) -> Arc<dyn AnyAgentTool> {
680        Arc::new(Erased(Arc::new(self)))
681    }
682}
683
684pub struct Erased<T>(T);
685
686pub trait AnyAgentTool {
687    fn name(&self) -> SharedString;
688    fn description(&self, cx: &mut App) -> SharedString;
689    fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value>;
690    fn needs_authorization(&self, input: serde_json::Value, cx: &mut App) -> Result<bool>;
691    fn run(self: Arc<Self>, input: serde_json::Value, cx: &mut App) -> Task<Result<String>>;
692}
693
694impl<T> AnyAgentTool for Erased<Arc<T>>
695where
696    T: AgentTool,
697{
698    fn name(&self) -> SharedString {
699        self.0.name()
700    }
701
702    fn description(&self, cx: &mut App) -> SharedString {
703        self.0.description(cx)
704    }
705
706    fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> {
707        Ok(serde_json::to_value(self.0.input_schema(format))?)
708    }
709
710    fn needs_authorization(&self, input: serde_json::Value, cx: &mut App) -> Result<bool> {
711        let parsed_input: Result<T::Input> = serde_json::from_value(input).map_err(Into::into);
712        match parsed_input {
713            Ok(input) => Ok(self.0.needs_authorization(input, cx)),
714            Err(error) => Err(anyhow!(error)),
715        }
716    }
717
718    fn run(self: Arc<Self>, input: serde_json::Value, cx: &mut App) -> Task<Result<String>> {
719        let parsed_input: Result<T::Input> = serde_json::from_value(input).map_err(Into::into);
720        match parsed_input {
721            Ok(input) => self.0.clone().run(input, cx),
722            Err(error) => Task::ready(Err(anyhow!(error))),
723        }
724    }
725}
726
727#[derive(Clone)]
728struct AgentResponseEventStream(
729    mpsc::UnboundedSender<Result<AgentResponseEvent, LanguageModelCompletionError>>,
730);
731
732impl AgentResponseEventStream {
733    fn send_text(&self, text: &str) {
734        self.0
735            .unbounded_send(Ok(AgentResponseEvent::Text(text.to_string())))
736            .ok();
737    }
738
739    fn send_thinking(&self, text: &str) {
740        self.0
741            .unbounded_send(Ok(AgentResponseEvent::Thinking(text.to_string())))
742            .ok();
743    }
744
745    fn authorize_tool_call(
746        &self,
747        tool_use: &LanguageModelToolUse,
748    ) -> impl use<> + Future<Output = Result<()>> {
749        let (response_tx, response_rx) = oneshot::channel();
750        self.0
751            .unbounded_send(Ok(AgentResponseEvent::ToolCallAuthorization(
752                ToolCallAuthorization {
753                    tool_call: acp::ToolCall {
754                        id: acp::ToolCallId(tool_use.id.to_string().into()),
755                        title: tool_use.name.to_string(),
756                        kind: acp::ToolKind::Other,
757                        status: acp::ToolCallStatus::Pending,
758                        content: vec![],
759                        locations: vec![],
760                        raw_input: Some(tool_use.input.clone()),
761                    },
762                    options: vec![
763                        acp::PermissionOption {
764                            id: acp::PermissionOptionId("always_allow".into()),
765                            name: "Always Allow".into(),
766                            kind: acp::PermissionOptionKind::AllowAlways,
767                        },
768                        acp::PermissionOption {
769                            id: acp::PermissionOptionId("allow".into()),
770                            name: "Allow".into(),
771                            kind: acp::PermissionOptionKind::AllowOnce,
772                        },
773                        acp::PermissionOption {
774                            id: acp::PermissionOptionId("deny".into()),
775                            name: "Deny".into(),
776                            kind: acp::PermissionOptionKind::RejectOnce,
777                        },
778                    ],
779                    response: response_tx,
780                },
781            )))
782            .ok();
783        async move {
784            match response_rx.await?.0.as_ref() {
785                "allow" | "always_allow" => Ok(()),
786                _ => Err(anyhow!("Permission to run tool denied by user")),
787            }
788        }
789    }
790
791    fn send_tool_call(&self, tool_use: &LanguageModelToolUse) {
792        self.0
793            .unbounded_send(Ok(AgentResponseEvent::ToolCall(acp::ToolCall {
794                id: acp::ToolCallId(tool_use.id.to_string().into()),
795                title: tool_use.name.to_string(),
796                kind: acp::ToolKind::Other,
797                status: acp::ToolCallStatus::Pending,
798                content: vec![],
799                locations: vec![],
800                raw_input: Some(tool_use.input.clone()),
801            })))
802            .ok();
803    }
804
805    fn send_tool_call_update(
806        &self,
807        tool_use_id: &LanguageModelToolUseId,
808        fields: acp::ToolCallUpdateFields,
809    ) {
810        self.0
811            .unbounded_send(Ok(AgentResponseEvent::ToolCallUpdate(
812                acp::ToolCallUpdate {
813                    id: acp::ToolCallId(tool_use_id.to_string().into()),
814                    fields,
815                },
816            )))
817            .ok();
818    }
819
820    fn send_tool_call_result(&self, tool_result: &LanguageModelToolResult) {
821        let status = if tool_result.is_error {
822            acp::ToolCallStatus::Failed
823        } else {
824            acp::ToolCallStatus::Completed
825        };
826        let content = match &tool_result.content {
827            LanguageModelToolResultContent::Text(text) => text.to_string().into(),
828            LanguageModelToolResultContent::Image(LanguageModelImage { source, .. }) => {
829                acp::ToolCallContent::Content {
830                    content: acp::ContentBlock::Image(acp::ImageContent {
831                        annotations: None,
832                        data: source.to_string(),
833                        mime_type: ImageFormat::Png.mime_type().to_string(),
834                    }),
835                }
836            }
837        };
838        self.0
839            .unbounded_send(Ok(AgentResponseEvent::ToolCallUpdate(
840                acp::ToolCallUpdate {
841                    id: acp::ToolCallId(tool_result.tool_use_id.to_string().into()),
842                    fields: acp::ToolCallUpdateFields {
843                        status: Some(status),
844                        content: Some(vec![content]),
845                        ..Default::default()
846                    },
847                },
848            )))
849            .ok();
850    }
851
852    fn send_stop(&self, reason: StopReason) {
853        match reason {
854            StopReason::EndTurn => {
855                self.0
856                    .unbounded_send(Ok(AgentResponseEvent::Stop(acp::StopReason::EndTurn)))
857                    .ok();
858            }
859            StopReason::MaxTokens => {
860                self.0
861                    .unbounded_send(Ok(AgentResponseEvent::Stop(acp::StopReason::MaxTokens)))
862                    .ok();
863            }
864            StopReason::Refusal => {
865                self.0
866                    .unbounded_send(Ok(AgentResponseEvent::Stop(acp::StopReason::Refusal)))
867                    .ok();
868            }
869            StopReason::ToolUse => {}
870        }
871    }
872
873    fn send_error(&self, error: LanguageModelCompletionError) {
874        self.0.unbounded_send(Err(error)).ok();
875    }
876}