Compare commits

...

2 Commits

Author SHA1 Message Date
iceweasel-oai
e3c3ee4eca fix Windows pseudoconsole attribute handle 2026-04-28 10:29:12 -07:00
iceweasel-oai
f4976eeac1 Allow manual unified_exec opt-in on Windows 2026-04-24 13:20:23 -07:00
4 changed files with 31 additions and 74 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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]

View File

@@ -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::<isize>(),
hpc as *mut c_void,
std::mem::size_of::<HANDLE>(),
std::ptr::null_mut(),
std::ptr::null_mut(),
)