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 a437176584..f3f558c32e 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 8e9ab428c3..d69d768f33 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 4d8c57dd8f..99c62201fa 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 fcdba2301e..d427290736 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 6f92b958b2..3a1d5c9fa8 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 8e5396590f..7e5d5b48a1 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);