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