diff --git a/crates/agent_servers/src/acp.rs b/crates/agent_servers/src/acp.rs index f60c8b1519..c3f920d36c 100644 --- a/crates/agent_servers/src/acp.rs +++ b/crates/agent_servers/src/acp.rs @@ -41,9 +41,10 @@ use acp_thread::{AcpThread, AuthRequired, LoadError, TerminalProviderEvent}; use terminal::TerminalBuilder; use terminal::terminal_settings::{AlternateScroll, CursorShape}; -use crate::GEMINI_ID; +use crate::{CURSOR_ID, GEMINI_ID}; pub const GEMINI_TERMINAL_AUTH_METHOD_ID: &str = "spawn-gemini-cli"; +const PARAMETERIZED_MODEL_PICKER_META_KEY: &str = "parameterizedModelPicker"; const MAX_DEBUG_BACKLOG_MESSAGES: usize = 2000; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -735,6 +736,25 @@ fn connect_client_future( ) } +fn client_capabilities_for_agent(agent_id: &AgentId) -> acp::ClientCapabilities { + let mut meta = acp::Meta::from_iter([ + ("terminal_output".into(), true.into()), + ("terminal-auth".into(), true.into()), + ]); + + if agent_id.as_ref() == CURSOR_ID { + meta.insert(PARAMETERIZED_MODEL_PICKER_META_KEY.into(), true.into()); + } + + acp::ClientCapabilities::new() + .fs(acp::FileSystemCapabilities::new() + .read_text_file(true) + .write_text_file(true)) + .terminal(true) + .auth(acp::AuthCapabilities::new().terminal(true)) + .meta(meta) +} + impl AcpConnection { pub fn subscribe_debug_messages( &self, @@ -922,18 +942,7 @@ impl AcpConnection { let initialize_response = connection .send_request( acp::InitializeRequest::new(ProtocolVersion::V1) - .client_capabilities( - acp::ClientCapabilities::new() - .fs(acp::FileSystemCapabilities::new() - .read_text_file(true) - .write_text_file(true)) - .terminal(true) - .auth(acp::AuthCapabilities::new().terminal(true)) - .meta(acp::Meta::from_iter([ - ("terminal_output".into(), true.into()), - ("terminal-auth".into(), true.into()), - ])), - ) + .client_capabilities(client_capabilities_for_agent(&agent_id)) .client_info( acp::Implementation::new("zed", version) .title(release_channel.map(ToOwned::to_owned)), @@ -2457,6 +2466,31 @@ mod tests { use super::*; use settings::Settings as _; + #[test] + fn cursor_client_capabilities_include_parameterized_model_picker_meta() { + let capabilities = client_capabilities_for_agent(&AgentId::new(CURSOR_ID)); + let meta = capabilities + .meta + .expect("expected client capabilities meta"); + + assert_eq!( + meta.get(PARAMETERIZED_MODEL_PICKER_META_KEY), + Some(&serde_json::json!(true)) + ); + assert_eq!(meta.get("terminal_output"), Some(&serde_json::json!(true))); + assert_eq!(meta.get("terminal-auth"), Some(&serde_json::json!(true))); + } + + #[test] + fn non_cursor_client_capabilities_do_not_include_parameterized_model_picker_meta() { + let capabilities = client_capabilities_for_agent(&AgentId::new("codex-acp")); + let meta = capabilities + .meta + .expect("expected client capabilities meta"); + + assert!(!meta.contains_key(PARAMETERIZED_MODEL_PICKER_META_KEY)); + } + #[test] fn terminal_auth_task_builds_spawn_from_prebuilt_command() { let command = AgentServerCommand { diff --git a/crates/agent_servers/src/custom.rs b/crates/agent_servers/src/custom.rs index 1fe92dc9c7..6039d79676 100644 --- a/crates/agent_servers/src/custom.rs +++ b/crates/agent_servers/src/custom.rs @@ -17,6 +17,7 @@ use ui::IconName; pub const GEMINI_ID: &str = "gemini"; pub const CLAUDE_AGENT_ID: &str = "claude-acp"; pub const CODEX_ID: &str = "codex-acp"; +pub const CURSOR_ID: &str = "cursor"; /// A generic agent server implementation for custom user-defined agents pub struct CustomAgentServer {