From 7cb2d836088a1c55c51eff38774c901cfc0e1f73 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Mon, 27 Oct 2025 11:05:50 +0100 Subject: [PATCH] acp: Start sending Client Info to the Agent (#41265) Updates to acp crate 0.7, which allows us to send information about the client to the Agent. In the future, we can also use the AgentInfo on the response for internal metrics. Release Notes: - N/A --- Cargo.lock | 9 ++- Cargo.toml | 2 +- crates/acp_thread/src/acp_thread.rs | 40 ++++++---- crates/agent_servers/Cargo.toml | 1 + crates/agent_servers/src/acp.rs | 19 ++++- crates/agent_ui/src/acp/thread_view.rs | 104 +++++++++++++++---------- 6 files changed, 111 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a43717658477683e7a8a5c2409abbf15749938a3..f3f558c32ea849e3d0433471fb528959ca58635d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,9 +210,9 @@ dependencies = [ [[package]] name = "agent-client-protocol" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f655394a107cd601bd2e5375c2d909ea83adc65678a0e0e8d77613d3c848a7d" +checksum = "525705e39c11cd73f7bc784e3681a9386aa30c8d0630808d3dc2237eb4f9cb1b" dependencies = [ "agent-client-protocol-schema", "anyhow", @@ -228,9 +228,9 @@ dependencies = [ [[package]] name = "agent-client-protocol-schema" -version = "0.4.11" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61be4454304d7df1a5b44c4ae55e707ffe72eac4dfb1ef8762510ce8d8f6d924" +checksum = "ecf16c18fea41282d6bbadd1549a06be6836bddb1893f44a6235f340fa24e2af" dependencies = [ "anyhow", "derive_more 2.0.1", @@ -266,6 +266,7 @@ dependencies = [ "log", "nix 0.29.0", "project", + "release_channel", "reqwest_client", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 8e9ab428c316954974d69eabebeb417231b0edd5..d69d768f33eb59e9f2c6e3194a976e612d5944ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -438,7 +438,7 @@ zlog_settings = { path = "crates/zlog_settings" } # External crates # -agent-client-protocol = { version = "0.5.0", features = ["unstable"] } +agent-client-protocol = { version = "0.7.0", features = ["unstable"] } aho-corasick = "1.1" alacritty_terminal = "0.25.1-rc1" any_vec = "0.14" diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index 4d8c57dd8f5a97aabc5cf3dc9e8d5aae9d6c8f2f..99c62201fa0c2576e588c5cc7325d525c2d03503 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -1105,13 +1105,13 @@ impl AcpThread { cx: &mut Context, ) -> Result<(), acp::Error> { match update { - acp::SessionUpdate::UserMessageChunk { content } => { + acp::SessionUpdate::UserMessageChunk(acp::ContentChunk { content, .. }) => { self.push_user_content_block(None, content, cx); } - acp::SessionUpdate::AgentMessageChunk { content } => { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content, .. }) => { self.push_assistant_content_block(content, false, cx); } - acp::SessionUpdate::AgentThoughtChunk { content } => { + acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk { content, .. }) => { self.push_assistant_content_block(content, true, cx); } acp::SessionUpdate::ToolCall(tool_call) => { @@ -1123,12 +1123,14 @@ impl AcpThread { acp::SessionUpdate::Plan(plan) => { self.update_plan(plan, cx); } - acp::SessionUpdate::AvailableCommandsUpdate { available_commands } => { - cx.emit(AcpThreadEvent::AvailableCommandsUpdated(available_commands)) - } - acp::SessionUpdate::CurrentModeUpdate { current_mode_id } => { - cx.emit(AcpThreadEvent::ModeUpdated(current_mode_id)) - } + acp::SessionUpdate::AvailableCommandsUpdate(acp::AvailableCommandsUpdate { + available_commands, + .. + }) => cx.emit(AcpThreadEvent::AvailableCommandsUpdated(available_commands)), + acp::SessionUpdate::CurrentModeUpdate(acp::CurrentModeUpdate { + current_mode_id, + .. + }) => cx.emit(AcpThreadEvent::ModeUpdated(current_mode_id)), } Ok(()) } @@ -2586,17 +2588,19 @@ mod tests { thread.update(&mut cx, |thread, cx| { thread .handle_session_update( - acp::SessionUpdate::AgentThoughtChunk { + acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk { content: "Thinking ".into(), - }, + meta: None, + }), cx, ) .unwrap(); thread .handle_session_update( - acp::SessionUpdate::AgentThoughtChunk { + acp::SessionUpdate::AgentThoughtChunk(acp::ContentChunk { content: "hard!".into(), - }, + meta: None, + }), cx, ) .unwrap(); @@ -3095,9 +3099,10 @@ mod tests { thread.update(&mut cx, |thread, cx| { thread .handle_session_update( - acp::SessionUpdate::AgentMessageChunk { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content: content.text.to_uppercase().into(), - }, + meta: None, + }), cx, ) .unwrap(); @@ -3454,9 +3459,10 @@ mod tests { thread.update(&mut cx, |thread, cx| { thread .handle_session_update( - acp::SessionUpdate::AgentMessageChunk { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content: content.text.to_uppercase().into(), - }, + meta: None, + }), cx, ) .unwrap(); diff --git a/crates/agent_servers/Cargo.toml b/crates/agent_servers/Cargo.toml index fcdba2301ee21254832a81899cff0bc9753e92f2..d427290736f140e51cc9d9fc7285aa71fd9b2548 100644 --- a/crates/agent_servers/Cargo.toml +++ b/crates/agent_servers/Cargo.toml @@ -38,6 +38,7 @@ language_model.workspace = true language_models.workspace = true log.workspace = true project.workspace = true +release_channel.workspace = true reqwest_client = { workspace = true, optional = true } serde.workspace = true serde_json.workspace = true diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index 6f92b958b2d94e48539e34b6a58b4789ea376fb5..3a1d5c9fa84b108108542da385286f5bdb88005d 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -105,6 +105,14 @@ impl AcpConnection { let sessions = Rc::new(RefCell::new(HashMap::default())); + let (release_channel, version) = cx.update(|cx| { + ( + release_channel::ReleaseChannel::try_global(cx) + .map(|release_channel| release_channel.display_name()), + release_channel::AppVersion::global(cx).to_string(), + ) + })?; + let client = ClientDelegate { sessions: sessions.clone(), cx: cx.clone(), @@ -172,6 +180,11 @@ impl AcpConnection { "terminal_output": true, })), }, + client_info: Some(acp::Implementation { + name: "zed".to_owned(), + title: release_channel.map(|c| c.to_owned()), + version, + }), meta: None, }) .await?; @@ -700,7 +713,11 @@ impl acp::Client for ClientDelegate { .get(¬ification.session_id) .context("Failed to get session")?; - if let acp::SessionUpdate::CurrentModeUpdate { current_mode_id } = ¬ification.update { + if let acp::SessionUpdate::CurrentModeUpdate(acp::CurrentModeUpdate { + current_mode_id, + .. + }) = ¬ification.update + { if let Some(session_modes) = &session.session_modes { session_modes.borrow_mut().current_mode_id = current_mode_id.clone(); } else { diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index 8e5396590fe0170b536075bff210c859435a4b3c..7e5d5b48a13adb7c3133245cd520f7b48c46517a 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -5981,9 +5981,12 @@ pub(crate) mod tests { impl StubAgentServer { fn default_response() -> Self { let conn = StubAgentConnection::new(); - conn.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk { - content: "Default response".into(), - }]); + conn.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk( + acp::ContentChunk { + content: "Default response".into(), + meta: None, + }, + )]); Self::new(conn) } } @@ -6334,13 +6337,16 @@ pub(crate) mod tests { let connection = StubAgentConnection::new(); - connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk { - content: acp::ContentBlock::Text(acp::TextContent { - text: "Response".into(), - annotations: None, + connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk( + acp::ContentChunk { + content: acp::ContentBlock::Text(acp::TextContent { + text: "Response".into(), + annotations: None, + meta: None, + }), meta: None, - }), - }]); + }, + )]); let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; add_to_workspace(thread_view.clone(), cx); @@ -6424,13 +6430,16 @@ pub(crate) mod tests { let connection = StubAgentConnection::new(); - connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk { - content: acp::ContentBlock::Text(acp::TextContent { - text: "Response".into(), - annotations: None, + connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk( + acp::ContentChunk { + content: acp::ContentBlock::Text(acp::TextContent { + text: "Response".into(), + annotations: None, + meta: None, + }), meta: None, - }), - }]); + }, + )]); let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection.clone()), cx).await; @@ -6468,13 +6477,16 @@ pub(crate) mod tests { }); // Send - connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk { - content: acp::ContentBlock::Text(acp::TextContent { - text: "New Response".into(), - annotations: None, + connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk( + acp::ContentChunk { + content: acp::ContentBlock::Text(acp::TextContent { + text: "New Response".into(), + annotations: None, + meta: None, + }), meta: None, - }), - }]); + }, + )]); user_message_editor.update_in(cx, |_editor, window, cx| { window.dispatch_action(Box::new(Chat), cx); @@ -6561,13 +6573,14 @@ pub(crate) mod tests { cx.update(|_, cx| { connection.send_update( session_id.clone(), - acp::SessionUpdate::AgentMessageChunk { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content: acp::ContentBlock::Text(acp::TextContent { text: "Response".into(), annotations: None, meta: None, }), - }, + meta: None, + }), cx, ); connection.end_turn(session_id, acp::StopReason::EndTurn); @@ -6619,9 +6632,10 @@ pub(crate) mod tests { cx.update(|_, cx| { connection.send_update( session_id.clone(), - acp::SessionUpdate::AgentMessageChunk { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content: "Message 1 resp".into(), - }, + meta: None, + }), cx, ); }); @@ -6655,9 +6669,10 @@ pub(crate) mod tests { // Simulate a response sent after beginning to cancel connection.send_update( session_id.clone(), - acp::SessionUpdate::AgentMessageChunk { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content: "onse".into(), - }, + meta: None, + }), cx, ); }); @@ -6688,9 +6703,10 @@ pub(crate) mod tests { cx.update(|_, cx| { connection.send_update( session_id.clone(), - acp::SessionUpdate::AgentMessageChunk { + acp::SessionUpdate::AgentMessageChunk(acp::ContentChunk { content: "Message 2 response".into(), - }, + meta: None, + }), cx, ); connection.end_turn(session_id.clone(), acp::StopReason::EndTurn); @@ -6728,13 +6744,16 @@ pub(crate) mod tests { init_test(cx); let connection = StubAgentConnection::new(); - connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk { - content: acp::ContentBlock::Text(acp::TextContent { - text: "Response".into(), - annotations: None, + connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk( + acp::ContentChunk { + content: acp::ContentBlock::Text(acp::TextContent { + text: "Response".into(), + annotations: None, + meta: None, + }), meta: None, - }), - }]); + }, + )]); let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; add_to_workspace(thread_view.clone(), cx); @@ -6811,13 +6830,16 @@ pub(crate) mod tests { init_test(cx); let connection = StubAgentConnection::new(); - connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk { - content: acp::ContentBlock::Text(acp::TextContent { - text: "Response".into(), - annotations: None, + connection.set_next_prompt_updates(vec![acp::SessionUpdate::AgentMessageChunk( + acp::ContentChunk { + content: acp::ContentBlock::Text(acp::TextContent { + text: "Response".into(), + annotations: None, + meta: None, + }), meta: None, - }), - }]); + }, + )]); let (thread_view, cx) = setup_thread_view(StubAgentServer::new(connection), cx).await; add_to_workspace(thread_view.clone(), cx);