From f4976eeac19b4ae46978b0bb5588d4c02d2fc724 Mon Sep 17 00:00:00 2001 From: iceweasel-oai Date: Fri, 24 Apr 2026 13:20:23 -0700 Subject: [PATCH 1/2] Allow manual unified_exec opt-in on Windows --- codex-rs/core/src/tools/spec_tests.rs | 25 ---------------- codex-rs/tools/src/tool_config.rs | 35 +++++++--------------- codex-rs/tools/src/tool_config_tests.rs | 39 ++++++++++++------------- 3 files changed, 28 insertions(+), 71 deletions(-) diff --git a/codex-rs/core/src/tools/spec_tests.rs b/codex-rs/core/src/tools/spec_tests.rs index a1e685df75a5..29596ddfe74d 100644 --- a/codex-rs/core/src/tools/spec_tests.rs +++ b/codex-rs/core/src/tools/spec_tests.rs @@ -295,31 +295,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, - sandbox_policy: &SandboxPolicy::new_workspace_write_policy(), - 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 20c812ce9629..9e45eeed3907 100644 --- a/codex-rs/tools/src/tool_config.rs +++ b/codex-rs/tools/src/tool_config.rs @@ -134,8 +134,7 @@ impl ToolsConfig { image_generation_tool_auth_allowed, web_search_mode, session_source, - sandbox_policy, - 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"), - sandbox_policy, - *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 { @@ -309,19 +307,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, - sandbox_policy: &SandboxPolicy, - windows_sandbox_level: WindowsSandboxLevel, -) -> bool { - !(is_windows - && windows_sandbox_level != WindowsSandboxLevel::Disabled - && !matches!( - sandbox_policy, - SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } - )) -} - #[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 25c78a053db6..a5901bff84db 100644 --- a/codex-rs/tools/src/tool_config_tests.rs +++ b/codex-rs/tools/src/tool_config_tests.rs @@ -50,27 +50,24 @@ fn model_info() -> ModelInfo { } #[test] -fn unified_exec_is_blocked_for_windows_sandboxed_policies_only() { - assert!(!unified_exec_allowed_in_environment( - /*is_windows*/ true, - &SandboxPolicy::new_read_only_policy(), - WindowsSandboxLevel::RestrictedToken, - )); - assert!(!unified_exec_allowed_in_environment( - /*is_windows*/ true, - &SandboxPolicy::new_workspace_write_policy(), - WindowsSandboxLevel::RestrictedToken, - )); - assert!(unified_exec_allowed_in_environment( - /*is_windows*/ true, - &SandboxPolicy::DangerFullAccess, - WindowsSandboxLevel::RestrictedToken, - )); - assert!(unified_exec_allowed_in_environment( - /*is_windows*/ true, - &SandboxPolicy::DangerFullAccess, - 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, + sandbox_policy: &SandboxPolicy::DangerFullAccess, + windows_sandbox_level: WindowsSandboxLevel::Disabled, + }); + + assert_eq!(tools_config.shell_type, ConfigShellToolType::ShellCommand); } #[test] From e3c3ee4eca6a2b581af606e5f359eb793dbe526f Mon Sep 17 00:00:00 2001 From: iceweasel-oai Date: Tue, 28 Apr 2026 10:29:12 -0700 Subject: [PATCH 2/2] fix Windows pseudoconsole attribute handle --- codex-rs/windows-sandbox-rs/src/proc_thread_attr.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codex-rs/windows-sandbox-rs/src/proc_thread_attr.rs b/codex-rs/windows-sandbox-rs/src/proc_thread_attr.rs index 7641bb5edd4a..d05628a975a4 100644 --- a/codex-rs/windows-sandbox-rs/src/proc_thread_attr.rs +++ b/codex-rs/windows-sandbox-rs/src/proc_thread_attr.rs @@ -1,4 +1,5 @@ use std::io; +use std::ffi::c_void; use windows_sys::Win32::Foundation::GetLastError; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::System::Threading::DeleteProcThreadAttributeList; @@ -45,14 +46,13 @@ impl ProcThreadAttributeList { pub fn set_pseudoconsole(&mut self, hpc: isize) -> io::Result<()> { let list = self.as_mut_ptr(); - let mut hpc_value = hpc; let ok = unsafe { UpdateProcThreadAttribute( list, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, - (&mut hpc_value as *mut isize).cast(), - std::mem::size_of::(), + hpc as *mut c_void, + std::mem::size_of::(), std::ptr::null_mut(), std::ptr::null_mut(), )