diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index 5c718cede6..8514f47b11 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -24,7 +24,9 @@ use codex_agent_graph_store::LocalAgentGraphStore; use codex_analytics::AnalyticsEventsClient; use codex_app_server_protocol::ThreadHistoryBuilder; use codex_app_server_protocol::TurnStatus; +use codex_code_mode::CodeModeSessionDelegate; use codex_code_mode::CodeModeSessionProvider; +use codex_code_mode::CodeModeSessionProviderFuture; use codex_code_mode::InProcessCodeModeSessionProvider; use codex_code_mode::ProcessOwnedCodeModeSessionProvider; use codex_core_plugins::PluginsManager; @@ -95,6 +97,20 @@ use tracing::instrument; use tracing::warn; const THREAD_CREATED_CHANNEL_CAPACITY: usize = 1024; + +struct DisabledCodeModeSessionProvider; + +impl CodeModeSessionProvider for DisabledCodeModeSessionProvider { + fn create_session<'a>( + &'a self, + _delegate: Arc, + ) -> CodeModeSessionProviderFuture<'a> { + Box::pin(async { + Err("code-mode host is disabled and in-process fallback is disabled".to_string()) + }) + } +} + /// Test-only override for enabling thread-manager behaviors used by integration /// tests. /// @@ -382,6 +398,8 @@ impl ThreadManager { } else { Arc::new(provider) } + } else if config.code_mode.disable_in_process_fallback { + Arc::new(DisabledCodeModeSessionProvider) } else { Arc::new(InProcessCodeModeSessionProvider) }; diff --git a/codex-rs/core/tests/suite/code_mode.rs b/codex-rs/core/tests/suite/code_mode.rs index 1003534324..2a9bf2b486 100644 --- a/codex-rs/core/tests/suite/code_mode.rs +++ b/codex-rs/core/tests/suite/code_mode.rs @@ -285,6 +285,35 @@ async fn missing_process_host_fails_when_in_process_fallback_is_disabled() -> Re Ok(()) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn disabled_process_host_fails_when_in_process_fallback_is_disabled() -> Result<()> { + skip_if_no_network!(Ok(())); + + let server = responses::start_mock_server().await; + let builder = test_codex() + .with_model("test-gpt-5.1-codex") + .with_config(|config| { + config + .features + .enable(Feature::CodeMode) + .expect("code mode should be enabled"); + config + .features + .disable(Feature::CodeModeHost) + .expect("code-mode host should be disabled"); + config.code_mode.disable_in_process_fallback = true; + }); + let (_test, follow_up_mock) = + run_code_mode_turn_with_builder(&server, "Run code mode", "text('unreachable')", builder) + .await?; + + let (output, _) = + custom_tool_output_body_and_success(&follow_up_mock.single_request(), "call-1"); + assert!(output.contains("code-mode host is disabled and in-process fallback is disabled")); + + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn missing_process_host_error_is_bounded_when_in_process_fallback_is_disabled() -> Result<()> {