Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion codex-rs/core/src/tools/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
9 changes: 9 additions & 0 deletions codex-rs/utils/pty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
1 change: 1 addition & 0 deletions codex-rs/utils/pty/src/win/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ mod procthreadattr;
mod psuedocon;

pub use conpty::ConPtySystem;
pub use psuedocon::conpty_supported;

#[derive(Debug)]
pub struct WinChild {
Expand Down
43 changes: 43 additions & 0 deletions codex-rs/utils/pty/src/win/psuedocon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -52,13 +54,18 @@ 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;

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,
Expand All @@ -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",
Expand All @@ -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,
}
Expand Down Expand Up @@ -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);
}
}
Loading