From da4cf1cdeaf8fb44a18bb75fd8df0094097f90b8 Mon Sep 17 00:00:00 2001 From: Won Park Date: Tue, 25 Aug 2026 16:41:39 +0000 Subject: [PATCH] Preserve Windows proxy settings in Guardian sessions (#40641) ## Why Guardian sessions can run alongside another Windows sandbox launch and should not reconcile the persistent proxy settings established by that launch. ## What changed - Start internal Guardian sessions with `WindowsSandboxProxySettingsMode::Preserve`. - Continue using `Reconcile` for other sessions. ## Testing Added a thread manager test that verifies a parent session reconciles proxy settings while its internal Guardian session preserves them. GitOrigin-RevId: c6a31637ecdf828548cfd7153da7e9405abe34fa --- codex-rs/core/src/thread_manager.rs | 12 +++++- codex-rs/core/src/thread_manager_tests.rs | 45 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index 5cbba9778b..0377f2485e 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -55,6 +55,7 @@ use codex_protocol::mcp::OPENAI_STANDARD_FORM_INPUT_EXTENSION_ID; use codex_protocol::openai_models::ModelPreset; use codex_protocol::protocol::Event; use codex_protocol::protocol::EventMsg; +use codex_protocol::protocol::InternalSessionSource; use codex_protocol::protocol::MultiAgentVersion; use codex_protocol::protocol::Op; use codex_protocol::protocol::SessionConfiguredEvent; @@ -1938,6 +1939,14 @@ impl ThreadManagerState { starting.retain(|runtime| runtime.strong_count() != 0); starting.push(Arc::downgrade(&source_changed_during_startup)); } + let windows_sandbox_proxy_settings_mode = if matches!( + &session_source, + SessionSource::Internal(InternalSessionSource::Guardian) + ) { + codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve + } else { + codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile + }; let (session, io) = Box::pin(Session::spawn(SessionSpawnArgs { config, allow_provider_model_fallback, @@ -1977,8 +1986,7 @@ impl ThreadManagerState { external_time_provider: self.external_time_provider.clone(), inherited_multi_agent_version: multi_agent_version, git_enrichment_policy: GitEnrichmentPolicy::Fresh, - windows_sandbox_proxy_settings_mode: - codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile, + windows_sandbox_proxy_settings_mode, })) .await?; // Enable Full Access form input only after session startup so a required MCP server cannot diff --git a/codex-rs/core/src/thread_manager_tests.rs b/codex-rs/core/src/thread_manager_tests.rs index 58bf026bdc..d64b5c7439 100644 --- a/codex-rs/core/src/thread_manager_tests.rs +++ b/codex-rs/core/src/thread_manager_tests.rs @@ -878,6 +878,51 @@ async fn start_thread_keeps_internal_threads_hidden_from_normal_lookups() { assert!(manager.list_thread_ids().await.is_empty()); } +#[tokio::test] +async fn spawn_internal_guardian_session_preserves_windows_sandbox_proxy_settings() { + let temp_dir = tempdir().expect("tempdir"); + let mut config = test_config().await; + config.codex_home = temp_dir.path().join("codex-home").abs(); + config.cwd = config.codex_home.abs(); + std::fs::create_dir_all(&config.codex_home).expect("create codex home"); + + let manager = ThreadManager::with_models_provider_and_home_for_tests( + CodexAuth::from_api_key("dummy"), + config.model_provider.clone(), + config.codex_home.to_path_buf(), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), + ); + let parent = manager + .start_thread(StartThreadOptions::new(config.clone())) + .await + .expect("start parent thread"); + let reviewer = manager + .spawn_internal_session( + parent.thread_id, + StartThreadOptions { + session_source: Some(SessionSource::Internal(InternalSessionSource::Guardian)), + ..StartThreadOptions::new(config) + }, + ) + .await + .expect("start internal reviewer"); + + assert_eq!( + ( + parent.thread.session.windows_sandbox_proxy_settings_mode, + reviewer.thread.session.windows_sandbox_proxy_settings_mode, + ), + ( + codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile, + codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve, + ) + ); + + manager + .shutdown_all_threads_bounded(Duration::from_secs(10)) + .await; +} + #[tokio::test] async fn spawn_internal_session_preserves_parent_lineage_without_forking_history() { struct ParentLifecycleContributor {