From 729bdf3c8d4995c10491debba476a9a3068ac183 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Wed, 20 May 2026 12:41:06 -0700 Subject: [PATCH] windows-sandbox: send permission profiles to elevated runner (#22918) ## Why This is the next PR in the Windows sandbox migration stack after #22896. The bottom PR introduces a Windows-local resolved permissions helper while existing callers still start from legacy `SandboxPolicy`. This PR moves the elevated runner IPC boundary to `PermissionProfile`, which makes the direction of the stack visible without changing the public core call sites yet. Because that changes the CLI-to-command-runner message shape, the framed IPC protocol version is bumped in the same PR so the boundary change is explicit. ## What changed - Replaced elevated IPC `policy_json_or_preset`/`sandbox_policy_cwd` fields with `permission_profile`/`permission_profile_cwd`. - Bumped the elevated command-runner IPC protocol to `IPC_PROTOCOL_VERSION = 2` and switched parent/runner frames to use the shared constant. - Converted the parent elevated paths from the parsed legacy policy into a materialized `PermissionProfile` before sending the runner request. - Added `WindowsSandboxTokenMode` resolution for managed `PermissionProfile` values and made the runner choose read-only vs writable-root capability tokens from that resolved profile. - Rejected disabled, external, unrestricted, and full-disk-write profiles before token selection. - Added IPC JSON coverage for tagged `PermissionProfile` payloads and token-mode unit coverage for the resolved permission helper. ## Verification - `cargo test -p codex-windows-sandbox` - `just fix -p codex-windows-sandbox` - `cargo check -p codex-windows-sandbox --target x86_64-pc-windows-msvc --tests` was attempted locally but blocked before crate type-checking because the macOS compiler environment lacks Windows C headers such as `windows.h` and `assert.h`; GitHub Windows CI is the required verification for the runner path. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22918). * #23715 * #23714 * #23167 * #22923 * __->__ #22918 --- .../src/bin/command_runner/win.rs | 31 +-- .../src/elevated/ipc_framed.rs | 52 ++++- .../src/elevated/runner_client.rs | 3 +- .../windows-sandbox-rs/src/elevated_impl.rs | 7 +- codex-rs/windows-sandbox-rs/src/lib.rs | 6 + .../src/resolved_permissions.rs | 182 ++++++++++++++++++ .../src/unified_exec/backends/elevated.rs | 12 +- .../unified_exec/backends/windows_common.rs | 7 +- 8 files changed, 273 insertions(+), 27 deletions(-) diff --git a/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs b/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs index b161dd57b8..afdbdd5813 100644 --- a/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs +++ b/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs @@ -16,17 +16,18 @@ use anyhow::Result; use codex_windows_sandbox::ErrorPayload; use codex_windows_sandbox::ExitPayload; use codex_windows_sandbox::FramedMessage; +use codex_windows_sandbox::IPC_PROTOCOL_VERSION; use codex_windows_sandbox::LocalSid; use codex_windows_sandbox::Message; use codex_windows_sandbox::OutputPayload; use codex_windows_sandbox::OutputStream; use codex_windows_sandbox::PipeSpawnHandles; use codex_windows_sandbox::ResizePayload; -use codex_windows_sandbox::SandboxPolicy; use codex_windows_sandbox::SpawnReady; use codex_windows_sandbox::SpawnRequest; use codex_windows_sandbox::StderrMode; use codex_windows_sandbox::StdinMode; +use codex_windows_sandbox::WindowsSandboxTokenMode; use codex_windows_sandbox::allow_null_device; use codex_windows_sandbox::create_readonly_token_with_caps_and_user_from; use codex_windows_sandbox::create_workspace_write_token_with_caps_and_user_from; @@ -35,11 +36,11 @@ use codex_windows_sandbox::encode_bytes; use codex_windows_sandbox::get_current_token_for_restriction; use codex_windows_sandbox::hide_current_user_profile_dir; use codex_windows_sandbox::log_note; -use codex_windows_sandbox::parse_policy; use codex_windows_sandbox::read_frame; use codex_windows_sandbox::read_handle_loop; use codex_windows_sandbox::spawn_process_with_pipes; use codex_windows_sandbox::to_wide; +use codex_windows_sandbox::token_mode_for_permission_profile; use codex_windows_sandbox::write_frame; use std::ffi::OsStr; use std::fs::File; @@ -167,7 +168,7 @@ fn open_pipe(name: &str, access: u32) -> Result { /// Send an error frame back to the parent process. fn send_error(writer: &Arc>, code: &str, message: String) -> Result<()> { let msg = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Error { payload: ErrorPayload { message, @@ -186,7 +187,7 @@ fn read_spawn_request(reader: &mut File) -> Result { let Some(msg) = read_frame(reader)? else { anyhow::bail!("runner: pipe closed before spawn_request"); }; - if msg.version != 1 { + if msg.version != IPC_PROTOCOL_VERSION { anyhow::bail!("runner: unsupported protocol version {}", msg.version); } match msg.message { @@ -235,7 +236,12 @@ fn effective_cwd(req_cwd: &Path, log_dir: Option<&Path>) -> PathBuf { fn spawn_ipc_process(req: &SpawnRequest) -> Result { let log_dir = req.codex_home.clone(); hide_current_user_profile_dir(req.codex_home.as_path()); - let policy = parse_policy(&req.policy_json_or_preset).context("parse policy_json_or_preset")?; + let token_mode = token_mode_for_permission_profile( + &req.permission_profile, + &req.permission_profile_cwd, + &req.env, + ) + .context("resolve permission profile token mode")?; let mut cap_psids: Vec = Vec::new(); for sid in &req.cap_sids { cap_psids.push( @@ -253,16 +259,13 @@ fn spawn_ipc_process(req: &SpawnRequest) -> Result { let cap_psid_ptrs: Vec<*mut _> = cap_psids.iter().map(LocalSid::as_ptr).collect(); let base = OwnedWinHandle::new(unsafe { get_current_token_for_restriction()? }); let h_token = OwnedWinHandle::new(unsafe { - match &policy { - SandboxPolicy::ReadOnly { .. } => { + match token_mode { + WindowsSandboxTokenMode::ReadOnlyCapability => { create_readonly_token_with_caps_and_user_from(base.raw(), &cap_psid_ptrs) } - SandboxPolicy::WorkspaceWrite { .. } => { + WindowsSandboxTokenMode::WritableRootsCapability => { create_workspace_write_token_with_caps_and_user_from(base.raw(), &cap_psid_ptrs) } - SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => { - unreachable!() - } } }?); unsafe { @@ -352,7 +355,7 @@ fn spawn_output_reader( ) -> std::thread::JoinHandle<()> { read_handle_loop(handle, move |chunk| { let msg = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Output { payload: OutputPayload { data_b64: encode_bytes(chunk), @@ -554,7 +557,7 @@ pub fn main() -> Result<()> { let process_handle = Arc::new(StdMutex::new(Some(pi.hProcess))); let msg = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::SpawnReady { payload: SpawnReady { process_id: unsafe { GetProcessId(pi.hProcess) }, @@ -631,7 +634,7 @@ pub fn main() -> Result<()> { } let exit_msg = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Exit { payload: ExitPayload { exit_code, diff --git a/codex-rs/windows-sandbox-rs/src/elevated/ipc_framed.rs b/codex-rs/windows-sandbox-rs/src/elevated/ipc_framed.rs index 56e33e57ca..5512ebefb4 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated/ipc_framed.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated/ipc_framed.rs @@ -10,6 +10,7 @@ use anyhow::Result; use base64::Engine as _; use base64::engine::general_purpose::STANDARD; +use codex_protocol::models::PermissionProfile; use serde::Deserialize; use serde::Serialize; use std::collections::HashMap; @@ -23,6 +24,9 @@ use std::path::PathBuf; /// obviously invalid frames. const MAX_FRAME_LEN: usize = 8 * 1024 * 1024; +/// Protocol version shared by the parent process and elevated command runner. +pub const IPC_PROTOCOL_VERSION: u8 = 2; + /// Length-prefixed, JSON-encoded frame. #[derive(Debug, Serialize, Deserialize, Clone)] pub struct FramedMessage { @@ -55,8 +59,8 @@ pub struct SpawnRequest { pub command: Vec, pub cwd: PathBuf, pub env: HashMap, - pub policy_json_or_preset: String, - pub sandbox_policy_cwd: PathBuf, + pub permission_profile: PermissionProfile, + pub permission_profile_cwd: PathBuf, pub codex_home: PathBuf, pub real_codex_home: PathBuf, pub cap_sids: Vec, @@ -164,11 +168,12 @@ pub fn read_frame(mut reader: R) -> Result> { #[cfg(test)] mod tests { use super::*; + use pretty_assertions::assert_eq; #[test] fn framed_round_trip() { let msg = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Output { payload: OutputPayload { data_b64: encode_bytes(b"hello"), @@ -179,7 +184,7 @@ mod tests { let mut buf = Vec::new(); write_frame(&mut buf, &msg).expect("write"); let decoded = read_frame(buf.as_slice()).expect("read").expect("some"); - assert_eq!(decoded.version, 1); + assert_eq!(decoded.version, IPC_PROTOCOL_VERSION); match decoded.message { Message::Output { payload } => { assert_eq!(payload.stream, OutputStream::Stdout); @@ -189,4 +194,43 @@ mod tests { other => panic!("unexpected message: {other:?}"), } } + + #[test] + fn spawn_request_serializes_permission_profile() { + let msg = FramedMessage { + version: IPC_PROTOCOL_VERSION, + message: Message::SpawnRequest { + payload: Box::new(SpawnRequest { + command: vec!["cmd.exe".to_string(), "/c".to_string(), "ver".to_string()], + cwd: PathBuf::from(r"C:\workspace"), + env: HashMap::new(), + permission_profile: PermissionProfile::read_only(), + permission_profile_cwd: PathBuf::from(r"C:\workspace"), + codex_home: PathBuf::from(r"C:\codex"), + real_codex_home: PathBuf::from(r"C:\Users\codex"), + cap_sids: vec!["S-1-15-3-1024-1".to_string()], + timeout_ms: Some(1000), + tty: false, + stdin_open: false, + use_private_desktop: false, + }), + }, + }; + + let encoded = serde_json::to_value(&msg).expect("serialize"); + assert_eq!("spawn_request", encoded["type"]); + assert_eq!("managed", encoded["payload"]["permission_profile"]["type"]); + assert_eq!(None, encoded["payload"].get("policy_json_or_preset")); + assert_eq!(None, encoded["payload"].get("sandbox_policy_cwd")); + + let decoded: FramedMessage = serde_json::from_value(encoded).expect("deserialize"); + let Message::SpawnRequest { payload } = decoded.message else { + panic!("unexpected message"); + }; + assert_eq!(PermissionProfile::read_only(), payload.permission_profile); + assert_eq!( + PathBuf::from(r"C:\workspace"), + payload.permission_profile_cwd + ); + } } diff --git a/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs b/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs index d313b6cfcc..0da7faae4d 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs @@ -1,5 +1,6 @@ use crate::identity::SandboxCreds; use crate::ipc_framed::FramedMessage; +use crate::ipc_framed::IPC_PROTOCOL_VERSION; use crate::ipc_framed::Message; use crate::ipc_framed::SpawnRequest; use crate::ipc_framed::read_frame; @@ -56,7 +57,7 @@ pub(crate) struct RunnerTransport { impl RunnerTransport { pub(crate) fn send_spawn_request(&mut self, request: SpawnRequest) -> Result<()> { let spawn_request = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::SpawnRequest { payload: Box::new(request), }, diff --git a/codex-rs/windows-sandbox-rs/src/elevated_impl.rs b/codex-rs/windows-sandbox-rs/src/elevated_impl.rs index f43715c122..5861317376 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated_impl.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated_impl.rs @@ -45,6 +45,7 @@ mod windows_impl { use crate::setup::effective_write_roots_for_setup; use crate::token::LocalSid; use anyhow::Result; + use codex_protocol::models::PermissionProfile; use codex_utils_absolute_path::AbsolutePathBuf; use std::path::Path; @@ -144,12 +145,14 @@ mod windows_impl { } (|| -> Result { + let permission_profile = + PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd); let spawn_request = SpawnRequest { command: command.clone(), cwd: cwd.to_path_buf(), env: env_map.clone(), - policy_json_or_preset: policy_json_or_preset.to_string(), - sandbox_policy_cwd: sandbox_policy_cwd.to_path_buf(), + permission_profile, + permission_profile_cwd: sandbox_policy_cwd.to_path_buf(), codex_home: sandbox_base.clone(), real_codex_home: codex_home.to_path_buf(), cap_sids, diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index 5accd0be36..d662a62db0 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -155,6 +155,8 @@ pub use ipc_framed::ExitPayload; #[cfg(target_os = "windows")] pub use ipc_framed::FramedMessage; #[cfg(target_os = "windows")] +pub use ipc_framed::IPC_PROTOCOL_VERSION; +#[cfg(target_os = "windows")] pub use ipc_framed::Message; #[cfg(target_os = "windows")] pub use ipc_framed::OutputPayload; @@ -197,6 +199,10 @@ pub use process::read_handle_loop; #[cfg(target_os = "windows")] pub use process::spawn_process_with_pipes; #[cfg(target_os = "windows")] +pub use resolved_permissions::WindowsSandboxTokenMode; +#[cfg(target_os = "windows")] +pub use resolved_permissions::token_mode_for_permission_profile; +#[cfg(target_os = "windows")] pub use setup::SETUP_VERSION; #[cfg(target_os = "windows")] pub use setup::SandboxSetupRequest; diff --git a/codex-rs/windows-sandbox-rs/src/resolved_permissions.rs b/codex-rs/windows-sandbox-rs/src/resolved_permissions.rs index 6b2daa56e0..5c6b0cdffe 100644 --- a/codex-rs/windows-sandbox-rs/src/resolved_permissions.rs +++ b/codex-rs/windows-sandbox-rs/src/resolved_permissions.rs @@ -1,5 +1,8 @@ +use anyhow::Result; +use codex_protocol::models::PermissionProfile; use codex_protocol::permissions::FileSystemPath; use codex_protocol::permissions::FileSystemSandboxEntry; +use codex_protocol::permissions::FileSystemSandboxKind; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::SandboxPolicy; @@ -25,6 +28,33 @@ pub(crate) struct WindowsWritableRoot { pub(crate) read_only_subpaths: Vec, } +/// Restricted-token family needed to enforce a Windows permission profile. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WindowsSandboxTokenMode { + ReadOnlyCapability, + WritableRootsCapability, +} + +/// Chooses the restricted-token family needed for a managed permission profile. +pub fn token_mode_for_permission_profile( + permission_profile: &PermissionProfile, + cwd: &Path, + env_map: &HashMap, +) -> Result { + let permissions = + ResolvedWindowsSandboxPermissions::try_from_permission_profile(permission_profile)?; + if permissions.file_system.has_full_disk_write_access() { + anyhow::bail!( + "permission profile requests full-disk filesystem writes, which cannot be enforced by the Windows sandbox" + ); + } + if permissions.writable_roots_for_cwd(cwd, env_map).is_empty() { + Ok(WindowsSandboxTokenMode::ReadOnlyCapability) + } else { + Ok(WindowsSandboxTokenMode::WritableRootsCapability) + } +} + impl ResolvedWindowsSandboxPermissions { pub(crate) fn from_legacy_policy(policy: &SandboxPolicy) -> Self { Self { @@ -40,6 +70,26 @@ impl ResolvedWindowsSandboxPermissions { } } + pub(crate) fn try_from_permission_profile( + permission_profile: &PermissionProfile, + ) -> Result { + if !matches!(permission_profile, PermissionProfile::Managed { .. }) { + anyhow::bail!( + "only managed permission profiles can be enforced by the Windows sandbox" + ); + } + let (file_system, network) = permission_profile.to_runtime_permissions(); + if !matches!(file_system.kind, FileSystemSandboxKind::Restricted) { + anyhow::bail!( + "only restricted managed filesystem permissions can be enforced by the Windows sandbox" + ); + } + Ok(Self { + file_system, + network, + }) + } + pub(crate) fn should_apply_network_block(&self) -> bool { !self.network.is_enabled() } @@ -114,3 +164,135 @@ fn windows_temp_env_roots(env_map: &HashMap) -> Vec { .filter(|path| path.is_absolute()) .collect() } + +#[cfg(test)] +mod tests { + use super::*; + use codex_protocol::models::ManagedFileSystemPermissions; + use codex_protocol::permissions::FileSystemAccessMode; + use codex_protocol::permissions::FileSystemSandboxEntry; + use codex_protocol::permissions::FileSystemSpecialPath; + use pretty_assertions::assert_eq; + use tempfile::TempDir; + + #[test] + fn permission_profile_workspace_write_uses_windows_temp_env_vars() { + let tmp = TempDir::new().expect("tempdir"); + let cwd = tmp.path().join("workspace"); + let temp_dir = tmp.path().join("temp"); + std::fs::create_dir_all(&cwd).expect("create cwd"); + std::fs::create_dir_all(&temp_dir).expect("create temp dir"); + + let mut env_map = HashMap::new(); + env_map.insert("TEMP".to_string(), temp_dir.to_string_lossy().to_string()); + env_map.insert("TMP".to_string(), temp_dir.to_string_lossy().to_string()); + + let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile( + &PermissionProfile::workspace_write(), + ) + .expect("managed permission profile"); + let roots = permissions + .writable_roots_for_cwd(&cwd, &env_map) + .into_iter() + .map(|root| root.root) + .collect::>(); + + let expected_roots = [ + temp_dir, + dunce::canonicalize(&cwd).expect("canonicalize cwd"), + ] + .into_iter() + .collect::>(); + + assert_eq!(expected_roots, roots); + } + + #[test] + fn token_mode_for_profile_without_writable_roots_uses_readonly_capability() { + let tmp = TempDir::new().expect("tempdir"); + let cwd = tmp.path().join("workspace"); + std::fs::create_dir_all(&cwd).expect("create cwd"); + + let token_mode = token_mode_for_permission_profile( + &PermissionProfile::read_only(), + &cwd, + &HashMap::new(), + ) + .expect("token mode"); + + assert_eq!(WindowsSandboxTokenMode::ReadOnlyCapability, token_mode); + } + + #[test] + fn token_mode_for_profile_with_writable_roots_uses_write_capabilities() { + let tmp = TempDir::new().expect("tempdir"); + let cwd = tmp.path().join("workspace"); + std::fs::create_dir_all(&cwd).expect("create cwd"); + + let token_mode = token_mode_for_permission_profile( + &PermissionProfile::workspace_write(), + &cwd, + &HashMap::new(), + ) + .expect("token mode"); + + assert_eq!(WindowsSandboxTokenMode::WritableRootsCapability, token_mode); + } + + #[test] + fn permission_profile_rejects_disabled_profiles() { + let err = ResolvedWindowsSandboxPermissions::try_from_permission_profile( + &PermissionProfile::Disabled, + ) + .expect_err("disabled profile should not resolve for sandbox enforcement"); + + assert!( + err.to_string() + .contains("only managed permission profiles can be enforced") + ); + } + + #[test] + fn permission_profile_rejects_unrestricted_managed_filesystem() { + let permission_profile = PermissionProfile::Managed { + file_system: ManagedFileSystemPermissions::Unrestricted, + network: NetworkSandboxPolicy::Restricted, + }; + + let err = + ResolvedWindowsSandboxPermissions::try_from_permission_profile(&permission_profile) + .expect_err("unrestricted profile should not resolve for sandbox enforcement"); + + assert!( + err.to_string() + .contains("only restricted managed filesystem permissions can be enforced") + ); + } + + #[test] + fn token_mode_rejects_full_disk_write_entries() { + let tmp = TempDir::new().expect("tempdir"); + let cwd = tmp.path().join("workspace"); + std::fs::create_dir_all(&cwd).expect("create cwd"); + let permission_profile = PermissionProfile::Managed { + file_system: ManagedFileSystemPermissions::Restricted { + entries: vec![FileSystemSandboxEntry { + path: FileSystemPath::Special { + value: FileSystemSpecialPath::Root, + }, + access: FileSystemAccessMode::Write, + }], + glob_scan_max_depth: None, + }, + network: NetworkSandboxPolicy::Restricted, + }; + + let err = token_mode_for_permission_profile(&permission_profile, &cwd, &HashMap::new()) + .expect_err("full disk writes should not resolve to a token mode"); + + assert!( + err.to_string() + .contains("full-disk filesystem writes, which cannot be enforced") + ); + } +} diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs index 0d7e94a1ff..352226644d 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/elevated.rs @@ -5,11 +5,13 @@ use super::windows_common::start_runner_stdin_writer; use super::windows_common::start_runner_stdout_reader; use crate::ipc_framed::EmptyPayload; use crate::ipc_framed::FramedMessage; +use crate::ipc_framed::IPC_PROTOCOL_VERSION; use crate::ipc_framed::Message; use crate::ipc_framed::SpawnRequest; use crate::runner_client::spawn_runner_transport; use crate::spawn_prep::prepare_elevated_spawn_context; use anyhow::Result; +use codex_protocol::models::PermissionProfile; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_pty::ProcessDriver; use codex_utils_pty::SpawnedProcess; @@ -60,12 +62,16 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated( &deny_write_paths_override, )?; + let permission_profile = PermissionProfile::from_legacy_sandbox_policy_for_cwd( + &elevated.common.policy, + sandbox_policy_cwd, + ); let spawn_request = SpawnRequest { command: command.clone(), cwd: cwd.to_path_buf(), env: env_map.clone(), - policy_json_or_preset: policy_json_or_preset.to_string(), - sandbox_policy_cwd: sandbox_policy_cwd.to_path_buf(), + permission_profile, + permission_profile_cwd: sandbox_policy_cwd.to_path_buf(), codex_home: elevated.common.sandbox_base.clone(), real_codex_home: codex_home.to_path_buf(), cap_sids: elevated.cap_sids.clone(), @@ -106,7 +112,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated( let outbound_tx = outbound_tx.clone(); Some(Box::new(move || { let _ = outbound_tx.send(FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Terminate { payload: EmptyPayload::default(), }, diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/windows_common.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/windows_common.rs index 3396b4ed98..f7be315ad8 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/windows_common.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/windows_common.rs @@ -1,5 +1,6 @@ use crate::ipc_framed::EmptyPayload; use crate::ipc_framed::FramedMessage; +use crate::ipc_framed::IPC_PROTOCOL_VERSION; use crate::ipc_framed::Message; use crate::ipc_framed::OutputStream; use crate::ipc_framed::ResizePayload; @@ -70,7 +71,7 @@ pub(crate) fn start_runner_stdin_writer( bytes }; let msg = FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Stdin { payload: StdinPayload { data_b64: encode_bytes(&bytes), @@ -83,7 +84,7 @@ pub(crate) fn start_runner_stdin_writer( } if stdin_open { let _ = outbound_tx.send(FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::CloseStdin { payload: EmptyPayload::default(), }, @@ -165,7 +166,7 @@ pub(crate) fn make_runner_resizer( Box::new(move |size: TerminalSize| { outbound_tx .send(FramedMessage { - version: 1, + version: IPC_PROTOCOL_VERSION, message: Message::Resize { payload: ResizePayload { rows: size.rows,