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