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 <kbond@openai.com>
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
iceweasel-oai
2026-04-29 16:06:33 -07:00
committed by GitHub
Unverified
parent 8de2a7a16d
commit 13dbcda28f
3 changed files with 54 additions and 92 deletions
-25
View File
@@ -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;
+10 -29
View File
@@ -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;
+44 -38
View File
@@ -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]