agent_servers.rs

  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, HashSet};
 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 connect(
 60        &self,
 61        root_dir: Option<&Path>,
 62        delegate: AgentServerDelegate,
 63        cx: &mut App,
 64    ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>>;
 65
 66    fn into_any(self: Rc<Self>) -> Rc<dyn Any>;
 67
 68    fn default_mode(&self, _cx: &App) -> Option<agent_client_protocol::SessionModeId> {
 69        None
 70    }
 71
 72    fn set_default_mode(
 73        &self,
 74        _mode_id: Option<agent_client_protocol::SessionModeId>,
 75        _fs: Arc<dyn Fs>,
 76        _cx: &mut App,
 77    ) {
 78    }
 79
 80    fn default_model(&self, _cx: &App) -> Option<agent_client_protocol::ModelId> {
 81        None
 82    }
 83
 84    fn set_default_model(
 85        &self,
 86        _model_id: Option<agent_client_protocol::ModelId>,
 87        _fs: Arc<dyn Fs>,
 88        _cx: &mut App,
 89    ) {
 90    }
 91
 92    fn favorite_model_ids(&self, _cx: &mut App) -> HashSet<agent_client_protocol::ModelId> {
 93        HashSet::default()
 94    }
 95
 96    fn default_config_option(&self, _config_id: &str, _cx: &App) -> Option<String> {
 97        None
 98    }
 99
100    fn set_default_config_option(
101        &self,
102        _config_id: &str,
103        _value_id: Option<&str>,
104        _fs: Arc<dyn Fs>,
105        _cx: &mut App,
106    ) {
107    }
108
109    fn favorite_config_option_value_ids(
110        &self,
111        _config_id: &agent_client_protocol::SessionConfigId,
112        _cx: &mut App,
113    ) -> HashSet<agent_client_protocol::SessionConfigValueId> {
114        HashSet::default()
115    }
116
117    fn toggle_favorite_config_option_value(
118        &self,
119        _config_id: agent_client_protocol::SessionConfigId,
120        _value_id: agent_client_protocol::SessionConfigValueId,
121        _should_be_favorite: bool,
122        _fs: Arc<dyn Fs>,
123        _cx: &App,
124    ) {
125    }
126
127    fn toggle_favorite_model(
128        &self,
129        _model_id: agent_client_protocol::ModelId,
130        _should_be_favorite: bool,
131        _fs: Arc<dyn Fs>,
132        _cx: &App,
133    ) {
134    }
135}
136
137impl dyn AgentServer {
138    pub fn downcast<T: 'static + AgentServer + Sized>(self: Rc<Self>) -> Option<Rc<T>> {
139        self.into_any().downcast().ok()
140    }
141}
142
143/// Load the default proxy environment variables to pass through to the agent
144pub fn load_proxy_env(cx: &mut App) -> HashMap<String, String> {
145    let proxy_url = cx
146        .read_global(|settings: &SettingsStore, _| settings.get::<ProxySettings>(None).proxy_url());
147    let mut env = HashMap::default();
148
149    if let Some(proxy_url) = &proxy_url {
150        let env_var = if proxy_url.scheme() == "https" {
151            "HTTPS_PROXY"
152        } else {
153            "HTTP_PROXY"
154        };
155        env.insert(env_var.to_owned(), proxy_url.to_string());
156    }
157
158    if let Some(no_proxy) = read_no_proxy_from_env() {
159        env.insert("NO_PROXY".to_owned(), no_proxy);
160    } else if proxy_url.is_some() {
161        // We sometimes need local MCP servers that we don't want to proxy
162        env.insert("NO_PROXY".to_owned(), "localhost,127.0.0.1".to_owned());
163    }
164
165    env
166}