Detailed changes
@@ -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"
@@ -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"
@@ -4027,7 +4027,7 @@ mod tests {
}
fn authenticate(&self, method: acp::AuthMethodId, _cx: &mut App) -> Task<gpui::Result<()>> {
- 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")))
@@ -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<Result<()>> {
+ fn close_session(
+ self: Rc<Self>,
+ _session_id: &acp::SessionId,
+ _cx: &mut App,
+ ) -> Task<Result<()>> {
Task::ready(Err(anyhow::Error::msg("Closing sessions is not supported")))
}
@@ -1418,7 +1418,11 @@ impl acp_thread::AgentConnection for NativeAgentConnection {
true
}
- fn close_session(&self, session_id: &acp::SessionId, cx: &mut App) -> Task<Result<()>> {
+ fn close_session(
+ self: Rc<Self>,
+ session_id: &acp::SessionId,
+ cx: &mut App,
+ ) -> Task<Result<()>> {
self.0.update(cx, |agent, _cx| {
let project_id = agent.sessions.get(session_id).map(|s| s.project_id);
agent.sessions.remove(session_id);
@@ -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<Self>,
+ session_id: &acp::SessionId,
+ cx: &mut App,
+ ) -> Task<Result<()>> {
+ 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<acp::KillTerminalCommandResponse, acp::Error> {
+ args: acp::KillTerminalRequest,
+ ) -> Result<acp::KillTerminalResponse, acp::Error> {
self.session_thread(&args.session_id)?
.update(&mut self.cx.clone(), |thread, cx| {
thread.kill_terminal(args.terminal_id, cx)
@@ -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<gpui::Result<()>> {
- if method_id == self.auth_method.id {
+ if &method_id == self.auth_method.id() {
*self.authenticated.lock() = true;
Task::ready(Ok(()))
} else {