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}