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