diff --git a/crates/agent_servers/src/agent_servers.rs b/crates/agent_servers/src/agent_servers.rs index 84980409b91f74fa641520d62b84c498f315f3ed..006739ed0595fa5c6868cbe3db1af8ed95e7c8c8 100644 --- a/crates/agent_servers/src/agent_servers.rs +++ b/crates/agent_servers/src/agent_servers.rs @@ -68,6 +68,20 @@ pub trait AgentServer: Send { ) { } + /// Returns the list of slash commands that should trigger Zed's authentication UI + /// when the user types them (e.g., "/login"). + /// These commands will be intercepted by Zed to show the auth method selection UI. + fn login_commands(&self) -> Vec<&'static str> { + Vec::new() + } + + /// Returns the list of logout-related slash commands that should be sent to the agent + /// to let it reset internal state (e.g., "/logout"). + /// These commands will be added to available_commands and passed through to the agent. + fn logout_commands(&self) -> Vec<&'static str> { + Vec::new() + } + fn connect( &self, root_dir: Option<&Path>, diff --git a/crates/agent_servers/src/claude.rs b/crates/agent_servers/src/claude.rs index 4c939220e074f08f9025a038bac7c5f26f3a07e6..914b43805dea5f5e0c3588b924a81c616904395c 100644 --- a/crates/agent_servers/src/claude.rs +++ b/crates/agent_servers/src/claude.rs @@ -56,6 +56,14 @@ impl AgentServer for ClaudeCode { }); } + fn login_commands(&self) -> Vec<&'static str> { + vec!["login"] + } + + fn logout_commands(&self) -> Vec<&'static str> { + vec!["logout"] + } + fn connect( &self, root_dir: Option<&Path>, diff --git a/crates/agent_servers/src/gemini.rs b/crates/agent_servers/src/gemini.rs index 2cda9f97432a5c48709d19854b9a205dfa7475a6..1fba2cb39df5793500c03944cb2221aabfb105c9 100644 --- a/crates/agent_servers/src/gemini.rs +++ b/crates/agent_servers/src/gemini.rs @@ -25,6 +25,10 @@ impl AgentServer for Gemini { ui::IconName::AiGemini } + fn login_commands(&self) -> Vec<&'static str> { + vec!["login"] + } + fn connect( &self, root_dir: Option<&Path>, diff --git a/crates/agent_ui/src/acp/thread_view.rs b/crates/agent_ui/src/acp/thread_view.rs index fd87942749247fd8093805ccb52a6bec4580baef..c25464e9f3c16f95f7a887c6a64a66d74b0d4618 100644 --- a/crates/agent_ui/src/acp/thread_view.rs +++ b/crates/agent_ui/src/acp/thread_view.rs @@ -1052,20 +1052,36 @@ impl AcpThreadView { let text = self.message_editor.read(cx).text(cx); let text = text.trim(); - if text == "/login" || text == "/logout" { + + // Check if this is a login or logout command + let command_name = text.strip_prefix('/'); + let is_login_command = if let Some(cmd) = command_name { + self.agent.login_commands().contains(&cmd) + } else { + false + }; + let is_logout_command = if let Some(cmd) = command_name { + self.agent.logout_commands().contains(&cmd) + } else { + false + }; + + if is_login_command || is_logout_command { let ThreadState::Ready { thread, .. } = &self.thread_state else { return; }; let connection = thread.read(cx).connection().clone(); let can_login = !connection.auth_methods().is_empty() || !self.auth_commands.is_empty(); + // Does the agent have a specific logout command? Prefer that in case they need to reset internal state. - let logout_supported = text == "/logout" + let logout_supported = is_logout_command && self .available_commands .borrow() .iter() - .any(|command| command.name == "logout"); + .any(|command| command_name == Some(command.name.as_str())); + if can_login && !logout_supported { self.message_editor .update(cx, |editor, cx| editor.clear(window, cx)); @@ -1422,21 +1438,20 @@ impl AcpThreadView { AcpThreadEvent::AvailableCommandsUpdated(available_commands) => { let mut available_commands = available_commands.clone(); - if thread - .read(cx) - .connection() - .auth_methods() - .iter() - .any(|method| method.id.0.as_ref() == "claude-login") - { + // Add login commands from the agent + for command_name in self.agent.login_commands() { available_commands.push(acp::AvailableCommand { - name: "login".to_owned(), + name: command_name.to_owned(), description: "Authenticate".to_owned(), input: None, meta: None, }); + } + + // Add logout commands from the agent + for command_name in self.agent.logout_commands() { available_commands.push(acp::AvailableCommand { - name: "logout".to_owned(), + name: command_name.to_owned(), description: "Authenticate".to_owned(), input: None, meta: None,