From ea2fd29943689c1b49c63669ba58c81478198fb0 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 8 Oct 2025 14:37:34 +0200 Subject: [PATCH 1/5] Provide codex as an option on remote sessions --- crates/project/src/agent_server_store.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/project/src/agent_server_store.rs b/crates/project/src/agent_server_store.rs index 19d62f019ec80a..2887a688c35d62 100644 --- a/crates/project/src/agent_server_store.rs +++ b/crates/project/src/agent_server_store.rs @@ -253,7 +253,6 @@ impl AgentServerStore { names: self .external_agents .keys() - .filter(|name| name.0 != CODEX_NAME) .map(|name| name.to_string()) .collect(), }) @@ -297,12 +296,12 @@ impl AgentServerStore { pub(crate) fn remote( project_id: u64, upstream_client: Entity, - _cx: &mut Context, + cx: &mut Context, ) -> Self { // Set up the builtin agents here so they're immediately available in // remote projects--we know that the HeadlessProject on the other end // will have them. - let external_agents = [ + let mut external_agents = [ ( GEMINI_NAME.into(), Box::new(RemoteExternalAgentServer { @@ -325,7 +324,21 @@ impl AgentServerStore { ), ] .into_iter() - .collect(); + .collect::>>(); + + use feature_flags::FeatureFlagAppExt as _; + if cx.has_flag::() { + external_agents.insert( + CODEX_NAME.into(), + Box::new(RemoteExternalAgentServer { + project_id, + upstream_client: upstream_client.clone(), + name: CODEX_NAME.into(), + status_tx: None, + new_version_available_tx: None, + }) as Box, + ); + } Self { state: AgentServerStoreState::Remote { From 544d883fa45cd2477309a81be2db3630c77f83f4 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 8 Oct 2025 15:01:45 +0200 Subject: [PATCH 2/5] Update test --- crates/remote_server/src/remote_editing_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index 7527e3f139cecc..4119134239fa62 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -1792,7 +1792,7 @@ async fn test_remote_external_agent_server( .map(|name| name.to_string()) .collect::>() }); - pretty_assertions::assert_eq!(names, ["gemini", "claude"]); + pretty_assertions::assert_eq!(names, ["gemini", "codex", "claude"]); server_cx.update_global::(|settings_store, cx| { settings_store .set_server_settings( From d58fb9ed6feb07504e78c7f4b2cf3618757b4e61 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Wed, 8 Oct 2025 15:25:13 +0200 Subject: [PATCH 3/5] Update test again --- crates/remote_server/src/remote_editing_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/remote_server/src/remote_editing_tests.rs b/crates/remote_server/src/remote_editing_tests.rs index 4119134239fa62..bd13598e3efda2 100644 --- a/crates/remote_server/src/remote_editing_tests.rs +++ b/crates/remote_server/src/remote_editing_tests.rs @@ -1822,7 +1822,7 @@ async fn test_remote_external_agent_server( .map(|name| name.to_string()) .collect::>() }); - pretty_assertions::assert_eq!(names, ["gemini", "foo", "claude"]); + pretty_assertions::assert_eq!(names, ["gemini", "codex", "claude", "foo"]); let (command, root, login) = project .update(cx, |project, cx| { project.agent_server_store().update(cx, |store, cx| { From 9a88976452fe76b96188f9793c8273114871ae57 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Wed, 8 Oct 2025 15:06:55 -0400 Subject: [PATCH 4/5] don't rely on having a global Client --- crates/project/src/agent_server_store.rs | 11 ++++++++--- crates/project/src/project.rs | 8 +++++++- crates/remote_server/src/headless_project.rs | 9 +++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/crates/project/src/agent_server_store.rs b/crates/project/src/agent_server_store.rs index 2887a688c35d62..2575bf34faa76a 100644 --- a/crates/project/src/agent_server_store.rs +++ b/crates/project/src/agent_server_store.rs @@ -8,7 +8,6 @@ use std::{ }; use anyhow::{Context as _, Result, bail}; -use client::Client; use collections::HashMap; use feature_flags::FeatureFlagAppExt as _; use fs::{Fs, RemoveOptions, RenameOptions}; @@ -16,7 +15,7 @@ use futures::StreamExt as _; use gpui::{ App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, SharedString, Subscription, Task, }; -use http_client::github::AssetKind; +use http_client::{HttpClient, github::AssetKind}; use node_runtime::NodeRuntime; use remote::RemoteClient; use rpc::{AnyProtoClient, TypedEnvelope, proto}; @@ -114,6 +113,7 @@ enum AgentServerStoreState { project_environment: Entity, downstream_client: Option<(u64, AnyProtoClient)>, settings: Option, + http_client: Arc, _subscriptions: [Subscription; 1], }, Remote { @@ -174,6 +174,7 @@ impl AgentServerStore { project_environment, downstream_client, settings: old_settings, + http_client, .. } = &mut self.state else { @@ -227,6 +228,7 @@ impl AgentServerStore { .codex .clone() .and_then(|settings| settings.custom_command()), + http_client: http_client.clone(), }), ); } @@ -265,6 +267,7 @@ impl AgentServerStore { node_runtime: NodeRuntime, fs: Arc, project_environment: Entity, + http_client: Arc, cx: &mut Context, ) -> Self { let subscription = cx.observe_global::(|this, cx| { @@ -282,6 +285,7 @@ impl AgentServerStore { node_runtime, fs, project_environment, + http_client, downstream_client: None, settings: None, _subscriptions: [subscription], @@ -1016,6 +1020,7 @@ impl ExternalAgentServer for LocalClaudeCode { struct LocalCodex { fs: Arc, project_environment: Entity, + http_client: Arc, custom_command: Option, } @@ -1030,6 +1035,7 @@ impl ExternalAgentServer for LocalCodex { ) -> Task)>> { 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 root_dir: Arc = root_dir .map(|root_dir| Path::new(root_dir)) @@ -1053,7 +1059,6 @@ impl ExternalAgentServer for LocalCodex { fs.create_dir(&dir).await?; // Find or install the latest Codex release (no update checks for now). - let http = cx.update(|cx| Client::global(cx).http_client())?; let release = ::http_client::github::latest_github_release( CODEX_ACP_REPO, true, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 66b5702f36f462..f3886b248ff714 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1154,7 +1154,13 @@ impl Project { }); let agent_server_store = cx.new(|cx| { - AgentServerStore::local(node.clone(), fs.clone(), environment.clone(), cx) + AgentServerStore::local( + node.clone(), + fs.clone(), + environment.clone(), + client.http_client(), + cx, + ) }); cx.subscribe(&lsp_store, Self::on_lsp_store_event).detach(); diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index 1d5e72ff9bd245..8ddc3e98e53813 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -195,8 +195,13 @@ impl HeadlessProject { }); let agent_server_store = cx.new(|cx| { - let mut agent_server_store = - AgentServerStore::local(node_runtime.clone(), fs.clone(), environment, cx); + let mut agent_server_store = AgentServerStore::local( + node_runtime.clone(), + fs.clone(), + environment, + http_client.clone(), + cx, + ); agent_server_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone(), cx); agent_server_store }); From 926725f9f6ac890ee2bf008fad13dfe82b4e25d9 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Thu, 9 Oct 2025 15:53:22 +0200 Subject: [PATCH 5/5] Don't use browser login in ssh mode --- crates/project/src/agent_server_store.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/project/src/agent_server_store.rs b/crates/project/src/agent_server_store.rs index 2575bf34faa76a..2a25ba9520849a 100644 --- a/crates/project/src/agent_server_store.rs +++ b/crates/project/src/agent_server_store.rs @@ -229,6 +229,7 @@ impl AgentServerStore { .clone() .and_then(|settings| settings.custom_command()), http_client: http_client.clone(), + is_remote: downstream_client.is_some(), }), ); } @@ -1022,6 +1023,7 @@ struct LocalCodex { project_environment: Entity, http_client: Arc, custom_command: Option, + is_remote: bool, } impl ExternalAgentServer for LocalCodex { @@ -1041,6 +1043,7 @@ impl ExternalAgentServer for LocalCodex { .map(|root_dir| Path::new(root_dir)) .unwrap_or(paths::home_dir()) .into(); + let is_remote = self.is_remote; cx.spawn(async move |cx| { let mut env = project_environment @@ -1049,6 +1052,9 @@ impl ExternalAgentServer for LocalCodex { })? .await .unwrap_or_default(); + if is_remote { + env.insert("NO_BROWSER".to_owned(), "1".to_owned()); + } let mut command = if let Some(mut custom_command) = custom_command { env.extend(custom_command.env.unwrap_or_default());