acp: Update agent-client-protocol SDK to 2.0.0 (#61570)

This isn't ACP v2, but just a new version of the SDK where we had to do
some breaking changes + cleanups.

Release Notes:

- N/A
This commit is contained in:
Ben Brandt authored and GitHub committed 2026-07-24 11:22:33 +00:00
1 parent c9142fae21
commit af57f1732c
3 files changed
+124 -33

No files matched your search

Generated
+18 -7
View File
@@ -323,9 +323,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
version = "1.3.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d386d6a58f4dbe0ebfa98e915caa54005dabdefd419de3d4678b28cd5752444"
checksum = "6d87bc7769eba641753ba5dc52f73ec3765d51022c6753bf040967125ddc86a8"
dependencies = [
"agent-client-protocol-derive",
"agent-client-protocol-schema",
@@ -347,19 +347,19 @@ dependencies = [
[[package]]
name = "agent-client-protocol-derive"
version = "1.3.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b94934d118a69e921d14e94d4af5b3076563318c52662c89a0ecb3f139e1da"
checksum = "3abd4080f51e4f24f5042beb7fb7a66ede29a2dc1c2582c329532e1c27264ddc"
dependencies = [
"quote",
"syn 2.0.117",
"syn 3.0.3",
]
[[package]]
name = "agent-client-protocol-schema"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06679e1542356341f4550ccfb16338b64f37f6af70de2105446ef6fbb078234c"
checksum = "d5c231915b4ab578c722eca2d1bd7df4d300bfd6cac3b8e9f0d1e3ddc95b187c"
dependencies = [
"anyhow",
"derive_more",
@@ -18156,6 +18156,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.2"
+1 -1
View File
@@ -514,7 +514,7 @@ accesskit = { version = "0.24.0", features = ["enumn"] }
accesskit_macos = "0.26.0"
accesskit_unix = "0.21.0"
accesskit_windows = "0.33.1"
agent-client-protocol = { version = "=1.3.0", features = ["unstable"] }
agent-client-protocol = { version = "=2.0.0", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "4c129667ce56611becdc82de6e28218c80e2e88f" }
any_vec = "0.14"
+105 -25
View File
@@ -81,17 +81,30 @@ pub struct AcpDebugMessage {
}
impl AcpDebugMessage {
fn parse(direction: AcpDebugMessageDirection, line: &str) -> Option<Self> {
fn parse_line(direction: AcpDebugMessageDirection, line: &str) -> Vec<Self> {
if direction == AcpDebugMessageDirection::Stderr {
return Some(Self {
return vec![Self {
direction,
message: AcpDebugMessageContent::Stderr {
line: Arc::from(line),
},
});
}];
}
let value: serde_json::Value = serde_json::from_str(line).ok()?;
let Ok(value) = serde_json::from_str(line) else {
return Vec::new();
};
match value {
serde_json::Value::Array(entries) => entries
.into_iter()
.filter_map(|entry| Self::parse_value(direction, entry))
.collect(),
value => Self::parse_value(direction, value).into_iter().collect(),
}
}
fn parse_value(direction: AcpDebugMessageDirection, value: serde_json::Value) -> Option<Self> {
let object = value.as_object()?;
let parsed_id = object
@@ -178,26 +191,29 @@ impl AcpDebugLog {
}
fn record_line(&self, direction: AcpDebugMessageDirection, line: &str) {
let Some(message) = AcpDebugMessage::parse(direction, line) else {
let messages = AcpDebugMessage::parse_line(direction, line);
if messages.is_empty() {
return;
};
self.record_message(message);
}
self.record_messages(messages);
}
fn record_message(&self, message: AcpDebugMessage) {
fn record_messages(&self, messages: Vec<AcpDebugMessage>) {
let mut state = self
.state
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
if state.messages.len() == MAX_DEBUG_BACKLOG_MESSAGES {
state.messages.pop_front();
}
state.messages.push_back(message.clone());
state.subscribers.retain(|sender| !sender.is_closed());
for sender in &state.subscribers {
sender.try_send(message.clone()).log_err();
for message in messages {
if state.messages.len() == MAX_DEBUG_BACKLOG_MESSAGES {
state.messages.pop_front();
}
state.messages.push_back(message.clone());
for sender in &state.subscribers {
sender.try_send(message.clone()).log_err();
}
}
}
@@ -322,7 +338,6 @@ where
Notif: Send + 'static,
{
notification: Notif,
connection: ConnectionTo<Agent>,
handler: fn(Notif, &mut AsyncApp, &ClientContext),
}
@@ -334,17 +349,12 @@ where
let Self {
notification,
handler,
..
} = *self;
handler(notification, cx, ctx);
}
fn reject(self: Box<Self>) {
let Self { connection, .. } = *self;
log::error!("ACP foreground dispatch queue closed while handling inbound notification");
connection
.send_error_notification(dispatch_queue_closed_error())
.log_err();
}
}
@@ -370,14 +380,12 @@ fn enqueue_request<Req, Res>(
fn enqueue_notification<Notif>(
dispatch_tx: &mpsc::UnboundedSender<ForegroundWork>,
notification: Notif,
connection: ConnectionTo<Agent>,
handler: fn(Notif, &mut AsyncApp, &ClientContext),
) where
Notif: Send + 'static,
{
let work: ForegroundWork = Box::new(NotificationForegroundWork {
notification,
connection,
handler,
});
if let Err(err) = dispatch_tx.unbounded_send(work) {
@@ -685,8 +693,8 @@ fn connect_client_future(
macro_rules! on_notification {
($handler:ident) => {{
let dispatch_tx = dispatch_tx.clone();
async move |notif, connection| {
enqueue_notification(&dispatch_tx, notif, connection, $handler);
async move |notif, _connection| {
enqueue_notification(&dispatch_tx, notif, $handler);
Ok(())
}
}};
@@ -3150,6 +3158,78 @@ mod tests {
);
}
#[test]
fn debug_log_records_each_json_rpc_batch_entry() {
let debug_log = AcpDebugLog::default();
debug_log.record_line(
AcpDebugMessageDirection::Incoming,
r#"{"jsonrpc":"2.0","method":"legacy/update"}"#,
);
debug_log.record_line(
AcpDebugMessageDirection::Incoming,
r#"[
{"jsonrpc":"2.0","method":"session/update","params":{"value":1}},
null,
[{"jsonrpc":"2.0","method":"nested/update"}],
{"jsonrpc":"2.0","id":1,"method":"session/one","params":{"value":2}},
{"jsonrpc":"2.0","id":{"invalid":true},"method":"invalid/id"}
]"#,
);
debug_log.record_line(
AcpDebugMessageDirection::Outgoing,
r#"[
{"jsonrpc":"2.0","id":1,"result":{"accepted":true}},
{"jsonrpc":"2.0","id":null,"error":{"code":-32600,"message":"Invalid Request"}}
]"#,
);
let (messages, _receiver) = debug_log.subscribe();
let mut messages = messages.iter();
assert!(matches!(
messages.next(),
Some(AcpDebugMessage {
direction: AcpDebugMessageDirection::Incoming,
message: AcpDebugMessageContent::Notification { method, .. },
}) if method.as_ref() == "legacy/update"
));
assert!(matches!(
messages.next(),
Some(AcpDebugMessage {
direction: AcpDebugMessageDirection::Incoming,
message: AcpDebugMessageContent::Notification { method, .. },
}) if method.as_ref() == "session/update"
));
assert!(matches!(
messages.next(),
Some(AcpDebugMessage {
direction: AcpDebugMessageDirection::Incoming,
message: AcpDebugMessageContent::Request { id, method, .. },
}) if id == &acp::RequestId::Number(1) && method.as_ref() == "session/one"
));
assert!(matches!(
messages.next(),
Some(AcpDebugMessage {
direction: AcpDebugMessageDirection::Outgoing,
message: AcpDebugMessageContent::Response {
id,
result: Ok(Some(_)),
},
}) if id == &acp::RequestId::Number(1)
));
assert!(matches!(
messages.next(),
Some(AcpDebugMessage {
direction: AcpDebugMessageDirection::Outgoing,
message: AcpDebugMessageContent::Response {
id,
result: Err(_),
},
}) if id == &acp::RequestId::Null
));
assert!(messages.next().is_none());
}
#[test]
fn session_directories_use_ordered_paths_when_supported() {
let work_dirs = PathList::new(&[