diff --git a/codex-rs/app-server-client/src/lib.rs b/codex-rs/app-server-client/src/lib.rs index 7bc5ccca93..fdd90f9281 100644 --- a/codex-rs/app-server-client/src/lib.rs +++ b/codex-rs/app-server-client/src/lib.rs @@ -970,9 +970,7 @@ mod tests { cloud_requirements: CloudRequirementsLoader::default(), feedback: CodexFeedback::new(), log_db: None, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source, enable_codex_api_key_env: false, @@ -1975,7 +1973,11 @@ mod tests { let config = Arc::new(build_test_config().await); let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: Some("ws://127.0.0.1:8765".to_string()), - local_runtime_paths: None, + local_runtime_paths: ExecServerRuntimePaths::new( + std::env::current_exe().expect("current exe"), + /*codex_linux_sandbox_exe*/ None, + ) + .expect("runtime paths"), })); let runtime_args = InProcessClientStartArgs { diff --git a/codex-rs/app-server/src/bespoke_event_handling.rs b/codex-rs/app-server/src/bespoke_event_handling.rs index 8d85fa7000..56cd688d2a 100644 --- a/codex-rs/app-server/src/bespoke_event_handling.rs +++ b/codex-rs/app-server/src/bespoke_event_handling.rs @@ -3497,9 +3497,7 @@ mod tests { CodexAuth::create_dummy_chatgpt_auth_for_testing(), config.model_provider.clone(), config.codex_home.to_path_buf(), - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ), ); let codex_core::NewThread { diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index ba6cf19a87..54c10f670c 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -5694,18 +5694,15 @@ impl CodexMessageProcessor { .await; let auth_manager = Arc::clone(&self.auth_manager); let auth = auth_manager.auth().await; - let runtime_environment = match self - .thread_manager - .environment_manager() - .default_environment() - { + let environment_manager = self.thread_manager.environment_manager(); + let runtime_environment = match environment_manager.default_environment() { Some(environment) => { // Status listing has no turn cwd. This fallback is used only // by executor-backed stdio MCPs whose config omits `cwd`. McpRuntimeEnvironment::new(environment, config.cwd.to_path_buf()) } None => McpRuntimeEnvironment::new( - Arc::new(codex_exec_server::Environment::default()), + environment_manager.local_environment(), config.cwd.to_path_buf(), ), }; @@ -5858,11 +5855,10 @@ impl CodexMessageProcessor { .await; let auth = self.auth_manager.auth().await; let runtime_environment = { - let environment = self - .thread_manager - .environment_manager() + let environment_manager = self.thread_manager.environment_manager(); + let environment = environment_manager .default_environment() - .unwrap_or_else(|| Arc::new(codex_exec_server::Environment::default())); + .unwrap_or_else(|| environment_manager.local_environment()); // Resource reads without a thread have no turn cwd. This fallback // is used only by executor-backed stdio MCPs whose config omits `cwd`. McpRuntimeEnvironment::new(environment, config.cwd.to_path_buf()) diff --git a/codex-rs/app-server/src/fs_api.rs b/codex-rs/app-server/src/fs_api.rs index a2c71871db..93b4f21c2b 100644 --- a/codex-rs/app-server/src/fs_api.rs +++ b/codex-rs/app-server/src/fs_api.rs @@ -20,7 +20,6 @@ use codex_app_server_protocol::FsWriteFileResponse; use codex_app_server_protocol::JSONRPCErrorError; use codex_exec_server::CopyOptions; use codex_exec_server::CreateDirectoryOptions; -use codex_exec_server::Environment; use codex_exec_server::ExecutorFileSystem; use codex_exec_server::RemoveOptions; use std::io; @@ -31,15 +30,11 @@ pub(crate) struct FsApi { file_system: Arc, } -impl Default for FsApi { - fn default() -> Self { - Self { - file_system: Environment::default().get_filesystem(), - } - } -} - impl FsApi { + pub(crate) fn new(file_system: Arc) -> Self { + Self { file_system } + } + pub(crate) async fn read_file( &self, params: FsReadFileParams, diff --git a/codex-rs/app-server/src/in_process.rs b/codex-rs/app-server/src/in_process.rs index d549956b5f..924398037b 100644 --- a/codex-rs/app-server/src/in_process.rs +++ b/codex-rs/app-server/src/in_process.rs @@ -738,9 +738,7 @@ mod tests { thread_config_loader: Arc::new(codex_config::NoopThreadConfigLoader), feedback: CodexFeedback::new(), log_db: None, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source, enable_codex_api_key_env: false, diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 0ebf22f4a1..d3e874c5c4 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -362,13 +362,12 @@ pub async fn run_main_with_transport( session_source: SessionSource, auth: AppServerWebsocketAuthSettings, ) -> IoResult<()> { - let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs { - local_runtime_paths: Some(ExecServerRuntimePaths::from_optional_paths( + let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::from_env( + ExecServerRuntimePaths::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), - )?), - ..EnvironmentManagerArgs::default() - })); + )?, + ))); let (transport_event_tx, mut transport_event_rx) = mpsc::channel::(CHANNEL_CAPACITY); let (outgoing_tx, mut outgoing_rx) = mpsc::channel::(CHANNEL_CAPACITY); diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 53d9f2df4c..7fb0df42f6 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -327,7 +327,12 @@ impl MessageProcessor { ); let external_agent_config_api = ExternalAgentConfigApi::new(config.codex_home.to_path_buf()); - let fs_api = FsApi::default(); + let fs_api = FsApi::new( + thread_manager + .environment_manager() + .local_environment() + .get_filesystem(), + ); let fs_watch_manager = FsWatchManager::new(outgoing.clone()); Self { diff --git a/codex-rs/app-server/src/message_processor/tracing_tests.rs b/codex-rs/app-server/src/message_processor/tracing_tests.rs index 6f81532cef..42ac4d8596 100644 --- a/codex-rs/app-server/src/message_processor/tracing_tests.rs +++ b/codex-rs/app-server/src/message_processor/tracing_tests.rs @@ -241,9 +241,7 @@ fn build_test_processor( outgoing, arg0_paths: Arg0DispatchPaths::default(), config, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), cli_overrides: Vec::new(), loader_overrides: LoaderOverrides::default(), cloud_requirements: CloudRequirementsLoader::default(), diff --git a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs index 7af4682cad..a347d87fc7 100644 --- a/codex-rs/app-server/tests/suite/v2/mcp_resource.rs +++ b/codex-rs/app-server/tests/suite/v2/mcp_resource.rs @@ -204,9 +204,7 @@ async fn mcp_resource_read_returns_error_for_unknown_thread() -> Result<()> { thread_config_loader: Arc::new(codex_config::NoopThreadConfigLoader), feedback: CodexFeedback::new(), log_db: None, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), config_warnings: Vec::new(), session_source: SessionSource::Cli, enable_codex_api_key_env: false, diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index 567ee1a029..1a5c38723f 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -95,9 +95,7 @@ impl AgentControlHarness { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); Self { @@ -911,9 +909,7 @@ async fn spawn_agent_respects_max_threads_limit() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); @@ -965,9 +961,7 @@ async fn spawn_agent_releases_slot_after_shutdown() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); @@ -1010,9 +1004,7 @@ async fn spawn_agent_limit_shared_across_clones() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); let cloned = control.clone(); @@ -1057,9 +1049,7 @@ async fn resume_agent_respects_max_threads_limit() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); @@ -1115,9 +1105,7 @@ async fn resume_agent_releases_slot_after_resume_failure() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); @@ -1512,9 +1500,7 @@ async fn resume_thread_subagent_restores_stored_nickname_and_role() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let control = manager.agent_control(); let harness = AgentControlHarness { diff --git a/codex-rs/core/src/connectors.rs b/codex-rs/core/src/connectors.rs index 965521ae3d..b4576f26af 100644 --- a/codex-rs/core/src/connectors.rs +++ b/codex-rs/core/src/connectors.rs @@ -13,7 +13,9 @@ pub use codex_app_server_protocol::AppInfo; pub use codex_app_server_protocol::AppMetadata; use codex_connectors::AllConnectorsCacheKey; use codex_connectors::DirectoryListResponse; -use codex_exec_server::Environment; +use codex_exec_server::EnvironmentManager; +use codex_exec_server::EnvironmentManagerArgs; +use codex_exec_server::ExecServerRuntimePaths; use codex_login::token_data::TokenData; use codex_protocol::protocol::SandboxPolicy; use codex_tools::DiscoverableTool; @@ -247,6 +249,16 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status( let (tx_event, rx_event) = unbounded(); drop(rx_event); + let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + config.codex_self_exe.clone(), + config.codex_linux_sandbox_exe.clone(), + )?; + let environment_manager = + EnvironmentManager::new(EnvironmentManagerArgs::from_env(local_runtime_paths)); + let environment = environment_manager + .default_environment() + .unwrap_or_else(|| environment_manager.local_environment()); + let (mcp_connection_manager, cancel_token) = McpConnectionManager::new( &mcp_servers, config.mcp_oauth_credentials_store_mode, @@ -255,7 +267,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status( INITIAL_SUBMIT_ID.to_owned(), tx_event, SandboxPolicy::new_read_only_policy(), - McpRuntimeEnvironment::new(Arc::new(Environment::default()), config.cwd.to_path_buf()), + McpRuntimeEnvironment::new(environment, config.cwd.to_path_buf()), config.codex_home.to_path_buf(), codex_apps_tools_cache_key(auth.as_ref()), ToolPluginProvenance::default(), diff --git a/codex-rs/core/src/memories/tests.rs b/codex-rs/core/src/memories/tests.rs index 18cec7c157..1b5614d314 100644 --- a/codex-rs/core/src/memories/tests.rs +++ b/codex-rs/core/src/memories/tests.rs @@ -491,9 +491,7 @@ mod phase2 { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - std::sync::Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + std::sync::Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let (mut session, _turn_context) = make_session_and_context().await; session.services.state_db = Some(Arc::clone(&state_db)); diff --git a/codex-rs/core/src/prompt_debug.rs b/codex-rs/core/src/prompt_debug.rs index 73d8c2ae14..1f62c2b088 100644 --- a/codex-rs/core/src/prompt_debug.rs +++ b/codex-rs/core/src/prompt_debug.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use codex_exec_server::EnvironmentManager; use codex_exec_server::EnvironmentManagerArgs; +use codex_exec_server::ExecServerRuntimePaths; use codex_features::Feature; use codex_login::AuthManager; use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig; @@ -30,6 +31,11 @@ pub async fn build_prompt_input( let auth_manager = AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false); + let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths( + config.codex_self_exe.clone(), + config.codex_linux_sandbox_exe.clone(), + )?; + let thread_manager = ThreadManager::new( &config, Arc::clone(&auth_manager), @@ -39,7 +45,9 @@ pub async fn build_prompt_input( .features .enabled(Feature::DefaultModeRequestUserInput), }, - Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::default())), + Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::from_env( + local_runtime_paths, + ))), /*analytics_events_client*/ None, ); let thread = thread_manager.start_thread(config).await?; diff --git a/codex-rs/core/src/session/mcp.rs b/codex-rs/core/src/session/mcp.rs index be7504d9e7..0696d9db0c 100644 --- a/codex-rs/core/src/session/mcp.rs +++ b/codex-rs/core/src/session/mcp.rs @@ -250,7 +250,7 @@ impl Session { turn_context .environment .clone() - .unwrap_or_else(|| Arc::new(Environment::default())), + .unwrap_or_else(|| self.services.environment_manager.local_environment()), turn_context.cwd.to_path_buf(), ), config.codex_home.to_path_buf(), diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 24ba14ca6d..0c8ab535f5 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -793,7 +793,7 @@ impl Session { sess.services .environment_manager .default_environment() - .unwrap_or_else(|| Arc::new(Environment::default())), + .unwrap_or_else(|| sess.services.environment_manager.local_environment()), session_configuration.cwd.to_path_buf(), ), config.codex_home.to_path_buf(), diff --git a/codex-rs/core/src/session/tests.rs b/codex-rs/core/src/session/tests.rs index 0726d6ba06..2479daae15 100644 --- a/codex-rs/core/src/session/tests.rs +++ b/codex-rs/core/src/session/tests.rs @@ -3179,7 +3179,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) { )); let network_approval = Arc::new(NetworkApprovalService::default()); let environment = Arc::new( - codex_exec_server::Environment::create(/*exec_server_url*/ None) + codex_exec_server::Environment::create_for_tests(/*exec_server_url*/ None) .expect("create environment"), ); @@ -4277,7 +4277,7 @@ where )); let network_approval = Arc::new(NetworkApprovalService::default()); let environment = Arc::new( - codex_exec_server::Environment::create(/*exec_server_url*/ None) + codex_exec_server::Environment::create_for_tests(/*exec_server_url*/ None) .expect("create environment"), ); diff --git a/codex-rs/core/src/session/tests/guardian_tests.rs b/codex-rs/core/src/session/tests/guardian_tests.rs index 32b9a5799b..5fb7fe3316 100644 --- a/codex-rs/core/src/session/tests/guardian_tests.rs +++ b/codex-rs/core/src/session/tests/guardian_tests.rs @@ -634,9 +634,7 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() { config, auth_manager, models_manager, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), skills_manager, plugins_manager, mcp_manager, diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index 5dc8a2b79e..066bfe3165 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -301,9 +301,7 @@ impl ThreadManager { auth, provider, codex_home.clone(), - Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(EnvironmentManager::default_for_tests()), ); manager._test_codex_home_guard = Some(TempCodexHomeGuard { path: codex_home }); manager diff --git a/codex-rs/core/src/thread_manager_tests.rs b/codex-rs/core/src/thread_manager_tests.rs index 8b6f2c3048..4dcc29f562 100644 --- a/codex-rs/core/src/thread_manager_tests.rs +++ b/codex-rs/core/src/thread_manager_tests.rs @@ -246,9 +246,7 @@ async fn shutdown_all_threads_bounded_submits_shutdown_to_every_thread() { CodexAuth::from_api_key("dummy"), config.model_provider.clone(), config.codex_home.to_path_buf(), - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), ); let thread_1 = manager .start_thread(config.clone()) @@ -297,9 +295,7 @@ async fn new_uses_configured_openai_provider_for_model_refresh() { auth_manager, SessionSource::Exec, CollaborationModesConfig::default(), - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), /*analytics_events_client*/ None, ); @@ -434,9 +430,7 @@ async fn interrupted_fork_snapshot_does_not_synthesize_turn_id_for_legacy_histor auth_manager.clone(), SessionSource::Exec, CollaborationModesConfig::default(), - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), /*analytics_events_client*/ None, ); @@ -537,9 +531,7 @@ async fn interrupted_fork_snapshot_preserves_explicit_turn_id() { auth_manager.clone(), SessionSource::Exec, CollaborationModesConfig::default(), - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), /*analytics_events_client*/ None, ); @@ -630,9 +622,7 @@ async fn interrupted_fork_snapshot_uses_persisted_mid_turn_history_without_live_ auth_manager.clone(), SessionSource::Exec, CollaborationModesConfig::default(), - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), /*analytics_events_client*/ None, ); diff --git a/codex-rs/core/src/unified_exec/mod_tests.rs b/codex-rs/core/src/unified_exec/mod_tests.rs index 9877b2cb9f..1865188d3e 100644 --- a/codex-rs/core/src/unified_exec/mod_tests.rs +++ b/codex-rs/core/src/unified_exec/mod_tests.rs @@ -508,7 +508,7 @@ async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> { shell_env(), ); - let environment = codex_exec_server::Environment::default(); + let environment = codex_exec_server::Environment::default_for_tests(); let process = UnifiedExecProcessManager::default() .open_session_with_exec_env( /*process_id*/ 1234, diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 67d1a49968..73219423b5 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -76,7 +76,8 @@ impl TestEnv { pub async fn local() -> Result { let local_cwd_temp_dir = Arc::new(TempDir::new()?); let cwd = local_cwd_temp_dir.abs(); - let environment = codex_exec_server::Environment::create(/*exec_server_url*/ None)?; + let environment = + codex_exec_server::Environment::create_for_tests(/*exec_server_url*/ None)?; Ok(Self { environment, cwd, @@ -115,7 +116,8 @@ pub async fn test_env() -> Result { match get_remote_test_env() { Some(remote_env) => { let websocket_url = remote_exec_server_url()?; - let environment = codex_exec_server::Environment::create(Some(websocket_url))?; + let environment = + codex_exec_server::Environment::create_for_tests(Some(websocket_url))?; let cwd = remote_aware_cwd_path(); environment .get_filesystem() @@ -363,7 +365,10 @@ impl TestCodexBuilder { let environment_manager = Arc::new(codex_exec_server::EnvironmentManager::new( codex_exec_server::EnvironmentManagerArgs { exec_server_url, - local_runtime_paths: None, + local_runtime_paths: codex_exec_server::ExecServerRuntimePaths::new( + std::env::current_exe()?, + /*codex_linux_sandbox_exe*/ None, + )?, }, )); let file_system = test_env.environment().get_filesystem(); diff --git a/codex-rs/core/tests/suite/client.rs b/codex-rs/core/tests/suite/client.rs index ae8fbc1024..2ebd49d53e 100644 --- a/codex-rs/core/tests/suite/client.rs +++ b/codex-rs/core/tests/suite/client.rs @@ -1103,9 +1103,7 @@ async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens() { .features .enabled(Feature::DefaultModeRequestUserInput), }, - Arc::new(codex_exec_server::EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()), /*analytics_events_client*/ None, ); let NewThread { thread: codex, .. } = thread_manager diff --git a/codex-rs/core/tests/suite/skills.rs b/codex-rs/core/tests/suite/skills.rs index 090f7a5790..59d28b61fc 100644 --- a/codex-rs/core/tests/suite/skills.rs +++ b/codex-rs/core/tests/suite/skills.rs @@ -5,6 +5,7 @@ use anyhow::Result; use codex_core::ThreadManager; use codex_exec_server::CreateDirectoryOptions; use codex_exec_server::EnvironmentManager; +use codex_exec_server::ExecServerRuntimePaths; use codex_exec_server::ExecutorFileSystem; use codex_login::CodexAuth; use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig; @@ -237,7 +238,10 @@ async fn list_skills_skips_cwd_roots_when_environment_disabled() -> Result<()> { Arc::new(EnvironmentManager::new( codex_exec_server::EnvironmentManagerArgs { exec_server_url: Some("none".to_string()), - local_runtime_paths: None, + local_runtime_paths: ExecServerRuntimePaths::new( + std::env::current_exe()?, + /*codex_linux_sandbox_exe*/ None, + )?, }, )), /*analytics_events_client*/ None, diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index 2182ad9197..9cc18ea7be 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -42,51 +42,40 @@ pub const REMOTE_ENVIRONMENT_ID: &str = "remote"; #[derive(Clone, Debug)] pub struct EnvironmentManagerArgs { pub exec_server_url: Option, - pub local_runtime_paths: Option, -} - -impl Default for EnvironmentManagerArgs { - fn default() -> Self { - Self { - exec_server_url: std::env::var(CODEX_EXEC_SERVER_URL_ENV_VAR).ok(), - local_runtime_paths: None, - } - } + pub local_runtime_paths: ExecServerRuntimePaths, } impl EnvironmentManagerArgs { - pub fn default_for_tests() -> Self { + pub fn new(local_runtime_paths: ExecServerRuntimePaths) -> Self { Self { exec_server_url: None, - local_runtime_paths: None, + local_runtime_paths, } } -} -impl From> for EnvironmentManagerArgs { - fn from(exec_server_url: Option) -> Self { + pub fn from_env(local_runtime_paths: ExecServerRuntimePaths) -> Self { Self { - exec_server_url, - local_runtime_paths: None, + exec_server_url: std::env::var(CODEX_EXEC_SERVER_URL_ENV_VAR).ok(), + local_runtime_paths, } } } -impl Default for EnvironmentManager { - fn default() -> Self { - Self::new(EnvironmentManagerArgs::default()) - } -} - impl EnvironmentManager { + /// Builds a test-only manager without configured sandbox helper paths. pub fn default_for_tests() -> Self { - Self::new(EnvironmentManagerArgs::default_for_tests()) + Self { + default_environment: Some(LOCAL_ENVIRONMENT_ID.to_string()), + environments: HashMap::from([( + LOCAL_ENVIRONMENT_ID.to_string(), + Arc::new(Environment::default_for_tests()), + )]), + } } /// Builds a manager from the raw `CODEX_EXEC_SERVER_URL` value and local /// runtime paths used when creating local filesystem helpers. - pub fn new(exec_server_url: impl Into) -> Self { - let args = exec_server_url.into(); + pub fn new(args: EnvironmentManagerArgs) -> Self { let EnvironmentManagerArgs { exec_server_url, local_runtime_paths, @@ -94,9 +83,7 @@ impl EnvironmentManager { let (exec_server_url, environment_disabled) = normalize_exec_server_url(exec_server_url); let mut environments = HashMap::from([( LOCAL_ENVIRONMENT_ID.to_string(), - Arc::new(Environment::local_with_runtime_paths( - local_runtime_paths.clone(), - )), + Arc::new(Environment::local(local_runtime_paths.clone())), )]); let default_environment = if environment_disabled { None @@ -105,10 +92,7 @@ impl EnvironmentManager { Some(exec_server_url) => { environments.insert( REMOTE_ENVIRONMENT_ID.to_string(), - Arc::new(Environment::remote_with_runtime_paths( - exec_server_url, - local_runtime_paths, - )), + Arc::new(Environment::remote(exec_server_url, local_runtime_paths)), ); Some(REMOTE_ENVIRONMENT_ID.to_string()) } @@ -129,6 +113,12 @@ impl EnvironmentManager { .and_then(|environment_id| self.get_environment(environment_id)) } + /// Returns the local environment instance used for internal runtime work. + pub fn local_environment(&self) -> Arc { + self.get_environment(LOCAL_ENVIRONMENT_ID) + .expect("EnvironmentManager always has a local environment") + } + /// Returns a named environment instance. pub fn get_environment(&self, environment_id: &str) -> Option> { self.environments.get(environment_id).cloned() @@ -147,8 +137,9 @@ pub struct Environment { local_runtime_paths: Option, } -impl Default for Environment { - fn default() -> Self { +impl Environment { + /// Builds a test-only local environment without configured sandbox helper paths. + pub fn default_for_tests() -> Self { Self { exec_server_url: None, exec_backend: Arc::new(LocalProcess::default()), @@ -168,13 +159,21 @@ impl std::fmt::Debug for Environment { impl Environment { /// Builds an environment from the raw `CODEX_EXEC_SERVER_URL` value. - pub fn create(exec_server_url: Option) -> Result { - Self::create_with_runtime_paths(exec_server_url, /*local_runtime_paths*/ None) + pub fn create( + exec_server_url: Option, + local_runtime_paths: ExecServerRuntimePaths, + ) -> Result { + Self::create_inner(exec_server_url, Some(local_runtime_paths)) + } + + /// Builds a test-only environment without configured sandbox helper paths. + pub fn create_for_tests(exec_server_url: Option) -> Result { + Self::create_inner(exec_server_url, /*local_runtime_paths*/ None) } /// Builds an environment from the raw `CODEX_EXEC_SERVER_URL` value and /// local runtime paths used when creating local filesystem helpers. - pub(crate) fn create_with_runtime_paths( + fn create_inner( exec_server_url: Option, local_runtime_paths: Option, ) -> Result { @@ -186,28 +185,30 @@ impl Environment { } Ok(match exec_server_url { - Some(exec_server_url) => { - Self::remote_with_runtime_paths(exec_server_url, local_runtime_paths) - } - None => Self::local_with_runtime_paths(local_runtime_paths), + Some(exec_server_url) => Self::remote_inner(exec_server_url, local_runtime_paths), + None => match local_runtime_paths { + Some(local_runtime_paths) => Self::local(local_runtime_paths), + None => Self::default_for_tests(), + }, }) } - fn local_with_runtime_paths(local_runtime_paths: Option) -> Self { - let filesystem: Arc = match local_runtime_paths.clone() { - Some(runtime_paths) => Arc::new(LocalFileSystem::with_runtime_paths(runtime_paths)), - None => Arc::new(LocalFileSystem::unsandboxed()), - }; - + fn local(local_runtime_paths: ExecServerRuntimePaths) -> Self { Self { exec_server_url: None, exec_backend: Arc::new(LocalProcess::default()), - filesystem, - local_runtime_paths, + filesystem: Arc::new(LocalFileSystem::with_runtime_paths( + local_runtime_paths.clone(), + )), + local_runtime_paths: Some(local_runtime_paths), } } - fn remote_with_runtime_paths( + fn remote(exec_server_url: String, local_runtime_paths: ExecServerRuntimePaths) -> Self { + Self::remote_inner(exec_server_url, Some(local_runtime_paths)) + } + + fn remote_inner( exec_server_url: String, local_runtime_paths: Option, ) -> Self { @@ -265,10 +266,18 @@ mod tests { use crate::ProcessId; use pretty_assertions::assert_eq; + fn test_runtime_paths() -> ExecServerRuntimePaths { + ExecServerRuntimePaths::new( + std::env::current_exe().expect("current exe"), + /*codex_linux_sandbox_exe*/ None, + ) + .expect("runtime paths") + } + #[tokio::test] async fn create_local_environment_does_not_connect() { - let environment = - Environment::create(/*exec_server_url*/ None).expect("create environment"); + let environment = Environment::create(/*exec_server_url*/ None, test_runtime_paths()) + .expect("create environment"); assert_eq!(environment.exec_server_url(), None); assert!(!environment.is_remote()); @@ -278,7 +287,7 @@ mod tests { async fn environment_manager_normalizes_empty_url() { let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: Some(String::new()), - local_runtime_paths: None, + local_runtime_paths: test_runtime_paths(), }); let environment = manager.default_environment().expect("default environment"); @@ -296,7 +305,7 @@ mod tests { async fn environment_manager_treats_none_value_as_disabled() { let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: Some("none".to_string()), - local_runtime_paths: None, + local_runtime_paths: test_runtime_paths(), }); assert!(manager.default_environment().is_none()); @@ -313,7 +322,7 @@ mod tests { async fn environment_manager_reports_remote_url() { let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: Some("ws://127.0.0.1:8765".to_string()), - local_runtime_paths: None, + local_runtime_paths: test_runtime_paths(), }); let environment = manager.default_environment().expect("default environment"); @@ -335,7 +344,7 @@ mod tests { #[tokio::test] async fn environment_manager_default_environment_caches_environment() { - let manager = EnvironmentManager::new(EnvironmentManagerArgs::default_for_tests()); + let manager = EnvironmentManager::default_for_tests(); let first = manager.default_environment().expect("default environment"); let second = manager.default_environment().expect("default environment"); @@ -349,14 +358,10 @@ mod tests { #[tokio::test] async fn environment_manager_carries_local_runtime_paths() { - let runtime_paths = ExecServerRuntimePaths::new( - std::env::current_exe().expect("current exe"), - /*codex_linux_sandbox_exe*/ None, - ) - .expect("runtime paths"); + let runtime_paths = test_runtime_paths(); let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: None, - local_runtime_paths: Some(runtime_paths.clone()), + local_runtime_paths: runtime_paths.clone(), }); let environment = manager.default_environment().expect("default environment"); @@ -364,7 +369,10 @@ mod tests { assert_eq!(environment.local_runtime_paths(), Some(&runtime_paths)); let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: environment.exec_server_url().map(str::to_owned), - local_runtime_paths: environment.local_runtime_paths().cloned(), + local_runtime_paths: environment + .local_runtime_paths() + .expect("local runtime paths") + .clone(), }); let environment = manager.default_environment().expect("default environment"); assert_eq!(environment.local_runtime_paths(), Some(&runtime_paths)); @@ -374,7 +382,7 @@ mod tests { async fn disabled_environment_manager_has_no_default_environment() { let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: Some("none".to_string()), - local_runtime_paths: None, + local_runtime_paths: test_runtime_paths(), }); assert!(manager.default_environment().is_none()); @@ -384,7 +392,7 @@ mod tests { async fn environment_manager_keeps_local_lookup_when_default_disabled() { let manager = EnvironmentManager::new(EnvironmentManagerArgs { exec_server_url: Some("none".to_string()), - local_runtime_paths: None, + local_runtime_paths: test_runtime_paths(), }); assert!(manager.default_environment().is_none()); @@ -399,14 +407,14 @@ mod tests { #[tokio::test] async fn get_environment_returns_none_for_unknown_id() { - let manager = EnvironmentManager::new(EnvironmentManagerArgs::default_for_tests()); + let manager = EnvironmentManager::default_for_tests(); assert!(manager.get_environment("does-not-exist").is_none()); } #[tokio::test] async fn default_environment_has_ready_local_executor() { - let environment = Environment::default(); + let environment = Environment::default_for_tests(); let response = environment .get_exec_backend() @@ -425,4 +433,27 @@ mod tests { assert_eq!(response.process.process_id().as_str(), "default-env-proc"); } + + #[tokio::test] + async fn test_environment_rejects_sandboxed_filesystem_without_runtime_paths() { + let environment = Environment::default_for_tests(); + let path = codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path( + std::env::current_exe().expect("current exe").as_path(), + ) + .expect("absolute current exe"); + let sandbox = crate::FileSystemSandboxContext::new( + codex_protocol::protocol::SandboxPolicy::new_read_only_policy(), + ); + + let err = environment + .get_filesystem() + .read_file(&path, Some(&sandbox)) + .await + .expect_err("sandboxed read should require runtime paths"); + + assert_eq!( + err.to_string(), + "sandboxed filesystem operations require configured runtime paths" + ); + } } diff --git a/codex-rs/exec-server/tests/exec_process.rs b/codex-rs/exec-server/tests/exec_process.rs index 4887a0be4d..9972cc004a 100644 --- a/codex-rs/exec-server/tests/exec_process.rs +++ b/codex-rs/exec-server/tests/exec_process.rs @@ -49,13 +49,13 @@ enum ProcessEventSnapshot { async fn create_process_context(use_remote: bool) -> Result { if use_remote { let server = exec_server().await?; - let environment = Environment::create(Some(server.websocket_url().to_string()))?; + let environment = Environment::create_for_tests(Some(server.websocket_url().to_string()))?; Ok(ProcessContext { backend: environment.get_exec_backend(), server: Some(server), }) } else { - let environment = Environment::create(/*exec_server_url*/ None)?; + let environment = Environment::create_for_tests(/*exec_server_url*/ None)?; Ok(ProcessContext { backend: environment.get_exec_backend(), server: None, diff --git a/codex-rs/exec-server/tests/file_system.rs b/codex-rs/exec-server/tests/file_system.rs index 4bb654198f..f137be9695 100644 --- a/codex-rs/exec-server/tests/file_system.rs +++ b/codex-rs/exec-server/tests/file_system.rs @@ -46,7 +46,7 @@ struct FileSystemContext { async fn create_file_system_context(use_remote: bool) -> Result { if use_remote { let server = exec_server().await?; - let environment = Environment::create(Some(server.websocket_url().to_string()))?; + let environment = Environment::create_for_tests(Some(server.websocket_url().to_string()))?; Ok(FileSystemContext { file_system: environment.get_filesystem(), _helper_paths: None, @@ -214,7 +214,7 @@ async fn sandboxed_file_system_helper_finds_bwrap_on_preserved_path() -> Result< let helper_path = std::env::join_paths(path_entries)?; let server = exec_server_with_env([("PATH", helper_path.as_os_str())]).await?; - let environment = Environment::create(Some(server.websocket_url().to_string()))?; + let environment = Environment::create_for_tests(Some(server.websocket_url().to_string()))?; let file_system = environment.get_filesystem(); let workspace = tmp.path().join("workspace"); std::fs::create_dir_all(&workspace)?; diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 1404348af1..1279532daf 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -498,10 +498,9 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result cloud_requirements: run_cloud_requirements, feedback: CodexFeedback::new(), log_db: None, - environment_manager: std::sync::Arc::new(EnvironmentManager::new(EnvironmentManagerArgs { - local_runtime_paths: Some(local_runtime_paths), - ..EnvironmentManagerArgs::default() - })), + environment_manager: std::sync::Arc::new(EnvironmentManager::new( + EnvironmentManagerArgs::from_env(local_runtime_paths), + )), config_warnings, session_source: SessionSource::Exec, enable_codex_api_key_env: true, diff --git a/codex-rs/mcp-server/src/lib.rs b/codex-rs/mcp-server/src/lib.rs index 7577818fc4..1d904e4577 100644 --- a/codex-rs/mcp-server/src/lib.rs +++ b/codex-rs/mcp-server/src/lib.rs @@ -60,13 +60,12 @@ pub async fn run_main( arg0_paths: Arg0DispatchPaths, cli_config_overrides: CliConfigOverrides, ) -> IoResult<()> { - let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs { - local_runtime_paths: Some(ExecServerRuntimePaths::from_optional_paths( + let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::from_env( + ExecServerRuntimePaths::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), - )?), - ..EnvironmentManagerArgs::default() - })); + )?, + ))); // Parse CLI overrides once and derive the base Config eagerly so later // components do not need to work with raw TOML values. let cli_kv_overrides = cli_config_overrides.parse_overrides().map_err(|e| { diff --git a/codex-rs/tui/src/app/test_support.rs b/codex-rs/tui/src/app/test_support.rs index a48739dedc..4dc724ee5e 100644 --- a/codex-rs/tui/src/app/test_support.rs +++ b/codex-rs/tui/src/app/test_support.rs @@ -38,9 +38,7 @@ pub(super) async fn make_test_app() -> App { backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), feedback_audience: FeedbackAudience::External, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), remote_app_server_url: None, remote_app_server_auth_token: None, pending_update_action: None, diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index 5237ce7d93..c498d3d41d 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -3576,9 +3576,7 @@ async fn make_test_app() -> App { backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), feedback_audience: FeedbackAudience::External, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), remote_app_server_url: None, remote_app_server_auth_token: None, pending_update_action: None, @@ -3635,9 +3633,7 @@ async fn make_test_app_with_channels() -> ( backtrack_render_pending: false, feedback: codex_feedback::CodexFeedback::new(), feedback_audience: FeedbackAudience::External, - environment_manager: Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new(EnvironmentManager::default_for_tests()), remote_app_server_url: None, remote_app_server_auth_token: None, pending_update_action: None, diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 930adeb57d..7e33f2e8a3 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -426,9 +426,7 @@ pub(crate) async fn start_embedded_app_server_for_picker( start_app_server_for_picker( config, &AppServerTarget::Embedded, - Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(EnvironmentManager::default_for_tests()), ) .await } @@ -731,13 +729,12 @@ pub async fn run_main( } }; - let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs { - local_runtime_paths: Some(ExecServerRuntimePaths::from_optional_paths( + let environment_manager = Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::from_env( + ExecServerRuntimePaths::from_optional_paths( arg0_paths.codex_self_exe.clone(), arg0_paths.codex_linux_sandbox_exe.clone(), - )?), - ..EnvironmentManagerArgs::default() - })); + )?, + ))); let cwd = cli.cwd.clone(); let config_cwd = config_cwd_for_app_server_target(cwd.as_deref(), &app_server_target, &environment_manager)?; @@ -1777,9 +1774,7 @@ mod tests { CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), /*log_db*/ None, - Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(EnvironmentManager::default_for_tests()), ) .await } @@ -1939,8 +1934,7 @@ mod tests { websocket_url: "ws://127.0.0.1:1234/".to_string(), auth_token: None, }; - let environment_manager = - EnvironmentManager::new(codex_exec_server::EnvironmentManagerArgs::default_for_tests()); + let environment_manager = EnvironmentManager::default_for_tests(); let config_cwd = config_cwd_for_app_server_target(Some(remote_only_cwd), &target, &environment_manager)?; @@ -1954,8 +1948,7 @@ mod tests { { let temp_dir = TempDir::new()?; let target = AppServerTarget::Embedded; - let environment_manager = - EnvironmentManager::new(codex_exec_server::EnvironmentManagerArgs::default_for_tests()); + let environment_manager = EnvironmentManager::default_for_tests(); let config_cwd = config_cwd_for_app_server_target(Some(temp_dir.path()), &target, &environment_manager)?; @@ -1975,8 +1968,7 @@ mod tests { let temp_dir = TempDir::new()?; let missing = temp_dir.path().join("missing"); let target = AppServerTarget::Embedded; - let environment_manager = - EnvironmentManager::new(codex_exec_server::EnvironmentManagerArgs::default_for_tests()); + let environment_manager = EnvironmentManager::default_for_tests(); let err = config_cwd_for_app_server_target(Some(&missing), &target, &environment_manager) .expect_err("missing embedded cwd should fail"); @@ -1997,7 +1989,10 @@ mod tests { let environment_manager = EnvironmentManager::new(codex_exec_server::EnvironmentManagerArgs { exec_server_url: Some("ws://127.0.0.1:8765".to_string()), - local_runtime_paths: None, + local_runtime_paths: ExecServerRuntimePaths::new( + std::env::current_exe().expect("current exe"), + /*codex_linux_sandbox_exe*/ None, + )?, }); let config_cwd = @@ -2125,9 +2120,7 @@ mod tests { CloudRequirementsLoader::default(), codex_feedback::CodexFeedback::new(), /*log_db*/ None, - Arc::new(EnvironmentManager::new( - codex_exec_server::EnvironmentManagerArgs::default_for_tests(), - )), + Arc::new(EnvironmentManager::default_for_tests()), |_args| async { Err(std::io::Error::other("boom")) }, ) .await; diff --git a/codex-rs/tui/src/onboarding/auth.rs b/codex-rs/tui/src/onboarding/auth.rs index 380f13580a..1e55b5c5d2 100644 --- a/codex-rs/tui/src/onboarding/auth.rs +++ b/codex-rs/tui/src/onboarding/auth.rs @@ -989,9 +989,9 @@ mod tests { ), feedback: codex_feedback::CodexFeedback::new(), log_db: None, - environment_manager: Arc::new(codex_app_server_client::EnvironmentManager::new( - codex_app_server_client::EnvironmentManagerArgs::default_for_tests(), - )), + environment_manager: Arc::new( + codex_app_server_client::EnvironmentManager::default_for_tests(), + ), config_warnings: Vec::new(), session_source: SessionSource::Cli, enable_codex_api_key_env: false,