From c478cdf30e55d51b1cc389b2447ec2557314432c Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 23 Jul 2025 14:33:26 +0200 Subject: [PATCH] Use acp_old everywhere --- crates/acp_thread/src/acp_thread.rs | 2 +- crates/acp_thread/src/connection.rs | 12 +- crates/agent_servers/src/claude.rs | 59 +++--- crates/agent_servers/src/claude/tools.rs | 171 +++++++++--------- crates/agent_servers/src/codex.rs | 28 ++- crates/agent_servers/src/e2e_tests.rs | 34 ++-- crates/agent_servers/src/mcp_server.rs | 8 +- .../agent_servers/src/stdio_agent_server.rs | 6 +- crates/agent_ui/src/acp/thread_view.rs | 107 ++++++----- 9 files changed, 221 insertions(+), 206 deletions(-) diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index e679597425..3c2dae5e08 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -375,7 +375,7 @@ impl ToolCallContent { .collect() } - fn to_markdown(&self, cx: &App) -> String { + pub fn to_markdown(&self, cx: &App) -> String { match self { Self::ContentBlock { content } => content.to_markdown(cx).to_string(), Self::Diff { diff } => diff.to_markdown(cx), diff --git a/crates/acp_thread/src/connection.rs b/crates/acp_thread/src/connection.rs index 7c0ba4f41c..094cd3efd0 100644 --- a/crates/acp_thread/src/connection.rs +++ b/crates/acp_thread/src/connection.rs @@ -1,19 +1,19 @@ -use agentic_coding_protocol as acp; +use agentic_coding_protocol as acp_old; use anyhow::Result; use futures::future::{FutureExt as _, LocalBoxFuture}; pub trait AgentConnection { fn request_any( &self, - params: acp::AnyAgentRequest, - ) -> LocalBoxFuture<'static, Result>; + params: acp_old::AnyAgentRequest, + ) -> LocalBoxFuture<'static, Result>; } -impl AgentConnection for acp::AgentConnection { +impl AgentConnection for acp_old::AgentConnection { fn request_any( &self, - params: acp::AnyAgentRequest, - ) -> LocalBoxFuture<'static, Result> { + params: acp_old::AnyAgentRequest, + ) -> LocalBoxFuture<'static, Result> { let task = self.request_any(params); async move { Ok(task.await?) }.boxed_local() } diff --git a/crates/agent_servers/src/claude.rs b/crates/agent_servers/src/claude.rs index d91babbd21..86321bcb98 100644 --- a/crates/agent_servers/src/claude.rs +++ b/crates/agent_servers/src/claude.rs @@ -11,10 +11,7 @@ use std::pin::pin; use std::rc::Rc; use uuid::Uuid; -use agentic_coding_protocol::{ - self as acp, AnyAgentRequest, AnyAgentResult, Client, ProtocolVersion, - StreamAssistantMessageChunkParams, ToolCallContent, UpdateToolCallParams, -}; +use agentic_coding_protocol as acp_old; use anyhow::{Result, anyhow}; use futures::channel::oneshot; use futures::future::LocalBoxFuture; @@ -32,7 +29,7 @@ use util::ResultExt; use crate::claude::tools::ClaudeTool; use crate::mcp_server::{self, McpConfig, ZedMcpServer}; use crate::{AgentServer, AgentServerCommand, AllAgentServersSettings}; -use acp_thread::{OldAcpClientDelegate, AcpThread, AgentConnection}; +use acp_thread::{AcpThread, AgentConnection, OldAcpClientDelegate}; #[derive(Clone)] pub struct ClaudeCode; @@ -211,8 +208,8 @@ impl AgentConnection for ClaudeAgentConnection { /// Send a request to the agent and wait for a response. fn request_any( &self, - params: AnyAgentRequest, - ) -> LocalBoxFuture<'static, Result> { + params: acp_old::AnyAgentRequest, + ) -> LocalBoxFuture<'static, Result> { let delegate = self.delegate.clone(); let end_turn_tx = self.end_turn_tx.clone(); let outgoing_tx = self.outgoing_tx.clone(); @@ -221,16 +218,16 @@ impl AgentConnection for ClaudeAgentConnection { async move { match params { // todo: consider sending an empty request so we get the init response? - AnyAgentRequest::InitializeParams(_) => Ok(AnyAgentResult::InitializeResponse( - acp::InitializeResponse { + acp_old::AnyAgentRequest::InitializeParams(_) => Ok( + acp_old::AnyAgentResult::InitializeResponse(acp::InitializeResponse { is_authenticated: true, - protocol_version: ProtocolVersion::latest(), - }, - )), - AnyAgentRequest::AuthenticateParams(_) => { + protocol_version: acp_old::ProtocolVersion::latest(), + }), + ), + acp_old::AnyAgentRequest::AuthenticateParams(_) => { Err(anyhow!("Authentication not supported")) } - AnyAgentRequest::SendUserMessageParams(message) => { + acp_old::AnyAgentRequest::SendUserMessageParams(message) => { delegate.clear_completed_plan_entries().await?; let (tx, rx) = oneshot::channel(); @@ -238,10 +235,8 @@ impl AgentConnection for ClaudeAgentConnection { let mut content = String::new(); for chunk in message.chunks { match chunk { - agentic_coding_protocol::UserMessageChunk::Text { text } => { - content.push_str(&text) - } - agentic_coding_protocol::UserMessageChunk::Path { path } => { + acp_old::UserMessageChunk::Text { text } => content.push_str(&text), + acp_old::UserMessageChunk::Path { path } => { content.push_str(&format!("@{path:?}")) } } @@ -259,16 +254,16 @@ impl AgentConnection for ClaudeAgentConnection { session_id: Some(session_id), })?; rx.await??; - Ok(AnyAgentResult::SendUserMessageResponse( + Ok(acp_old::AnyAgentResult::SendUserMessageResponse( acp::SendUserMessageResponse, )) } - AnyAgentRequest::CancelSendMessageParams(_) => { + acp_old::AnyAgentRequest::CancelSendMessageParams(_) => { let (done_tx, done_rx) = oneshot::channel(); cancel_tx.send(done_tx).await?; done_rx.await??; - Ok(AnyAgentResult::CancelSendMessageResponse( + Ok(acp_old::AnyAgentResult::CancelSendMessageResponse( acp::CancelSendMessageResponse, )) } @@ -350,9 +345,11 @@ impl ClaudeAgentConnection { match chunk { ContentChunk::Text { text } | ContentChunk::UntaggedText(text) => { delegate - .stream_assistant_message_chunk(StreamAssistantMessageChunkParams { - chunk: acp::AssistantMessageChunk::Text { text }, - }) + .stream_assistant_message_chunk( + acp_old::StreamAssistantMessageChunkParams { + chunk: acp::AssistantMessageChunk::Text { text }, + }, + ) .await .log_err(); } @@ -382,12 +379,12 @@ impl ClaudeAgentConnection { if let Some(id) = id { let content = content.to_string(); delegate - .update_tool_call(UpdateToolCallParams { + .update_tool_call(acp_old::UpdateToolCallParams { tool_call_id: id, status: acp::ToolCallStatus::Finished, // Don't unset existing content content: (!content.is_empty()).then_some( - ToolCallContent::Markdown { + acp_old::ToolCallContent::Markdown { // For now we only include text content markdown: content, }, @@ -403,11 +400,13 @@ impl ClaudeAgentConnection { | ContentChunk::RedactedThinking | ContentChunk::WebSearchToolResult => { delegate - .stream_assistant_message_chunk(StreamAssistantMessageChunkParams { - chunk: acp::AssistantMessageChunk::Text { - text: format!("Unsupported content: {:?}", chunk), + .stream_assistant_message_chunk( + acp_old::StreamAssistantMessageChunkParams { + chunk: acp::AssistantMessageChunk::Text { + text: format!("Unsupported content: {:?}", chunk), + }, }, - }) + ) .await .log_err(); } diff --git a/crates/agent_servers/src/claude/tools.rs b/crates/agent_servers/src/claude/tools.rs index 9c82139a07..4ba6ccd767 100644 --- a/crates/agent_servers/src/claude/tools.rs +++ b/crates/agent_servers/src/claude/tools.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use agentic_coding_protocol::{self as acp, PushToolCallParams, ToolCallLocation}; +use agentic_coding_protocol as acp_old; use itertools::Itertools; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -116,49 +116,49 @@ impl ClaudeTool { } } - pub fn content(&self) -> Option { + pub fn content(&self) -> Option { match &self { - Self::Other { input, .. } => Some(acp::ToolCallContent::Markdown { + Self::Other { input, .. } => Some(acp_old::ToolCallContent::Markdown { markdown: format!( "```json\n{}```", serde_json::to_string_pretty(&input).unwrap_or("{}".to_string()) ), }), - Self::Task(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::Task(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.prompt.clone(), }), - Self::NotebookRead(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::NotebookRead(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.notebook_path.display().to_string(), }), - Self::NotebookEdit(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::NotebookEdit(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.new_source.clone(), }), - Self::Terminal(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::Terminal(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: format!( "`{}`\n\n{}", params.command, params.description.as_deref().unwrap_or_default() ), }), - Self::ReadFile(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::ReadFile(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.abs_path.display().to_string(), }), - Self::Ls(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::Ls(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.path.display().to_string(), }), - Self::Glob(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::Glob(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.to_string(), }), - Self::Grep(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::Grep(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: format!("`{params}`"), }), - Self::WebFetch(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::WebFetch(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.prompt.clone(), }), - Self::WebSearch(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::WebSearch(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.to_string(), }), - Self::TodoWrite(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::TodoWrite(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params .todos .iter() @@ -176,18 +176,18 @@ impl ClaudeTool { }) .join("\n"), }), - Self::ExitPlanMode(Some(params)) => Some(acp::ToolCallContent::Markdown { + Self::ExitPlanMode(Some(params)) => Some(acp_old::ToolCallContent::Markdown { markdown: params.plan.clone(), }), - Self::Edit(Some(params)) => Some(acp::ToolCallContent::Diff { - diff: acp::Diff { + Self::Edit(Some(params)) => Some(acp_old::ToolCallContent::Diff { + diff: acp_old::Diff { path: params.abs_path.clone(), old_text: Some(params.old_text.clone()), new_text: params.new_text.clone(), }, }), - Self::Write(Some(params)) => Some(acp::ToolCallContent::Diff { - diff: acp::Diff { + Self::Write(Some(params)) => Some(acp_old::ToolCallContent::Diff { + diff: acp_old::Diff { path: params.file_path.clone(), old_text: None, new_text: params.content.clone(), @@ -195,13 +195,16 @@ impl ClaudeTool { }), Self::MultiEdit(Some(params)) => { // todo: show multiple edits in a multibuffer? - params.edits.first().map(|edit| acp::ToolCallContent::Diff { - diff: acp::Diff { - path: params.file_path.clone(), - old_text: Some(edit.old_string.clone()), - new_text: edit.new_string.clone(), - }, - }) + params + .edits + .first() + .map(|edit| acp_old::ToolCallContent::Diff { + diff: acp_old::Diff { + path: params.file_path.clone(), + old_text: Some(edit.old_string.clone()), + new_text: edit.new_string.clone(), + }, + }) } Self::Task(None) | Self::NotebookRead(None) @@ -221,33 +224,33 @@ impl ClaudeTool { } } - pub fn icon(&self) -> acp::Icon { + pub fn icon(&self) -> acp_old::Icon { match self { - Self::Task(_) => acp::Icon::Hammer, - Self::NotebookRead(_) => acp::Icon::FileSearch, - Self::NotebookEdit(_) => acp::Icon::Pencil, - Self::Edit(_) => acp::Icon::Pencil, - Self::MultiEdit(_) => acp::Icon::Pencil, - Self::Write(_) => acp::Icon::Pencil, - Self::ReadFile(_) => acp::Icon::FileSearch, - Self::Ls(_) => acp::Icon::Folder, - Self::Glob(_) => acp::Icon::FileSearch, - Self::Grep(_) => acp::Icon::Regex, - Self::Terminal(_) => acp::Icon::Terminal, - Self::WebSearch(_) => acp::Icon::Globe, - Self::WebFetch(_) => acp::Icon::Globe, - Self::TodoWrite(_) => acp::Icon::LightBulb, - Self::ExitPlanMode(_) => acp::Icon::Hammer, - Self::Other { .. } => acp::Icon::Hammer, + Self::Task(_) => acp_old::Icon::Hammer, + Self::NotebookRead(_) => acp_old::Icon::FileSearch, + Self::NotebookEdit(_) => acp_old::Icon::Pencil, + Self::Edit(_) => acp_old::Icon::Pencil, + Self::MultiEdit(_) => acp_old::Icon::Pencil, + Self::Write(_) => acp_old::Icon::Pencil, + Self::ReadFile(_) => acp_old::Icon::FileSearch, + Self::Ls(_) => acp_old::Icon::Folder, + Self::Glob(_) => acp_old::Icon::FileSearch, + Self::Grep(_) => acp_old::Icon::Regex, + Self::Terminal(_) => acp_old::Icon::Terminal, + Self::WebSearch(_) => acp_old::Icon::Globe, + Self::WebFetch(_) => acp_old::Icon::Globe, + Self::TodoWrite(_) => acp_old::Icon::LightBulb, + Self::ExitPlanMode(_) => acp_old::Icon::Hammer, + Self::Other { .. } => acp_old::Icon::Hammer, } } - pub fn confirmation(&self, description: Option) -> acp::ToolCallConfirmation { + pub fn confirmation(&self, description: Option) -> acp_old::ToolCallConfirmation { match &self { Self::Edit(_) | Self::Write(_) | Self::NotebookEdit(_) | Self::MultiEdit(_) => { - acp::ToolCallConfirmation::Edit { description } + acp_old::ToolCallConfirmation::Edit { description } } - Self::WebFetch(params) => acp::ToolCallConfirmation::Fetch { + Self::WebFetch(params) => acp_old::ToolCallConfirmation::Fetch { urls: params .as_ref() .map(|p| vec![p.url.clone()]) @@ -258,19 +261,19 @@ impl ClaudeTool { description, command, .. - })) => acp::ToolCallConfirmation::Execute { + })) => acp_old::ToolCallConfirmation::Execute { command: command.clone(), root_command: command.clone(), description: description.clone(), }, - Self::ExitPlanMode(Some(params)) => acp::ToolCallConfirmation::Other { + Self::ExitPlanMode(Some(params)) => acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {}", params.plan) } else { params.plan.clone() }, }, - Self::Task(Some(params)) => acp::ToolCallConfirmation::Other { + Self::Task(Some(params)) => acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {}", params.description) } else { @@ -280,7 +283,7 @@ impl ClaudeTool { Self::Ls(Some(LsToolParams { path, .. })) | Self::ReadFile(Some(ReadToolParams { abs_path: path, .. })) => { let path = path.display(); - acp::ToolCallConfirmation::Other { + acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {path}") } else { @@ -290,7 +293,7 @@ impl ClaudeTool { } Self::NotebookRead(Some(NotebookReadToolParams { notebook_path, .. })) => { let path = notebook_path.display(); - acp::ToolCallConfirmation::Other { + acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {path}") } else { @@ -298,21 +301,21 @@ impl ClaudeTool { }, } } - Self::Glob(Some(params)) => acp::ToolCallConfirmation::Other { + Self::Glob(Some(params)) => acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {params}") } else { params.to_string() }, }, - Self::Grep(Some(params)) => acp::ToolCallConfirmation::Other { + Self::Grep(Some(params)) => acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {params}") } else { params.to_string() }, }, - Self::WebSearch(Some(params)) => acp::ToolCallConfirmation::Other { + Self::WebSearch(Some(params)) => acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {params}") } else { @@ -321,7 +324,7 @@ impl ClaudeTool { }, Self::TodoWrite(Some(params)) => { let params = params.todos.iter().map(|todo| &todo.content).join(", "); - acp::ToolCallConfirmation::Other { + acp_old::ToolCallConfirmation::Other { description: if let Some(description) = description { format!("{description} {params}") } else { @@ -339,53 +342,55 @@ impl ClaudeTool { | Self::ReadFile(None) | Self::WebSearch(None) | Self::TodoWrite(None) - | Self::Other { .. } => acp::ToolCallConfirmation::Other { + | Self::Other { .. } => acp_old::ToolCallConfirmation::Other { description: description.unwrap_or("".to_string()), }, } } - pub fn locations(&self) -> Vec { + pub fn locations(&self) -> Vec { match &self { - Self::Edit(Some(EditToolParams { abs_path, .. })) => vec![ToolCallLocation { + Self::Edit(Some(EditToolParams { abs_path, .. })) => vec![acp_old::ToolCallLocation { path: abs_path.clone(), line: None, }], Self::MultiEdit(Some(MultiEditToolParams { file_path, .. })) => { - vec![ToolCallLocation { + vec![acp_old::ToolCallLocation { + path: file_path.clone(), + line: None, + }] + } + Self::Write(Some(WriteToolParams { file_path, .. })) => { + vec![acp_old::ToolCallLocation { path: file_path.clone(), line: None, }] } - Self::Write(Some(WriteToolParams { file_path, .. })) => vec![ToolCallLocation { - path: file_path.clone(), - line: None, - }], Self::ReadFile(Some(ReadToolParams { abs_path, offset, .. - })) => vec![ToolCallLocation { + })) => vec![acp_old::ToolCallLocation { path: abs_path.clone(), line: *offset, }], Self::NotebookRead(Some(NotebookReadToolParams { notebook_path, .. })) => { - vec![ToolCallLocation { + vec![acp_old::ToolCallLocation { path: notebook_path.clone(), line: None, }] } Self::NotebookEdit(Some(NotebookEditToolParams { notebook_path, .. })) => { - vec![ToolCallLocation { + vec![acp_old::ToolCallLocation { path: notebook_path.clone(), line: None, }] } Self::Glob(Some(GlobToolParams { path: Some(path), .. - })) => vec![ToolCallLocation { + })) => vec![acp_old::ToolCallLocation { path: path.clone(), line: None, }], - Self::Ls(Some(LsToolParams { path, .. })) => vec![ToolCallLocation { + Self::Ls(Some(LsToolParams { path, .. })) => vec![acp_old::ToolCallLocation { path: path.clone(), line: None, }], @@ -414,8 +419,8 @@ impl ClaudeTool { } } - pub fn as_acp(&self) -> PushToolCallParams { - PushToolCallParams { + pub fn as_acp(&self) -> acp_old::PushToolCallParams { + acp_old::PushToolCallParams { label: self.label(), content: self.content(), icon: self.icon(), @@ -614,12 +619,12 @@ pub enum TodoPriority { Low, } -impl Into for TodoPriority { - fn into(self) -> acp::PlanEntryPriority { +impl Into for TodoPriority { + fn into(self) -> acp_old::PlanEntryPriority { match self { - TodoPriority::High => acp::PlanEntryPriority::High, - TodoPriority::Medium => acp::PlanEntryPriority::Medium, - TodoPriority::Low => acp::PlanEntryPriority::Low, + TodoPriority::High => acp_old::PlanEntryPriority::High, + TodoPriority::Medium => acp_old::PlanEntryPriority::Medium, + TodoPriority::Low => acp_old::PlanEntryPriority::Low, } } } @@ -632,12 +637,12 @@ pub enum TodoStatus { Completed, } -impl Into for TodoStatus { - fn into(self) -> acp::PlanEntryStatus { +impl Into for TodoStatus { + fn into(self) -> acp_old::PlanEntryStatus { match self { - TodoStatus::Pending => acp::PlanEntryStatus::Pending, - TodoStatus::InProgress => acp::PlanEntryStatus::InProgress, - TodoStatus::Completed => acp::PlanEntryStatus::Completed, + TodoStatus::Pending => acp_old::PlanEntryStatus::Pending, + TodoStatus::InProgress => acp_old::PlanEntryStatus::InProgress, + TodoStatus::Completed => acp_old::PlanEntryStatus::Completed, } } } @@ -654,9 +659,9 @@ pub struct Todo { pub status: TodoStatus, } -impl Into for Todo { - fn into(self) -> acp::PlanEntry { - acp::PlanEntry { +impl Into for Todo { + fn into(self) -> acp_old::PlanEntry { + acp_old::PlanEntry { content: self.content, priority: self.priority.into(), status: self.status.into(), diff --git a/crates/agent_servers/src/codex.rs b/crates/agent_servers/src/codex.rs index c443e688d6..45c75ed480 100644 --- a/crates/agent_servers/src/codex.rs +++ b/crates/agent_servers/src/codex.rs @@ -13,9 +13,7 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Arc; -use agentic_coding_protocol::{ - self as acp, AnyAgentRequest, AnyAgentResult, Client as _, ProtocolVersion, -}; +use agentic_coding_protocol::{self as acp_old, Client as _}; use anyhow::{Context, Result, anyhow}; use futures::future::LocalBoxFuture; use futures::{AsyncWriteExt, FutureExt, SinkExt as _}; @@ -341,8 +339,8 @@ impl AgentConnection for CodexAgentConnection { /// Send a request to the agent and wait for a response. fn request_any( &self, - params: AnyAgentRequest, - ) -> LocalBoxFuture<'static, Result> { + params: acp_old::AnyAgentRequest, + ) -> LocalBoxFuture<'static, Result> { let client = self.codex_mcp.client(); let root_dir = self.root_dir.clone(); let cancel_request_tx = self.cancel_request_tx.clone(); @@ -351,16 +349,16 @@ impl AgentConnection for CodexAgentConnection { match params { // todo: consider sending an empty request so we get the init response? - AnyAgentRequest::InitializeParams(_) => Ok(AnyAgentResult::InitializeResponse( - acp::InitializeResponse { + acp_old::AnyAgentRequest::InitializeParams(_) => Ok( + acp_old::AnyAgentResult::InitializeResponse(acp::InitializeResponse { is_authenticated: true, - protocol_version: ProtocolVersion::latest(), - }, - )), - AnyAgentRequest::AuthenticateParams(_) => { + protocol_version: acp_old::ProtocolVersion::latest(), + }), + ), + acp_old::AnyAgentRequest::AuthenticateParams(_) => { Err(anyhow!("Authentication not supported")) } - AnyAgentRequest::SendUserMessageParams(message) => { + acp_old::AnyAgentRequest::SendUserMessageParams(message) => { let (new_cancel_tx, cancel_rx) = oneshot::channel(); cancel_request_tx.borrow_mut().replace(new_cancel_tx); @@ -388,18 +386,18 @@ impl AgentConnection for CodexAgentConnection { ) .await?; - Ok(AnyAgentResult::SendUserMessageResponse( + Ok(acp_old::AnyAgentResult::SendUserMessageResponse( acp::SendUserMessageResponse, )) } - AnyAgentRequest::CancelSendMessageParams(_) => { + acp_old::AnyAgentRequest::CancelSendMessageParams(_) => { if let Ok(mut borrow) = cancel_request_tx.try_borrow_mut() { if let Some(cancel_tx) = borrow.take() { cancel_tx.send(()).ok(); } } - Ok(AnyAgentResult::CancelSendMessageResponse( + Ok(acp_old::AnyAgentResult::CancelSendMessageResponse( acp::CancelSendMessageResponse, )) } diff --git a/crates/agent_servers/src/e2e_tests.rs b/crates/agent_servers/src/e2e_tests.rs index 09faaa5853..b13f5d1a4c 100644 --- a/crates/agent_servers/src/e2e_tests.rs +++ b/crates/agent_servers/src/e2e_tests.rs @@ -4,7 +4,7 @@ use crate::{AgentServer, AgentServerSettings, AllAgentServersSettings}; use acp_thread::{ AcpThread, AgentThreadEntry, ToolCall, ToolCallConfirmation, ToolCallContent, ToolCallStatus, }; -use agentic_coding_protocol as acp; +use agentic_coding_protocol as acp_old; use futures::{FutureExt, StreamExt, channel::mpsc, select}; use gpui::{Entity, TestAppContext}; use indoc::indoc; @@ -54,15 +54,15 @@ pub async fn test_path_mentions(server: impl AgentServer + 'static, cx: &mut Tes thread .update(cx, |thread, cx| { thread.send( - acp::SendUserMessageParams { + acp_old::SendUserMessageParams { chunks: vec![ - acp::UserMessageChunk::Text { + acp_old::UserMessageChunk::Text { text: "Read the file ".into(), }, - acp::UserMessageChunk::Path { + acp_old::UserMessageChunk::Path { path: Path::new("foo.rs").into(), }, - acp::UserMessageChunk::Text { + acp_old::UserMessageChunk::Text { text: " and tell me what the content of the println! is".into(), }, ], @@ -161,11 +161,8 @@ pub async fn test_tool_call_with_confirmation( let tool_call_id = thread.read_with(cx, |thread, _cx| { let AgentThreadEntry::ToolCall(ToolCall { id, - status: - ToolCallStatus::WaitingForConfirmation { - confirmation: ToolCallConfirmation::Execute { root_command, .. }, - .. - }, + content: Some(content), + status: ToolCallStatus::WaitingForConfirmation { .. }, .. }) = &thread .entries() @@ -176,13 +173,17 @@ pub async fn test_tool_call_with_confirmation( panic!(); }; - assert!(root_command.contains("touch")); + assert!(content.to_markdown(cx).contains("touch")); *id }); thread.update(cx, |thread, cx| { - thread.authorize_tool_call(tool_call_id, acp::ToolCallConfirmationOutcome::Allow, cx); + thread.authorize_tool_call( + tool_call_id, + acp_old::ToolCallConfirmationOutcome::Allow, + cx, + ); assert!(thread.entries().iter().any(|entry| matches!( entry, @@ -249,18 +250,15 @@ pub async fn test_cancel(server: impl AgentServer + 'static, cx: &mut TestAppCon thread.read_with(cx, |thread, _cx| { let AgentThreadEntry::ToolCall(ToolCall { id, - status: - ToolCallStatus::WaitingForConfirmation { - confirmation: ToolCallConfirmation::Execute { root_command, .. }, - .. - }, + content: Some(content), + status: ToolCallStatus::WaitingForConfirmation { .. }, .. }) = &thread.entries()[first_tool_call_ix] else { panic!("{:?}", thread.entries()[1]); }; - assert!(root_command.contains("touch")); + assert!(content.to_markdown(cx).contains("touch")); *id }); diff --git a/crates/agent_servers/src/mcp_server.rs b/crates/agent_servers/src/mcp_server.rs index dc4726b8c0..f1f8ead69b 100644 --- a/crates/agent_servers/src/mcp_server.rs +++ b/crates/agent_servers/src/mcp_server.rs @@ -1,7 +1,7 @@ use std::{cell::RefCell, path::PathBuf, rc::Rc}; use acp_thread::OldAcpClientDelegate; -use agentic_coding_protocol::{self as acp, Client, ReadTextFileParams, WriteTextFileParams}; +use agentic_coding_protocol::{self as acp_old, Client as _}; use anyhow::{Context, Result}; use collections::HashMap; use context_server::types::{ @@ -207,7 +207,7 @@ impl ZedMcpServer { ) -> Task> { cx.foreground_executor().spawn(async move { let response = delegate - .read_text_file(ReadTextFileParams { + .read_text_file(acp_old::ReadTextFileParams { path: params.abs_path, line: params.offset, limit: params.limit, @@ -227,7 +227,7 @@ impl ZedMcpServer { ) -> Task> { cx.foreground_executor().spawn(async move { let response = delegate - .read_text_file_reusing_snapshot(ReadTextFileParams { + .read_text_file_reusing_snapshot(acp_old::ReadTextFileParams { path: params.abs_path.clone(), line: None, limit: None, @@ -240,7 +240,7 @@ impl ZedMcpServer { } delegate - .write_text_file(WriteTextFileParams { + .write_text_file(acp_old::WriteTextFileParams { path: params.abs_path, content: new_content, }) diff --git a/crates/agent_servers/src/stdio_agent_server.rs b/crates/agent_servers/src/stdio_agent_server.rs index 4751f2bb41..6c6f52519d 100644 --- a/crates/agent_servers/src/stdio_agent_server.rs +++ b/crates/agent_servers/src/stdio_agent_server.rs @@ -1,6 +1,6 @@ use crate::{AgentServer, AgentServerCommand, AgentServerVersion}; -use acp_thread::{OldAcpClientDelegate, AcpThread, LoadError}; -use agentic_coding_protocol as acp; +use acp_thread::{AcpThread, LoadError, OldAcpClientDelegate}; +use agentic_coding_protocol as acp_old; use anyhow::{Result, anyhow}; use gpui::{App, AsyncApp, Entity, Task, prelude::*}; use project::Project; @@ -76,7 +76,7 @@ impl AgentServer for T { cx.new(|cx| { let foreground_executor = cx.foreground_executor().clone(); - let (connection, io_fut) = acp::AgentConnection::connect_to_agent( + let (connection, io_fut) = acp_old::AgentConnection::connect_to_agent( OldAcpClientDelegate::new(cx.entity().downgrade(), cx.to_async()), stdin, stdout, diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 26cd7a0590..bfa60abd09 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -7,7 +7,8 @@ use std::rc::Rc; use std::sync::Arc; use std::time::Duration; -use agentic_coding_protocol::{self as acp}; +use agent_client_protocol as acp; +use agentic_coding_protocol as acp_old; use assistant_tool::ActionLog; use buffer_diff::BufferDiff; use collections::{HashMap, HashSet}; @@ -69,7 +70,7 @@ pub struct AcpThreadView { edits_expanded: bool, plan_expanded: bool, editor_expanded: bool, - message_history: Rc>>, + message_history: Rc>>, } enum ThreadState { @@ -89,7 +90,7 @@ enum ThreadState { struct AlwaysAllowOption { id: &'static str, label: SharedString, - outcome: acp::ToolCallConfirmationOutcome, + outcome: acp_old::ToolCallConfirmationOutcome, } impl AcpThreadView { @@ -97,7 +98,7 @@ impl AcpThreadView { agent: Rc, workspace: WeakEntity, project: Entity, - message_history: Rc>>, + message_history: Rc>>, min_lines: usize, max_lines: Option, window: &mut Window, @@ -362,7 +363,7 @@ impl AcpThreadView { self.last_error.take(); let mut ix = 0; - let mut chunks: Vec = Vec::new(); + let mut chunks: Vec = Vec::new(); let project = self.project.clone(); self.message_editor.update(cx, |editor, cx| { let text = editor.text(cx); @@ -374,12 +375,12 @@ impl AcpThreadView { { let crease_range = crease.range().to_offset(&snapshot.buffer_snapshot); if crease_range.start > ix { - chunks.push(acp::UserMessageChunk::Text { + chunks.push(acp_old::UserMessageChunk::Text { text: text[ix..crease_range.start].to_string(), }); } if let Some(abs_path) = project.read(cx).absolute_path(&project_path, cx) { - chunks.push(acp::UserMessageChunk::Path { path: abs_path }); + chunks.push(acp_old::UserMessageChunk::Path { path: abs_path }); } ix = crease_range.end; } @@ -388,7 +389,7 @@ impl AcpThreadView { if ix < text.len() { let last_chunk = text[ix..].trim(); if !last_chunk.is_empty() { - chunks.push(acp::UserMessageChunk::Text { + chunks.push(acp_old::UserMessageChunk::Text { text: last_chunk.into(), }); } @@ -401,7 +402,7 @@ impl AcpThreadView { } let Some(thread) = self.thread() else { return }; - let message = acp::SendUserMessageParams { chunks }; + let message = acp_old::SendUserMessageParams { chunks }; let task = thread.update(cx, |thread, cx| thread.send(message.clone(), cx)); cx.spawn(async move |this, cx| { @@ -490,7 +491,7 @@ impl AcpThreadView { message_editor: Entity, mention_set: Arc>, project: Entity, - message: Option<&acp::SendUserMessageParams>, + message: Option<&acp_old::SendUserMessageParams>, window: &mut Window, cx: &mut Context, ) -> bool { @@ -505,10 +506,10 @@ impl AcpThreadView { for chunk in &message.chunks { match chunk { - acp::UserMessageChunk::Text { text: chunk } => { + acp_old::UserMessageChunk::Text { text: chunk } => { text.push_str(&chunk); } - acp::UserMessageChunk::Path { path } => { + acp_old::UserMessageChunk::Path { path } => { let start = text.len(); let content = MentionPath::new(path).to_string(); text.push_str(&content); @@ -685,7 +686,7 @@ impl AcpThreadView { fn authorize_tool_call( &mut self, id: ToolCallId, - outcome: acp::ToolCallConfirmationOutcome, + outcome: acp_old::ToolCallConfirmationOutcome, cx: &mut Context, ) { let Some(thread) = self.thread() else { @@ -866,7 +867,7 @@ impl AcpThreadView { let status_icon = match &tool_call.status { ToolCallStatus::WaitingForConfirmation { .. } => None, ToolCallStatus::Allowed { - status: acp::ToolCallStatus::Running, + status: acp::ToolCallStatus::InProgress, .. } => Some( Icon::new(IconName::ArrowCircle) @@ -880,13 +881,13 @@ impl AcpThreadView { .into_any(), ), ToolCallStatus::Allowed { - status: acp::ToolCallStatus::Finished, + status: acp::ToolCallStatus::Completed, .. } => None, ToolCallStatus::Rejected | ToolCallStatus::Canceled | ToolCallStatus::Allowed { - status: acp::ToolCallStatus::Error, + status: acp::ToolCallStatus::Failed, .. } => Some( Icon::new(IconName::X) @@ -909,14 +910,24 @@ impl AcpThreadView { let content = if is_open { match &tool_call.status { - ToolCallStatus::WaitingForConfirmation { confirmation, .. } => { - Some(self.render_tool_call_confirmation( - tool_call.id, - confirmation, - tool_call.content.as_ref(), - window, - cx, - )) + ToolCallStatus::WaitingForConfirmation { + possible_grants, + respond_tx, + } => { + // Some(self.render_tool_call_confirmation( + // tool_call.id, + // confirmation, + // tool_call.content.as_ref(), + // window, + // cx, + // )) + // todo! render buttons based on grants + tool_call.content.as_ref().map(|content| { + div() + .py_1p5() + .child(self.render_tool_call_content(content, window, cx)) + .into_any_element() + }) } ToolCallStatus::Allowed { .. } | ToolCallStatus::Canceled => { tool_call.content.as_ref().map(|content| { @@ -1114,7 +1125,7 @@ impl AcpThreadView { &[AlwaysAllowOption { id: "always_allow", label: "Always Allow Edits".into(), - outcome: acp::ToolCallConfirmationOutcome::AlwaysAllow, + outcome: acp_old::ToolCallConfirmationOutcome::AlwaysAllow, }], tool_call_id, cx, @@ -1141,7 +1152,7 @@ impl AcpThreadView { &[AlwaysAllowOption { id: "always_allow", label: format!("Always Allow {root_command}").into(), - outcome: acp::ToolCallConfirmationOutcome::AlwaysAllow, + outcome: acp_old::ToolCallConfirmationOutcome::AlwaysAllow, }], tool_call_id, cx, @@ -1171,12 +1182,12 @@ impl AcpThreadView { AlwaysAllowOption { id: "always_allow_server", label: format!("Always Allow {server_name}").into(), - outcome: acp::ToolCallConfirmationOutcome::AlwaysAllowMcpServer, + outcome: acp_old::ToolCallConfirmationOutcome::AlwaysAllowMcpServer, }, AlwaysAllowOption { id: "always_allow_tool", label: format!("Always Allow {tool_display_name}").into(), - outcome: acp::ToolCallConfirmationOutcome::AlwaysAllowTool, + outcome: acp_old::ToolCallConfirmationOutcome::AlwaysAllowTool, }, ], tool_call_id, @@ -1213,7 +1224,7 @@ impl AcpThreadView { &[AlwaysAllowOption { id: "always_allow", label: "Always Allow".into(), - outcome: acp::ToolCallConfirmationOutcome::AlwaysAllow, + outcome: acp_old::ToolCallConfirmationOutcome::AlwaysAllow, }], tool_call_id, cx, @@ -1229,7 +1240,7 @@ impl AcpThreadView { &[AlwaysAllowOption { id: "always_allow", label: "Always Allow".into(), - outcome: acp::ToolCallConfirmationOutcome::AlwaysAllow, + outcome: acp_old::ToolCallConfirmationOutcome::AlwaysAllow, }], tool_call_id, cx, @@ -1281,7 +1292,7 @@ impl AcpThreadView { move |this, _, _, cx| { this.authorize_tool_call( id, - acp::ToolCallConfirmationOutcome::Allow, + acp_old::ToolCallConfirmationOutcome::Allow, cx, ); } @@ -1298,7 +1309,7 @@ impl AcpThreadView { move |this, _, _, cx| { this.authorize_tool_call( id, - acp::ToolCallConfirmationOutcome::Reject, + acp_old::ToolCallConfirmationOutcome::Reject, cx, ); } @@ -1638,21 +1649,25 @@ impl AcpThreadView { .text_xs() .text_color(cx.theme().colors().text_muted) .child(match entry.status { - acp::PlanEntryStatus::Pending => Icon::new(IconName::TodoPending) + acp_old::PlanEntryStatus::Pending => Icon::new(IconName::TodoPending) .size(IconSize::Small) .color(Color::Muted) .into_any_element(), - acp::PlanEntryStatus::InProgress => Icon::new(IconName::TodoProgress) - .size(IconSize::Small) - .color(Color::Accent) - .with_animation( - "running", - Animation::new(Duration::from_secs(2)).repeat(), - |icon, delta| { - icon.transform(Transformation::rotate(percentage(delta))) - }, - ) - .into_any_element(), + acp_old::PlanEntryStatus::InProgress => { + Icon::new(IconName::TodoProgress) + .size(IconSize::Small) + .color(Color::Accent) + .with_animation( + "running", + Animation::new(Duration::from_secs(2)).repeat(), + |icon, delta| { + icon.transform(Transformation::rotate(percentage( + delta, + ))) + }, + ) + .into_any_element() + } acp::PlanEntryStatus::Completed => Icon::new(IconName::TodoComplete) .size(IconSize::Small) .color(Color::Success) @@ -2547,7 +2562,7 @@ fn default_markdown_style(buffer_font: bool, window: &Window, cx: &App) -> Markd } fn plan_label_markdown_style( - status: &acp::PlanEntryStatus, + status: &acp_old::PlanEntryStatus, window: &Window, cx: &App, ) -> MarkdownStyle { @@ -2556,7 +2571,7 @@ fn plan_label_markdown_style( MarkdownStyle { base_text_style: TextStyle { color: cx.theme().colors().text_muted, - strikethrough: if matches!(status, acp::PlanEntryStatus::Completed) { + strikethrough: if matches!(status, acp_old::PlanEntryStatus::Completed) { Some(gpui::StrikethroughStyle { thickness: px(1.), color: Some(cx.theme().colors().text_muted.opacity(0.8)),