From 8d4913168c4ea3ac6a4d6cc1b70d3e7d006d8639 Mon Sep 17 00:00:00 2001 From: Bennet Bo Fenner Date: Wed, 11 Mar 2026 13:52:03 +0100 Subject: [PATCH] acp: Update to `0.10.2` (#51280) Updates to `0.10.2`, most notable change is implementing `session/close`. Release Notes: - N/A --- Cargo.lock | 35 ++++++++++++++++++----- Cargo.toml | 2 +- crates/acp_thread/src/acp_thread.rs | 2 +- crates/acp_thread/src/connection.rs | 6 +++- crates/agent/src/agent.rs | 6 +++- crates/agent_servers/src/acp.rs | 39 +++++++++++++++++++++----- crates/agent_ui/src/connection_view.rs | 26 +++++++++++------ 7 files changed, 89 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f11d2023b319501778768fdea39fb8dbb242a9e9..6fbffbcaff377bdf49d02afae172a19e72a2d188 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,9 +227,9 @@ dependencies = [ [[package]] name = "agent-client-protocol" -version = "0.9.4" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2659b1089101b15db31137710159421cb44785ecdb5ba784be3b4a6f8cb8a475" +checksum = "9c56a59cf6315e99f874d2c1f96c69d2da5ffe0087d211297fc4a41f849770a2" dependencies = [ "agent-client-protocol-schema", "anyhow", @@ -244,16 +244,16 @@ dependencies = [ [[package]] name = "agent-client-protocol-schema" -version = "0.10.8" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bc1fef9c32f03bce2ab44af35b6f483bfd169bf55cc59beeb2e3b1a00ae4d1" +checksum = "e0497b9a95a404e35799904835c57c6f8c69b9d08ccfd3cb5b7d746425cd6789" dependencies = [ "anyhow", "derive_more", "schemars", "serde", "serde_json", - "strum 0.27.2", + "strum 0.28.0", ] [[package]] @@ -7151,7 +7151,7 @@ dependencies = [ "serde", "serde_json", "serde_yaml", - "strum_macros", + "strum_macros 0.27.2", ] [[package]] @@ -16544,7 +16544,16 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros", + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", ] [[package]] @@ -16559,6 +16568,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" diff --git a/Cargo.toml b/Cargo.toml index c184837bfd6a67490169b7a6908b17b4d61e121f..f650dace84b1b2e6491acf2806077f72000605f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -473,7 +473,7 @@ ztracing_macro = { path = "crates/ztracing_macro" } # External crates # -agent-client-protocol = { version = "=0.9.4", features = ["unstable"] } +agent-client-protocol = { version = "=0.10.2", features = ["unstable"] } aho-corasick = "1.1" alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "9d9640d4" } any_vec = "0.14" diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index 58252eaddca553eb1da4c960a829a88afb9eb497..95030443f642b019b27758f53fd413c5146857b1 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -4027,7 +4027,7 @@ mod tests { } fn authenticate(&self, method: acp::AuthMethodId, _cx: &mut App) -> Task> { - if self.auth_methods().iter().any(|m| m.id == method) { + if self.auth_methods().iter().any(|m| m.id() == &method) { Task::ready(Ok(())) } else { Task::ready(Err(anyhow!("Invalid Auth Method"))) diff --git a/crates/acp_thread/src/connection.rs b/crates/acp_thread/src/connection.rs index 1236058226eee840e1a36009df85291a774548dc..4f6aaf86bad68f919c2c5de30214b21ff851c3dd 100644 --- a/crates/acp_thread/src/connection.rs +++ b/crates/acp_thread/src/connection.rs @@ -60,7 +60,11 @@ pub trait AgentConnection { } /// Close an existing session. Allows the agent to free the session from memory. - fn close_session(&self, _session_id: &acp::SessionId, _cx: &mut App) -> Task> { + fn close_session( + self: Rc, + _session_id: &acp::SessionId, + _cx: &mut App, + ) -> Task> { Task::ready(Err(anyhow::Error::msg("Closing sessions is not supported"))) } diff --git a/crates/agent/src/agent.rs b/crates/agent/src/agent.rs index a62e219b2d075e10e074b55859fc6c366c25523d..95346d665732b40599b096d480178264601ce6d6 100644 --- a/crates/agent/src/agent.rs +++ b/crates/agent/src/agent.rs @@ -1418,7 +1418,11 @@ impl acp_thread::AgentConnection for NativeAgentConnection { true } - fn close_session(&self, session_id: &acp::SessionId, cx: &mut App) -> Task> { + fn close_session( + self: Rc, + session_id: &acp::SessionId, + cx: &mut App, + ) -> Task> { self.0.update(cx, |agent, _cx| { let project_id = agent.sessions.get(session_id).map(|s| s.project_id); agent.sessions.remove(session_id); diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index b9e4eba497ef1e01016a17e34d634fea20cab499..a661289f6221818c6f63c799b0593907bb665eb9 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -279,7 +279,7 @@ impl AcpConnection { acp::InitializeRequest::new(acp::ProtocolVersion::V1) .client_capabilities( acp::ClientCapabilities::new() - .fs(acp::FileSystemCapability::new() + .fs(acp::FileSystemCapabilities::new() .read_text_file(true) .write_text_file(true)) .terminal(true) @@ -331,11 +331,11 @@ impl AcpConnection { "env": command.env.clone().unwrap_or_default(), }); let meta = acp::Meta::from_iter([("terminal-auth".to_string(), value)]); - vec![ - acp::AuthMethod::new("spawn-gemini-cli", "Login") + vec![acp::AuthMethod::Agent( + acp::AuthMethodAgent::new("spawn-gemini-cli", "Login") .description("Login with your Google or Vertex AI account") .meta(meta), - ] + )] } else { response.auth_methods }; @@ -744,6 +744,31 @@ impl AgentConnection for AcpConnection { }) } + fn supports_close_session(&self) -> bool { + self.agent_capabilities.session_capabilities.close.is_some() + } + + fn close_session( + self: Rc, + session_id: &acp::SessionId, + cx: &mut App, + ) -> Task> { + if !self.agent_capabilities.session_capabilities.close.is_none() { + return Task::ready(Err(anyhow!(LoadError::Other( + "Closing sessions is not supported by this agent.".into() + )))); + } + + let conn = self.connection.clone(); + let session_id = session_id.clone(); + cx.foreground_executor().spawn(async move { + conn.close_session(acp::CloseSessionRequest::new(session_id.clone())) + .await?; + self.sessions.borrow_mut().remove(&session_id); + Ok(()) + }) + } + fn auth_methods(&self) -> &[acp::AuthMethod] { &self.auth_methods } @@ -1373,10 +1398,10 @@ impl acp::Client for ClientDelegate { Ok(acp::CreateTerminalResponse::new(terminal_id)) } - async fn kill_terminal_command( + async fn kill_terminal( &self, - args: acp::KillTerminalCommandRequest, - ) -> Result { + args: acp::KillTerminalRequest, + ) -> Result { self.session_thread(&args.session_id)? .update(&mut self.cx.clone(), |thread, cx| { thread.kill_terminal(args.terminal_id, cx) diff --git a/crates/agent_ui/src/connection_view.rs b/crates/agent_ui/src/connection_view.rs index 3f1f1fb164693e0bb9e0b6d8883b97ab5539ba4f..2fd86f9c9d91abb7d5b08bd7a779b93592f2011c 100644 --- a/crates/agent_ui/src/connection_view.rs +++ b/crates/agent_ui/src/connection_view.rs @@ -463,7 +463,7 @@ impl ConnectedServerState { let tasks = self .threads .keys() - .map(|id| self.connection.close_session(id, cx)); + .map(|id| self.connection.clone().close_session(id, cx)); let task = futures::future::join_all(tasks); cx.background_spawn(async move { task.await; @@ -1431,7 +1431,7 @@ impl ConnectionView { .connection() .auth_methods() .iter() - .any(|method| method.id.0.as_ref() == "claude-login") + .any(|method| method.id().0.as_ref() == "claude-login") { available_commands.push(acp::AvailableCommand::new("login", "Authenticate")); available_commands.push(acp::AvailableCommand::new("logout", "Authenticate")); @@ -1495,10 +1495,15 @@ impl ConnectionView { let agent_telemetry_id = connection.telemetry_id(); // Check for the experimental "terminal-auth" _meta field - let auth_method = connection.auth_methods().iter().find(|m| m.id == method); + let auth_method = connection.auth_methods().iter().find(|m| m.id() == &method); if let Some(terminal_auth) = auth_method - .and_then(|a| a.meta.as_ref()) + .and_then(|a| match a { + acp::AuthMethod::EnvVar(env_var) => env_var.meta.as_ref(), + acp::AuthMethod::Terminal(terminal) => terminal.meta.as_ref(), + acp::AuthMethod::Agent(agent) => agent.meta.as_ref(), + _ => None, + }) .and_then(|m| m.get("terminal-auth")) { // Extract terminal auth details from meta @@ -1882,7 +1887,7 @@ impl ConnectionView { .enumerate() .rev() .map(|(ix, method)| { - let (method_id, name) = (method.id.0.clone(), method.name.clone()); + let (method_id, name) = (method.id().0.clone(), method.name().to_string()); let agent_telemetry_id = connection.telemetry_id(); Button::new(method_id.clone(), name) @@ -1894,8 +1899,8 @@ impl ConnectionView { this.style(ButtonStyle::Outlined) } }) - .when_some(method.description.clone(), |this, description| { - this.tooltip(Tooltip::text(description)) + .when_some(method.description(), |this, description| { + this.tooltip(Tooltip::text(description.to_string())) }) .on_click({ cx.listener(move |this, _, window, cx| { @@ -4074,7 +4079,10 @@ pub(crate) mod tests { fn new() -> Self { Self { authenticated: Arc::new(Mutex::new(false)), - auth_method: acp::AuthMethod::new(Self::AUTH_METHOD_ID, "Test Login"), + auth_method: acp::AuthMethod::Agent(acp::AuthMethodAgent::new( + Self::AUTH_METHOD_ID, + "Test Login", + )), } } } @@ -4127,7 +4135,7 @@ pub(crate) mod tests { method_id: acp::AuthMethodId, _cx: &mut App, ) -> Task> { - if method_id == self.auth_method.id { + if &method_id == self.auth_method.id() { *self.authenticated.lock() = true; Task::ready(Ok(())) } else {