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
This commit is contained in:
Ben Brandt 2025-10-27 11:05:50 +01:00 committed by GitHub
parent ae3abf50d8
commit 7cb2d83608
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 111 additions and 64 deletions

9
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -1105,13 +1105,13 @@ impl AcpThread {
cx: &mut Context<Self>,
) -> 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();

View file

@ -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

View file

@ -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(&notification.session_id)
.context("Failed to get session")?;
if let acp::SessionUpdate::CurrentModeUpdate { current_mode_id } = &notification.update {
if let acp::SessionUpdate::CurrentModeUpdate(acp::CurrentModeUpdate {
current_mode_id,
..
}) = &notification.update
{
if let Some(session_modes) = &session.session_modes {
session_modes.borrow_mut().current_mode_id = current_mode_id.clone();
} else {

View file

@ -5981,9 +5981,12 @@ pub(crate) mod tests {
impl StubAgentServer<StubAgentConnection> {
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);