1mod acp;
2mod claude;
3mod codex;
4mod custom;
5mod gemini;
6
7#[cfg(any(test, feature = "test-support"))]
8pub mod e2e_tests;
9
10pub use claude::*;
11use client::ProxySettings;
12pub use codex::*;
13use collections::HashMap;
14pub use custom::*;
15use fs::Fs;
16pub use gemini::*;
17use http_client::read_no_proxy_from_env;
18use project::agent_server_store::AgentServerStore;
19
20use acp_thread::AgentConnection;
21use anyhow::Result;
22use gpui::{App, AppContext, Entity, SharedString, Task};
23use project::Project;
24use settings::SettingsStore;
25use std::{any::Any, path::Path, rc::Rc, sync::Arc};
26
27pub use acp::AcpConnection;
28
29pub struct AgentServerDelegate {
30 store: Entity<AgentServerStore>,
31 project: Entity<Project>,
32 status_tx: Option<watch::Sender<SharedString>>,
33 new_version_available: Option<watch::Sender<Option<String>>>,
34}
35
36impl AgentServerDelegate {
37 pub fn new(
38 store: Entity<AgentServerStore>,
39 project: Entity<Project>,
40 status_tx: Option<watch::Sender<SharedString>>,
41 new_version_tx: Option<watch::Sender<Option<String>>>,
42 ) -> Self {
43 Self {
44 store,
45 project,
46 status_tx,
47 new_version_available: new_version_tx,
48 }
49 }
50
51 pub fn project(&self) -> &Entity<Project> {
52 &self.project
53 }
54}
55
56pub trait AgentServer: Send {
57 fn logo(&self) -> ui::IconName;
58 fn name(&self) -> SharedString;
59 fn telemetry_id(&self) -> &'static str;
60 fn default_mode(&self, _cx: &mut App) -> Option<agent_client_protocol::SessionModeId> {
61 None
62 }
63 fn set_default_mode(
64 &self,
65 _mode_id: Option<agent_client_protocol::SessionModeId>,
66 _fs: Arc<dyn Fs>,
67 _cx: &mut App,
68 ) {
69 }
70
71 fn connect(
72 &self,
73 root_dir: Option<&Path>,
74 delegate: AgentServerDelegate,
75 cx: &mut App,
76 ) -> Task<
77 Result<(
78 Rc<dyn AgentConnection>,
79 HashMap<String, task::SpawnInTerminal>,
80 )>,
81 >;
82
83 fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
84}
85
86impl dyn AgentServer {
87 pub fn downcast<T: 'static + AgentServer + Sized>(self: Rc<Self>) -> Option<Rc<T>> {
88 self.into_any().downcast().ok()
89 }
90}
91
92/// Extension trait for ACP-specific agent capabilities.
93/// This trait is only implemented by agents that use the Agent Client Protocol (ACP).
94pub trait AcpAgentServer: AgentServer {
95 /// Returns the list of slash commands that should trigger Zed's authentication UI
96 /// when running locally (e.g., "/login").
97 /// These commands will be intercepted by Zed to show the auth method selection UI.
98 fn local_login_commands(&self) -> Vec<String>;
99
100 /// Returns the list of slash commands that should trigger Zed's authentication UI
101 /// when running remotely (e.g., "/login").
102 /// These commands will be intercepted by Zed to show the auth method selection UI.
103 fn remote_login_commands(&self) -> Vec<String>;
104
105 /// Returns the list of logout-related slash commands that should be sent to the agent
106 /// when running locally to let it reset internal state (e.g., "/logout").
107 /// These commands will be added to available_commands and passed through to the agent.
108 fn local_logout_commands(&self) -> Vec<String>;
109
110 /// Returns the list of logout-related slash commands that should be sent to the agent
111 /// when running remotely to let it reset internal state (e.g., "/logout").
112 /// These commands will be added to available_commands and passed through to the agent.
113 fn remote_logout_commands(&self) -> Vec<String>;
114}
115
116/// Load the default proxy environment variables to pass through to the agent
117pub fn load_proxy_env(cx: &mut App) -> HashMap<String, String> {
118 let proxy_url = cx
119 .read_global(|settings: &SettingsStore, _| settings.get::<ProxySettings>(None).proxy_url());
120 let mut env = HashMap::default();
121
122 if let Some(proxy_url) = &proxy_url {
123 let env_var = if proxy_url.scheme() == "https" {
124 "HTTPS_PROXY"
125 } else {
126 "HTTP_PROXY"
127 };
128 env.insert(env_var.to_owned(), proxy_url.to_string());
129 }
130
131 if let Some(no_proxy) = read_no_proxy_from_env() {
132 env.insert("NO_PROXY".to_owned(), no_proxy);
133 } else if proxy_url.is_some() {
134 // We sometimes need local MCP servers that we don't want to proxy
135 env.insert("NO_PROXY".to_owned(), "localhost,127.0.0.1".to_owned());
136 }
137
138 env
139}