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