feat: fallback unified_exec to shell_command (#8075)

This commit is contained in:
jif-oai
2025-12-17 10:29:45 +00:00
committed by GitHub
parent 4897efcced
commit 813bdb9010
4 changed files with 59 additions and 1 deletions

View File

@@ -43,7 +43,12 @@ impl ToolsConfig {
let shell_type = if !features.enabled(Feature::ShellTool) {
ConfigShellToolType::Disabled
} else if features.enabled(Feature::UnifiedExec) {
ConfigShellToolType::UnifiedExec
// If ConPTY not supported (for old Windows versions), fallback on ShellCommand.
if codex_utils_pty::conpty_supported() {
ConfigShellToolType::UnifiedExec
} else {
ConfigShellToolType::ShellCommand
}
} else {
model_family.shell_type
};

View File

@@ -133,6 +133,15 @@ pub struct SpawnedPty {
pub exit_rx: oneshot::Receiver<i32>,
}
#[allow(unreachable_code)]
pub fn conpty_supported() -> bool {
// Annotation required because `win` can't be compiled on other OS.
#[cfg(windows)]
return win::conpty_supported();
true
}
#[cfg(windows)]
fn platform_native_pty_system() -> Box<dyn portable_pty::PtySystem + Send> {
Box::new(win::ConPtySystem::default())

View File

@@ -41,6 +41,7 @@ mod procthreadattr;
mod psuedocon;
pub use conpty::ConPtySystem;
pub use psuedocon::conpty_supported;
#[derive(Debug)]
pub struct WinChild {

View File

@@ -42,6 +42,8 @@ use std::path::Path;
use std::ptr;
use std::sync::Mutex;
use winapi::shared::minwindef::DWORD;
use winapi::shared::ntdef::NTSTATUS;
use winapi::shared::ntstatus::STATUS_SUCCESS;
use winapi::shared::winerror::HRESULT;
use winapi::shared::winerror::S_OK;
use winapi::um::handleapi::*;
@@ -52,6 +54,7 @@ use winapi::um::winbase::STARTF_USESTDHANDLES;
use winapi::um::winbase::STARTUPINFOEXW;
use winapi::um::wincon::COORD;
use winapi::um::winnt::HANDLE;
use winapi::um::winnt::OSVERSIONINFOW;
pub type HPCON = HANDLE;
@@ -59,6 +62,10 @@ pub const PSEUDOCONSOLE_RESIZE_QUIRK: DWORD = 0x2;
#[allow(dead_code)]
pub const PSEUDOCONSOLE_PASSTHROUGH_MODE: DWORD = 0x8;
// https://learn.microsoft.com/en-gb/windows/console/createpseudoconsole
// https://learn.microsoft.com/en-gb/windows/release-health/release-information
const MIN_CONPTY_BUILD: u32 = 17_763;
shared_library!(ConPtyFuncs,
pub fn CreatePseudoConsole(
size: COORD,
@@ -71,6 +78,12 @@ shared_library!(ConPtyFuncs,
pub fn ClosePseudoConsole(hpc: HPCON),
);
shared_library!(Ntdll,
pub fn RtlGetVersion(
version_info: *mut OSVERSIONINFOW
) -> NTSTATUS,
);
fn load_conpty() -> ConPtyFuncs {
let kernel = ConPtyFuncs::open(Path::new("kernel32.dll")).expect(
"this system does not support conpty. Windows 10 October 2018 or newer is required",
@@ -87,6 +100,22 @@ lazy_static! {
static ref CONPTY: ConPtyFuncs = load_conpty();
}
pub fn conpty_supported() -> bool {
windows_build_number().is_some_and(|build| build >= MIN_CONPTY_BUILD)
}
fn windows_build_number() -> Option<u32> {
let ntdll = Ntdll::open(Path::new("ntdll.dll")).ok()?;
let mut info: OSVERSIONINFOW = unsafe { mem::zeroed() };
info.dwOSVersionInfoSize = mem::size_of::<OSVERSIONINFOW>() as u32;
let status = unsafe { (ntdll.RtlGetVersion)(&mut info) };
if status == STATUS_SUCCESS {
Some(info.dwBuildNumber)
} else {
None
}
}
pub struct PsuedoCon {
con: HPCON,
}
@@ -320,3 +349,17 @@ fn append_quoted(arg: &OsStr, cmdline: &mut Vec<u16>) {
}
cmdline.push('"' as u16);
}
#[cfg(test)]
mod tests {
use super::windows_build_number;
use super::MIN_CONPTY_BUILD;
#[test]
fn windows_build_number_returns_value() {
// We can't stably check the version of the GH workers, but we can
// at least check that this.
let version = windows_build_number().unwrap();
assert!(version > MIN_CONPTY_BUILD);
}
}