From 13dbcda28fba739ea7aeafca9251c2162df53cf5 Mon Sep 17 00:00:00 2001 From: iceweasel-oai Date: Wed, 29 Apr 2026 16:06:33 -0700 Subject: [PATCH] stop blocking unified_exec on Windows (#19435) ## Summary - remove the Windows-specific unified-exec environment block from tool selection - keep `unified_exec` default-off on Windows unless the feature is explicitly enabled - normalize model-provided `shell_type = unified_exec` to `shell_command` when the feature is disabled - drop obsolete tests tied to the removed environment gate and keep the feature-flag regression coverage ## Why Now that the session/long-lived process backend is implemented for the Windows sandbox, we don't need to hard disable it anymore. We will be rolling out slowly using a feature gate. ## Impact This allows manual Windows opt-in in CLI and app-backed flows while preserving the existing default-off behavior for Windows users. --------- Co-authored-by: canvrno-oai Co-authored-by: Codex --- codex-rs/core/src/tools/spec_tests.rs | 25 -------- codex-rs/tools/src/tool_config.rs | 39 +++--------- codex-rs/tools/src/tool_config_tests.rs | 82 +++++++++++++------------ 3 files changed, 54 insertions(+), 92 deletions(-) diff --git a/codex-rs/core/src/tools/spec_tests.rs b/codex-rs/core/src/tools/spec_tests.rs index f3ba9cc48..afa586dc6 100644 --- a/codex-rs/core/src/tools/spec_tests.rs +++ b/codex-rs/core/src/tools/spec_tests.rs @@ -298,31 +298,6 @@ fn build_specs_with_unavailable_tools( ) } -#[tokio::test] -async fn model_provided_unified_exec_is_blocked_for_windows_sandboxed_policies() { - let mut model_info = model_info_from_models_json("gpt-5.4").await; - model_info.shell_type = ConfigShellToolType::UnifiedExec; - let features = Features::with_defaults(); - let available_models = Vec::new(); - let config = ToolsConfig::new(&ToolsConfigParams { - model_info: &model_info, - available_models: &available_models, - features: &features, - image_generation_tool_auth_allowed: true, - web_search_mode: Some(WebSearchMode::Cached), - session_source: SessionSource::Cli, - permission_profile: &PermissionProfile::workspace_write(), - windows_sandbox_level: WindowsSandboxLevel::RestrictedToken, - }); - - let expected_shell_type = if cfg!(target_os = "windows") { - ConfigShellToolType::ShellCommand - } else { - ConfigShellToolType::UnifiedExec - }; - assert_eq!(config.shell_type, expected_shell_type); -} - #[tokio::test] async fn get_memory_requires_feature_flag() { let config = test_config().await; diff --git a/codex-rs/tools/src/tool_config.rs b/codex-rs/tools/src/tool_config.rs index a218a5530..e95d5e2da 100644 --- a/codex-rs/tools/src/tool_config.rs +++ b/codex-rs/tools/src/tool_config.rs @@ -136,8 +136,7 @@ impl ToolsConfig { image_generation_tool_auth_allowed, web_search_mode, session_source, - permission_profile, - windows_sandbox_level, + .. } = params; let include_apply_patch_tool = features.enabled(Feature::ApplyPatchFreeform); let include_code_mode = features.enabled(Feature::CodeMode); @@ -167,26 +166,25 @@ impl ToolsConfig { } else { ShellCommandBackendConfig::Classic }; - let unified_exec_allowed = unified_exec_allowed_in_environment( - cfg!(target_os = "windows"), - permission_profile, - *windows_sandbox_level, - ); + let unified_exec_enabled = features.enabled(Feature::UnifiedExec); + let model_shell_type = match model_info.shell_type { + ConfigShellToolType::UnifiedExec if !unified_exec_enabled => { + ConfigShellToolType::ShellCommand + } + other => other, + }; let shell_type = if !features.enabled(Feature::ShellTool) { ConfigShellToolType::Disabled } else if features.enabled(Feature::ShellZshFork) { ConfigShellToolType::ShellCommand - } else if features.enabled(Feature::UnifiedExec) && unified_exec_allowed { + } else if unified_exec_enabled { if codex_utils_pty::conpty_supported() { ConfigShellToolType::UnifiedExec } else { ConfigShellToolType::ShellCommand } - } else if model_info.shell_type == ConfigShellToolType::UnifiedExec && !unified_exec_allowed - { - ConfigShellToolType::ShellCommand } else { - model_info.shell_type + model_shell_type }; let apply_patch_tool_type = match model_info.apply_patch_tool_type { @@ -353,23 +351,6 @@ fn supports_image_generation(model_info: &ModelInfo) -> bool { model_info.input_modalities.contains(&InputModality::Image) } -fn unified_exec_allowed_in_environment( - is_windows: bool, - permission_profile: &PermissionProfile, - windows_sandbox_level: WindowsSandboxLevel, -) -> bool { - let managed_sandbox_required = match permission_profile { - PermissionProfile::Managed { - file_system, - network, - } => !file_system.to_sandbox_policy().has_full_disk_write_access() || !network.is_enabled(), - PermissionProfile::Disabled | PermissionProfile::External { .. } => false, - }; - !(is_windows - && windows_sandbox_level != WindowsSandboxLevel::Disabled - && managed_sandbox_required) -} - #[cfg(test)] #[path = "tool_config_tests.rs"] mod tests; diff --git a/codex-rs/tools/src/tool_config_tests.rs b/codex-rs/tools/src/tool_config_tests.rs index 79431d58b..77bb76a04 100644 --- a/codex-rs/tools/src/tool_config_tests.rs +++ b/codex-rs/tools/src/tool_config_tests.rs @@ -3,12 +3,10 @@ use codex_features::Feature; use codex_features::Features; use codex_protocol::config_types::WebSearchMode; use codex_protocol::config_types::WindowsSandboxLevel; -use codex_protocol::models::ManagedFileSystemPermissions; use codex_protocol::models::PermissionProfile; use codex_protocol::openai_models::ConfigShellToolType; use codex_protocol::openai_models::InputModality; use codex_protocol::openai_models::ModelInfo; -use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::SessionSource; use codex_protocol::protocol::SubAgentSource; use codex_utils_absolute_path::AbsolutePathBuf; @@ -52,42 +50,50 @@ fn model_info() -> ModelInfo { } #[test] -fn unified_exec_is_blocked_for_windows_managed_profiles_only() { - assert!(!unified_exec_allowed_in_environment( - /*is_windows*/ true, - &PermissionProfile::read_only(), - WindowsSandboxLevel::RestrictedToken, - )); - assert!(!unified_exec_allowed_in_environment( - /*is_windows*/ true, - &PermissionProfile::workspace_write(), - WindowsSandboxLevel::RestrictedToken, - )); - assert!(unified_exec_allowed_in_environment( - /*is_windows*/ true, - &PermissionProfile::Disabled, - WindowsSandboxLevel::RestrictedToken, - )); - assert!(unified_exec_allowed_in_environment( - /*is_windows*/ true, - &PermissionProfile::External { - network: Default::default(), - }, - WindowsSandboxLevel::RestrictedToken, - )); - assert!(unified_exec_allowed_in_environment( - /*is_windows*/ true, - &PermissionProfile::Managed { - file_system: ManagedFileSystemPermissions::Unrestricted, - network: NetworkSandboxPolicy::Enabled, - }, - WindowsSandboxLevel::RestrictedToken, - )); - assert!(unified_exec_allowed_in_environment( - /*is_windows*/ true, - &PermissionProfile::Disabled, - WindowsSandboxLevel::Disabled, - )); +fn model_provided_unified_exec_requires_feature_flag() { + let model_info = model_info(); + let mut features = Features::with_defaults(); + features.disable(Feature::UnifiedExec); + + let available_models = Vec::new(); + let tools_config = ToolsConfig::new(&ToolsConfigParams { + model_info: &model_info, + available_models: &available_models, + features: &features, + image_generation_tool_auth_allowed: true, + web_search_mode: Some(WebSearchMode::Cached), + session_source: SessionSource::Cli, + permission_profile: &PermissionProfile::Disabled, + windows_sandbox_level: WindowsSandboxLevel::Disabled, + }); + + assert_eq!(tools_config.shell_type, ConfigShellToolType::ShellCommand); +} + +#[test] +fn unified_exec_can_be_enabled_for_restricted_token_workspace_write() { + let model_info = model_info(); + let mut features = Features::with_defaults(); + features.enable(Feature::UnifiedExec); + + let available_models = Vec::new(); + let tools_config = ToolsConfig::new(&ToolsConfigParams { + model_info: &model_info, + available_models: &available_models, + features: &features, + image_generation_tool_auth_allowed: true, + web_search_mode: Some(WebSearchMode::Cached), + session_source: SessionSource::Cli, + permission_profile: &PermissionProfile::workspace_write(), + windows_sandbox_level: WindowsSandboxLevel::RestrictedToken, + }); + + let expected_shell_type = if codex_utils_pty::conpty_supported() { + ConfigShellToolType::UnifiedExec + } else { + ConfigShellToolType::ShellCommand + }; + assert_eq!(tools_config.shell_type, expected_shell_type); } #[test]