agent_servers.rs

  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}