agent_server: Remove root_dir from agent server connect APIs (#50093)

Ben Brandt created

This isn't necessary and allows us to potentially share processes across
threads.

Release Notes:

- N/A

Change summary

crates/agent/src/native_agent_server.rs                   |   8 
crates/agent/src/thread_store.rs                          |  29 --
crates/agent_servers/src/acp.rs                           |  15 -
crates/agent_servers/src/agent_servers.rs                 |   3 
crates/agent_servers/src/claude.rs                        |   9 
crates/agent_servers/src/codex.rs                         |  10 
crates/agent_servers/src/custom.rs                        |  10 
crates/agent_servers/src/e2e_tests.rs                     |   5 
crates/agent_servers/src/gemini.rs                        |  10 
crates/agent_ui/src/acp/thread_view.rs                    |  15 -
crates/agent_ui/src/mention_set.rs                        |   2 
crates/project/src/agent_server_store.rs                  | 107 ++------
crates/project/tests/integration/ext_agent_tests.rs       |   4 
crates/project/tests/integration/extension_agent_tests.rs |   4 
crates/remote_server/src/remote_editing_tests.rs          |   4 
crates/zed/src/visual_test_runner.rs                      |   1 
16 files changed, 46 insertions(+), 190 deletions(-)

Detailed changes

crates/agent/src/native_agent_server.rs πŸ”—

@@ -1,4 +1,4 @@
-use std::{any::Any, path::Path, rc::Rc, sync::Arc};
+use std::{any::Any, rc::Rc, sync::Arc};
 
 use agent_client_protocol as acp;
 use agent_servers::{AgentServer, AgentServerDelegate};
@@ -35,7 +35,6 @@ impl AgentServer for NativeAgentServer {
 
     fn connect(
         &self,
-        _root_dir: Option<&Path>,
         delegate: AgentServerDelegate,
         cx: &mut App,
     ) -> Task<
@@ -44,10 +43,7 @@ impl AgentServer for NativeAgentServer {
             Option<task::SpawnInTerminal>,
         )>,
     > {
-        log::debug!(
-            "NativeAgentServer::connect called for path: {:?}",
-            _root_dir
-        );
+        log::debug!("NativeAgentServer::connect");
         let project = delegate.project().clone();
         let fs = self.fs.clone();
         let thread_store = self.thread_store.clone();

crates/agent/src/thread_store.rs πŸ”—

@@ -2,40 +2,11 @@ use crate::{DbThread, DbThreadMetadata, ThreadsDatabase};
 use agent_client_protocol as acp;
 use anyhow::{Result, anyhow};
 use gpui::{App, Context, Entity, Global, Task, prelude::*};
-use project::Project;
-use std::rc::Rc;
 
 struct GlobalThreadStore(Entity<ThreadStore>);
 
 impl Global for GlobalThreadStore {}
 
-// TODO: Remove once ACP thread loading is fully handled elsewhere.
-pub fn load_agent_thread(
-    session_id: acp::SessionId,
-    thread_store: Entity<ThreadStore>,
-    project: Entity<Project>,
-    cx: &mut App,
-) -> Task<Result<Entity<crate::Thread>>> {
-    use agent_servers::{AgentServer, AgentServerDelegate};
-
-    let server = Rc::new(crate::NativeAgentServer::new(
-        project.read(cx).fs().clone(),
-        thread_store,
-    ));
-    let delegate = AgentServerDelegate::new(
-        project.read(cx).agent_server_store().clone(),
-        project.clone(),
-        None,
-        None,
-    );
-    let connection = server.connect(None, delegate, cx);
-    cx.spawn(async move |cx| {
-        let (agent, _) = connection.await?;
-        let agent = agent.downcast::<crate::NativeAgentConnection>().unwrap();
-        cx.update(|cx| agent.load_thread(session_id, cx)).await
-    })
-}
-
 pub struct ThreadStore {
     threads: Vec<DbThreadMetadata>,
 }

crates/agent_servers/src/acp.rs πŸ”—

@@ -45,7 +45,6 @@ pub struct AcpConnection {
     default_mode: Option<acp::SessionModeId>,
     default_model: Option<acp::ModelId>,
     default_config_options: HashMap<String, String>,
-    root_dir: PathBuf,
     child: Child,
     session_list: Option<Rc<AcpSessionList>>,
     _io_task: Task<Result<(), acp::Error>>,
@@ -161,22 +160,18 @@ pub async fn connect(
     server_name: SharedString,
     display_name: SharedString,
     command: AgentServerCommand,
-    root_dir: &Path,
     default_mode: Option<acp::SessionModeId>,
     default_model: Option<acp::ModelId>,
     default_config_options: HashMap<String, String>,
-    is_remote: bool,
     cx: &mut AsyncApp,
 ) -> Result<Rc<dyn AgentConnection>> {
     let conn = AcpConnection::stdio(
         server_name,
         display_name,
         command.clone(),
-        root_dir,
         default_mode,
         default_model,
         default_config_options,
-        is_remote,
         cx,
     )
     .await?;
@@ -190,11 +185,9 @@ impl AcpConnection {
         server_name: SharedString,
         display_name: SharedString,
         command: AgentServerCommand,
-        root_dir: &Path,
         default_mode: Option<acp::SessionModeId>,
         default_model: Option<acp::ModelId>,
         default_config_options: HashMap<String, String>,
-        is_remote: bool,
         cx: &mut AsyncApp,
     ) -> Result<Self> {
         let shell = cx.update(|cx| TerminalSettings::get(None, cx).shell.clone());
@@ -202,9 +195,6 @@ impl AcpConnection {
         let mut child =
             builder.build_std_command(Some(command.path.display().to_string()), &command.args);
         child.envs(command.env.iter().flatten());
-        if !is_remote {
-            child.current_dir(root_dir);
-        }
         let mut child = Child::spawn(child, Stdio::piped(), Stdio::piped(), Stdio::piped())?;
 
         let stdout = child.stdout.take().context("Failed to take stdout")?;
@@ -331,7 +321,6 @@ impl AcpConnection {
 
         Ok(Self {
             auth_methods: response.auth_methods,
-            root_dir: root_dir.to_owned(),
             connection,
             server_name,
             display_name,
@@ -352,10 +341,6 @@ impl AcpConnection {
     pub fn prompt_capabilities(&self) -> &acp::PromptCapabilities {
         &self.agent_capabilities.prompt_capabilities
     }
-
-    pub fn root_dir(&self) -> &Path {
-        &self.root_dir
-    }
 }
 
 impl Drop for AcpConnection {

crates/agent_servers/src/agent_servers.rs πŸ”—

@@ -22,7 +22,7 @@ use anyhow::Result;
 use gpui::{App, AppContext, Entity, SharedString, Task};
 use project::Project;
 use settings::SettingsStore;
-use std::{any::Any, path::Path, rc::Rc, sync::Arc};
+use std::{any::Any, rc::Rc, sync::Arc};
 
 pub use acp::AcpConnection;
 
@@ -58,7 +58,6 @@ pub trait AgentServer: Send {
     fn name(&self) -> SharedString;
     fn connect(
         &self,
-        root_dir: Option<&Path>,
         delegate: AgentServerDelegate,
         cx: &mut App,
     ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>>;

crates/agent_servers/src/claude.rs πŸ”—

@@ -2,7 +2,6 @@ use agent_client_protocol as acp;
 use collections::HashSet;
 use fs::Fs;
 use settings::{SettingsStore, update_settings_file};
-use std::path::Path;
 use std::rc::Rc;
 use std::sync::Arc;
 use std::{any::Any, path::PathBuf};
@@ -208,13 +207,10 @@ impl AgentServer for ClaudeCode {
 
     fn connect(
         &self,
-        root_dir: Option<&Path>,
         delegate: AgentServerDelegate,
         cx: &mut App,
     ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
         let name = self.name();
-        let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
-        let is_remote = delegate.project.read(cx).is_via_remote_server();
         let store = delegate.store.downgrade();
         let extra_env = load_proxy_env(cx);
         let default_mode = self.default_mode(cx);
@@ -229,13 +225,12 @@ impl AgentServer for ClaudeCode {
         });
 
         cx.spawn(async move |cx| {
-            let (command, root_dir, login) = store
+            let (command, login) = store
                 .update(cx, |store, cx| {
                     let agent = store
                         .get_external_agent(&CLAUDE_AGENT_NAME.into())
                         .context("Claude Agent is not registered")?;
                     anyhow::Ok(agent.get_command(
-                        root_dir.as_deref(),
                         extra_env,
                         delegate.status_tx,
                         delegate.new_version_available,
@@ -247,11 +242,9 @@ impl AgentServer for ClaudeCode {
                 name.clone(),
                 name,
                 command,
-                root_dir.as_ref(),
                 default_mode,
                 default_model,
                 default_config_options,
-                is_remote,
                 cx,
             )
             .await?;

crates/agent_servers/src/codex.rs πŸ”—

@@ -1,6 +1,6 @@
+use std::any::Any;
 use std::rc::Rc;
 use std::sync::Arc;
-use std::{any::Any, path::Path};
 
 use acp_thread::AgentConnection;
 use agent_client_protocol as acp;
@@ -205,13 +205,10 @@ impl AgentServer for Codex {
 
     fn connect(
         &self,
-        root_dir: Option<&Path>,
         delegate: AgentServerDelegate,
         cx: &mut App,
     ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
         let name = self.name();
-        let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
-        let is_remote = delegate.project.read(cx).is_via_remote_server();
         let store = delegate.store.downgrade();
         let mut extra_env = load_proxy_env(cx);
         let default_mode = self.default_mode(cx);
@@ -232,13 +229,12 @@ impl AgentServer for Codex {
         }
 
         cx.spawn(async move |cx| {
-            let (command, root_dir, login) = store
+            let (command, login) = store
                 .update(cx, |store, cx| {
                     let agent = store
                         .get_external_agent(&CODEX_NAME.into())
                         .context("Codex is not registered")?;
                     anyhow::Ok(agent.get_command(
-                        root_dir.as_deref(),
                         extra_env,
                         delegate.status_tx,
                         delegate.new_version_available,
@@ -251,11 +247,9 @@ impl AgentServer for Codex {
                 name.clone(),
                 name,
                 command,
-                root_dir.as_ref(),
                 default_mode,
                 default_model,
                 default_config_options,
-                is_remote,
                 cx,
             )
             .await?;

crates/agent_servers/src/custom.rs πŸ”—

@@ -7,7 +7,7 @@ use fs::Fs;
 use gpui::{App, AppContext as _, SharedString, Task};
 use project::agent_server_store::{AllAgentServersSettings, ExternalAgentServerName};
 use settings::{SettingsStore, update_settings_file};
-use std::{path::Path, rc::Rc, sync::Arc};
+use std::{rc::Rc, sync::Arc};
 use ui::IconName;
 
 /// A generic agent server implementation for custom user-defined agents
@@ -327,7 +327,6 @@ impl AgentServer for CustomAgentServer {
 
     fn connect(
         &self,
-        root_dir: Option<&Path>,
         delegate: AgentServerDelegate,
         cx: &mut App,
     ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
@@ -337,8 +336,6 @@ impl AgentServer for CustomAgentServer {
             .read(cx)
             .agent_display_name(&ExternalAgentServerName(name.clone()))
             .unwrap_or_else(|| name.clone());
-        let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
-        let is_remote = delegate.project.read(cx).is_via_remote_server();
         let default_mode = self.default_mode(cx);
         let default_model = self.default_model(cx);
         let (default_config_options, is_registry_agent) =
@@ -386,7 +383,7 @@ impl AgentServer for CustomAgentServer {
         let store = delegate.store.downgrade();
         let extra_env = load_proxy_env(cx);
         cx.spawn(async move |cx| {
-            let (command, root_dir, login) = store
+            let (command, login) = store
                 .update(cx, |store, cx| {
                     let agent = store
                         .get_external_agent(&ExternalAgentServerName(name.clone()))
@@ -394,7 +391,6 @@ impl AgentServer for CustomAgentServer {
                             format!("Custom agent server `{}` is not registered", name)
                         })?;
                     anyhow::Ok(agent.get_command(
-                        root_dir.as_deref(),
                         extra_env,
                         delegate.status_tx,
                         delegate.new_version_available,
@@ -406,11 +402,9 @@ impl AgentServer for CustomAgentServer {
                 name,
                 display_name,
                 command,
-                root_dir.as_ref(),
                 default_mode,
                 default_model,
                 default_config_options,
-                is_remote,
                 cx,
             )
             .await?;

crates/agent_servers/src/e2e_tests.rs πŸ”—

@@ -444,10 +444,7 @@ pub async fn new_test_thread(
     let store = project.read_with(cx, |project, _| project.agent_server_store().clone());
     let delegate = AgentServerDelegate::new(store, project.clone(), None, None);
 
-    let (connection, _) = cx
-        .update(|cx| server.connect(Some(current_dir.as_ref()), delegate, cx))
-        .await
-        .unwrap();
+    let (connection, _) = cx.update(|cx| server.connect(delegate, cx)).await.unwrap();
 
     cx.update(|cx| connection.new_session(project.clone(), current_dir.as_ref(), cx))
         .await

crates/agent_servers/src/gemini.rs πŸ”—

@@ -1,5 +1,5 @@
+use std::any::Any;
 use std::rc::Rc;
-use std::{any::Any, path::Path};
 
 use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
 use acp_thread::AgentConnection;
@@ -45,13 +45,10 @@ impl AgentServer for Gemini {
 
     fn connect(
         &self,
-        root_dir: Option<&Path>,
         delegate: AgentServerDelegate,
         cx: &mut App,
     ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
         let name = self.name();
-        let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
-        let is_remote = delegate.project.read(cx).is_via_remote_server();
         let store = delegate.store.downgrade();
         let mut extra_env = load_proxy_env(cx);
         let default_mode = self.default_mode(cx);
@@ -71,13 +68,12 @@ impl AgentServer for Gemini {
             if let Some(api_key) = cx.update(api_key_for_gemini_cli).await.ok() {
                 extra_env.insert("GEMINI_API_KEY".into(), api_key);
             }
-            let (command, root_dir, login) = store
+            let (command, login) = store
                 .update(cx, |store, cx| {
                     let agent = store
                         .get_external_agent(&GEMINI_NAME.into())
                         .context("Gemini CLI is not registered")?;
                     anyhow::Ok(agent.get_command(
-                        root_dir.as_deref(),
                         extra_env,
                         delegate.status_tx,
                         delegate.new_version_available,
@@ -90,11 +86,9 @@ impl AgentServer for Gemini {
                 name.clone(),
                 name,
                 command,
-                root_dir.as_ref(),
                 default_mode,
                 default_model,
                 default_config_options,
-                is_remote,
                 cx,
             )
             .await?;

crates/agent_ui/src/acp/thread_view.rs πŸ”—

@@ -569,7 +569,6 @@ impl AcpServerView {
                 }
             })
             .collect();
-        let root_dir = worktree_roots.first().cloned();
         let session_cwd = resume_thread
             .as_ref()
             .and_then(|resume| {
@@ -584,7 +583,7 @@ impl AcpServerView {
                     })
                     .map(|path| path.into())
             })
-            .or_else(|| root_dir.clone())
+            .or_else(|| worktree_roots.first().cloned())
             .unwrap_or_else(|| paths::home_dir().as_path().into());
 
         let (status_tx, mut status_rx) = watch::channel("Loading…".into());
@@ -596,7 +595,7 @@ impl AcpServerView {
             Some(new_version_available_tx),
         );
 
-        let connect_task = agent.connect(root_dir.as_deref(), delegate, cx);
+        let connect_task = agent.connect(delegate, cx);
         let load_task = cx.spawn_in(window, async move |this, cx| {
             let connection = match connect_task.await {
                 Ok((connection, login)) => {
@@ -1419,13 +1418,6 @@ impl AcpServerView {
                     })
                     .unwrap_or_default();
 
-                // Run SpawnInTerminal in the same dir as the ACP server
-                let cwd = connected
-                    .connection
-                    .clone()
-                    .downcast::<agent_servers::AcpConnection>()
-                    .map(|acp_conn| acp_conn.root_dir().to_path_buf());
-
                 // Build SpawnInTerminal from _meta
                 let login = task::SpawnInTerminal {
                     id: task::TaskId(format!("external-agent-{}-login", label)),
@@ -1434,7 +1426,6 @@ impl AcpServerView {
                     command: Some(command.to_string()),
                     args,
                     command_label: label.to_string(),
-                    cwd,
                     env,
                     use_new_terminal: true,
                     allow_concurrent_runs: true,
@@ -3624,7 +3615,6 @@ pub(crate) mod tests {
 
         fn connect(
             &self,
-            _root_dir: Option<&Path>,
             _delegate: AgentServerDelegate,
             _cx: &mut App,
         ) -> Task<gpui::Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
@@ -3649,7 +3639,6 @@ pub(crate) mod tests {
 
         fn connect(
             &self,
-            _root_dir: Option<&Path>,
             _delegate: AgentServerDelegate,
             _cx: &mut App,
         ) -> Task<gpui::Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {

crates/agent_ui/src/mention_set.rs πŸ”—

@@ -547,7 +547,7 @@ impl MentionSet {
             None,
             None,
         );
-        let connection = server.connect(None, delegate, cx);
+        let connection = server.connect(delegate, cx);
         cx.spawn(async move |_, cx| {
             let (agent, _) = connection.await?;
             let agent = agent.downcast::<agent::NativeAgentConnection>().unwrap();

crates/project/src/agent_server_store.rs πŸ”—

@@ -105,12 +105,11 @@ pub enum ExternalAgentSource {
 pub trait ExternalAgentServer {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         status_tx: Option<watch::Sender<SharedString>>,
         new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>>;
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>>;
 
     fn as_any_mut(&mut self) -> &mut dyn Any;
 }
@@ -799,7 +798,7 @@ impl AgentServerStore {
         envelope: TypedEnvelope<proto::GetAgentServerCommand>,
         mut cx: AsyncApp,
     ) -> Result<proto::AgentServerCommand> {
-        let (command, root_dir, login_command) = this
+        let (command, login_command) = this
             .update(&mut cx, |this, cx| {
                 let AgentServerStoreState::Local {
                     downstream_client, ..
@@ -858,7 +857,6 @@ impl AgentServerStore {
                     })
                     .unzip();
                 anyhow::Ok(agent.get_command(
-                    envelope.payload.root_dir.as_deref(),
                     HashMap::default(),
                     status_tx,
                     new_version_available_tx,
@@ -873,7 +871,8 @@ impl AgentServerStore {
                 .env
                 .map(|env| env.into_iter().collect())
                 .unwrap_or_default(),
-            root_dir: root_dir,
+            // This is no longer used, but returned for backwards compatibility
+            root_dir: paths::home_dir().to_string_lossy().to_string(),
             login: login_command.map(|cmd| cmd.to_proto()),
         })
     }
@@ -1254,16 +1253,14 @@ struct RemoteExternalAgentServer {
 impl ExternalAgentServer for RemoteExternalAgentServer {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         status_tx: Option<watch::Sender<SharedString>>,
         new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let project_id = self.project_id;
         let name = self.name.to_string();
         let upstream_client = self.upstream_client.downgrade();
-        let root_dir = root_dir.map(|root_dir| root_dir.to_owned());
         self.status_tx = status_tx;
         self.new_version_available_tx = new_version_available_tx;
         cx.spawn(async move |cx| {
@@ -1274,7 +1271,7 @@ impl ExternalAgentServer for RemoteExternalAgentServer {
                         .request(proto::GetAgentServerCommand {
                             project_id,
                             name,
-                            root_dir: root_dir.clone(),
+                            root_dir: None,
                         })
                 })?
                 .await?;
@@ -1296,7 +1293,6 @@ impl ExternalAgentServer for RemoteExternalAgentServer {
                     args: command.args,
                     env: Some(command.env),
                 },
-                root_dir,
                 response.login.map(SpawnInTerminal::from_proto),
             ))
         })
@@ -1319,29 +1315,25 @@ struct LocalGemini {
 impl ExternalAgentServer for LocalGemini {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         status_tx: Option<watch::Sender<SharedString>>,
         new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let fs = self.fs.clone();
         let node_runtime = self.node_runtime.clone();
         let project_environment = self.project_environment.downgrade();
         let custom_command = self.custom_command.clone();
         let settings_env = self.settings_env.clone();
         let ignore_system_version = self.ignore_system_version;
-        let root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
+        let home_dir = paths::home_dir();
 
         cx.spawn(async move |cx| {
             let mut env = project_environment
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        root_dir.clone(),
+                        home_dir.as_path().into(),
                         cx,
                     )
                 })?
@@ -1355,7 +1347,7 @@ impl ExternalAgentServer for LocalGemini {
                 custom_command
             } else if !ignore_system_version
                 && let Some(bin) =
-                    find_bin_in_path("gemini".into(), root_dir.to_path_buf(), env.clone(), cx).await
+                    find_bin_in_path("gemini".into(), home_dir.to_path_buf(), env.clone(), cx).await
             {
                 AgentServerCommand {
                     path: bin,
@@ -1395,11 +1387,7 @@ impl ExternalAgentServer for LocalGemini {
 
             command.env.get_or_insert_default().extend(extra_env);
             command.args.push("--experimental-acp".into());
-            Ok((
-                command,
-                root_dir.to_string_lossy().into_owned(),
-                Some(login),
-            ))
+            Ok((command, Some(login)))
         })
     }
 
@@ -1419,28 +1407,23 @@ struct LocalClaudeCode {
 impl ExternalAgentServer for LocalClaudeCode {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         status_tx: Option<watch::Sender<SharedString>>,
         new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let fs = self.fs.clone();
         let node_runtime = self.node_runtime.clone();
         let project_environment = self.project_environment.downgrade();
         let custom_command = self.custom_command.clone();
         let settings_env = self.settings_env.clone();
-        let root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
 
         cx.spawn(async move |cx| {
             let mut env = project_environment
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        root_dir.clone(),
+                        paths::home_dir().as_path().into(),
                         cx,
                     )
                 })?
@@ -1472,11 +1455,7 @@ impl ExternalAgentServer for LocalClaudeCode {
             };
 
             command.env.get_or_insert_default().extend(extra_env);
-            Ok((
-                command,
-                root_dir.to_string_lossy().into_owned(),
-                login_command,
-            ))
+            Ok((command, login_command))
         })
     }
 
@@ -1497,21 +1476,16 @@ struct LocalCodex {
 impl ExternalAgentServer for LocalCodex {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         mut status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let fs = self.fs.clone();
         let project_environment = self.project_environment.downgrade();
         let http = self.http_client.clone();
         let custom_command = self.custom_command.clone();
         let settings_env = self.settings_env.clone();
-        let root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
         let no_browser = self.no_browser;
 
         cx.spawn(async move |cx| {
@@ -1519,7 +1493,7 @@ impl ExternalAgentServer for LocalCodex {
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        root_dir.clone(),
+                        paths::home_dir().as_path().into(),
                         cx,
                     )
                 })?
@@ -1664,7 +1638,7 @@ impl ExternalAgentServer for LocalCodex {
             };
 
             command.env.get_or_insert_default().extend(extra_env);
-            Ok((command, root_dir.to_string_lossy().into_owned(), None))
+            Ok((command, None))
         })
     }
 
@@ -1723,12 +1697,11 @@ pub struct LocalExtensionArchiveAgent {
 impl ExternalAgentServer for LocalExtensionArchiveAgent {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         _status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let fs = self.fs.clone();
         let http_client = self.http_client.clone();
         let node_runtime = self.node_runtime.clone();
@@ -1738,18 +1711,13 @@ impl ExternalAgentServer for LocalExtensionArchiveAgent {
         let targets = self.targets.clone();
         let base_env = self.env.clone();
 
-        let root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
-
         cx.spawn(async move |cx| {
             // Get project environment
             let mut env = project_environment
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        root_dir.clone(),
+                        paths::home_dir().as_path().into(),
                         cx,
                     )
                 })?
@@ -1909,7 +1877,7 @@ impl ExternalAgentServer for LocalExtensionArchiveAgent {
                 env: Some(env),
             };
 
-            Ok((command, version_dir.to_string_lossy().into_owned(), None))
+            Ok((command, None))
         })
     }
 
@@ -1931,12 +1899,11 @@ struct LocalRegistryArchiveAgent {
 impl ExternalAgentServer for LocalRegistryArchiveAgent {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         _status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let fs = self.fs.clone();
         let http_client = self.http_client.clone();
         let node_runtime = self.node_runtime.clone();
@@ -1945,17 +1912,12 @@ impl ExternalAgentServer for LocalRegistryArchiveAgent {
         let targets = self.targets.clone();
         let settings_env = self.env.clone();
 
-        let root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
-
         cx.spawn(async move |cx| {
             let mut env = project_environment
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        root_dir.clone(),
+                        paths::home_dir().as_path().into(),
                         cx,
                     )
                 })?
@@ -2099,7 +2061,7 @@ impl ExternalAgentServer for LocalRegistryArchiveAgent {
                 env: Some(env),
             };
 
-            Ok((command, version_dir.to_string_lossy().into_owned(), None))
+            Ok((command, None))
         })
     }
 
@@ -2120,12 +2082,11 @@ struct LocalRegistryNpxAgent {
 impl ExternalAgentServer for LocalRegistryNpxAgent {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         _status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let node_runtime = self.node_runtime.clone();
         let project_environment = self.project_environment.downgrade();
         let package = self.package.clone();
@@ -2133,17 +2094,12 @@ impl ExternalAgentServer for LocalRegistryNpxAgent {
         let distribution_env = self.distribution_env.clone();
         let settings_env = self.settings_env.clone();
 
-        let env_root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
-
         cx.spawn(async move |cx| {
             let mut env = project_environment
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        env_root_dir.clone(),
+                        paths::home_dir().as_path().into(),
                         cx,
                     )
                 })?
@@ -2176,7 +2132,7 @@ impl ExternalAgentServer for LocalRegistryNpxAgent {
                 env: Some(env),
             };
 
-            Ok((command, env_root_dir.to_string_lossy().into_owned(), None))
+            Ok((command, None))
         })
     }
 
@@ -2193,24 +2149,19 @@ struct LocalCustomAgent {
 impl ExternalAgentServer for LocalCustomAgent {
     fn get_command(
         &mut self,
-        root_dir: Option<&str>,
         extra_env: HashMap<String, String>,
         _status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         let mut command = self.command.clone();
-        let root_dir: Arc<Path> = root_dir
-            .map(|root_dir| Path::new(root_dir))
-            .unwrap_or(paths::home_dir())
-            .into();
         let project_environment = self.project_environment.downgrade();
         cx.spawn(async move |cx| {
             let mut env = project_environment
                 .update(cx, |project_environment, cx| {
                     project_environment.local_directory_environment(
                         &Shell::System,
-                        root_dir.clone(),
+                        paths::home_dir().as_path().into(),
                         cx,
                     )
                 })?
@@ -2219,7 +2170,7 @@ impl ExternalAgentServer for LocalCustomAgent {
             env.extend(command.env.unwrap_or_default());
             env.extend(extra_env);
             command.env = Some(env);
-            Ok((command, root_dir.to_string_lossy().into_owned(), None))
+            Ok((command, None))
         })
     }
 

crates/project/tests/integration/ext_agent_tests.rs πŸ”—

@@ -9,19 +9,17 @@ struct NoopExternalAgent;
 impl ExternalAgentServer for NoopExternalAgent {
     fn get_command(
         &mut self,
-        _root_dir: Option<&str>,
         _extra_env: HashMap<String, String>,
         _status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         _cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         Task::ready(Ok((
             AgentServerCommand {
                 path: PathBuf::from("noop"),
                 args: Vec::new(),
                 env: None,
             },
-            "".to_string(),
             None,
         )))
     }

crates/project/tests/integration/extension_agent_tests.rs πŸ”—

@@ -25,19 +25,17 @@ struct NoopExternalAgent;
 impl ExternalAgentServer for NoopExternalAgent {
     fn get_command(
         &mut self,
-        _root_dir: Option<&str>,
         _extra_env: HashMap<String, String>,
         _status_tx: Option<watch::Sender<SharedString>>,
         _new_version_available_tx: Option<watch::Sender<Option<String>>>,
         _cx: &mut AsyncApp,
-    ) -> Task<Result<(AgentServerCommand, String, Option<task::SpawnInTerminal>)>> {
+    ) -> Task<Result<(AgentServerCommand, Option<task::SpawnInTerminal>)>> {
         Task::ready(Ok((
             AgentServerCommand {
                 path: PathBuf::from("noop"),
                 args: Vec::new(),
                 env: None,
             },
-            "".to_string(),
             None,
         )))
     }

crates/remote_server/src/remote_editing_tests.rs πŸ”—

@@ -2030,14 +2030,13 @@ async fn test_remote_external_agent_server(
             .collect::<Vec<_>>()
     });
     pretty_assertions::assert_eq!(names, ["gemini", "codex", "claude", "foo"]);
-    let (command, root, login) = project
+    let (command, login) = project
         .update(cx, |project, cx| {
             project.agent_server_store().update(cx, |store, cx| {
                 store
                     .get_external_agent(&"foo".into())
                     .unwrap()
                     .get_command(
-                        None,
                         HashMap::from_iter([("OTHER_VAR".into(), "other-val".into())]),
                         None,
                         None,
@@ -2058,7 +2057,6 @@ async fn test_remote_external_agent_server(
             ]))
         }
     );
-    assert_eq!(&PathBuf::from(root), paths::home_dir());
     assert!(login.is_none());
 }
 

crates/zed/src/visual_test_runner.rs πŸ”—

@@ -1945,7 +1945,6 @@ impl AgentServer for StubAgentServer {
 
     fn connect(
         &self,
-        _root_dir: Option<&Path>,
         _delegate: AgentServerDelegate,
         _cx: &mut App,
     ) -> gpui::Task<gpui::Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {