diff --git a/codex-rs/core/src/exec.rs b/codex-rs/core/src/exec.rs index 0142f995f5..f144230cf7 100644 --- a/codex-rs/core/src/exec.rs +++ b/codex-rs/core/src/exec.rs @@ -442,6 +442,7 @@ pub(crate) async fn execute_exec_request( windows_sandbox_filesystem_overrides, network_environment_id, arg0, + exec_server_sandbox: _, } = exec_request; // TODO(anp): Keep PathUri through the local process launch boundary. diff --git a/codex-rs/core/src/sandboxing/mod.rs b/codex-rs/core/src/sandboxing/mod.rs index c156b0d70e..94e72d1f20 100644 --- a/codex-rs/core/src/sandboxing/mod.rs +++ b/codex-rs/core/src/sandboxing/mod.rs @@ -14,6 +14,7 @@ use crate::exec::execute_exec_request; #[cfg(target_os = "macos")] use crate::spawn::CODEX_SANDBOX_ENV_VAR; use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR; +use codex_exec_server::FileSystemSandboxContext; use codex_network_proxy::NetworkProxy; use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::exec_output::ExecToolCallOutput; @@ -60,6 +61,7 @@ pub struct ExecRequest { pub network_sandbox_policy: NetworkSandboxPolicy, pub(crate) windows_sandbox_filesystem_overrides: Option, pub arg0: Option, + pub(crate) exec_server_sandbox: Option, } impl ExecRequest { @@ -102,6 +104,7 @@ impl ExecRequest { network_sandbox_policy, windows_sandbox_filesystem_overrides: None, arg0, + exec_server_sandbox: None, } } @@ -158,6 +161,7 @@ impl ExecRequest { network_sandbox_policy, windows_sandbox_filesystem_overrides: None, arg0, + exec_server_sandbox: None, } } } diff --git a/codex-rs/core/src/tasks/user_shell.rs b/codex-rs/core/src/tasks/user_shell.rs index c5b44a6494..9ff9e5d2d8 100644 --- a/codex-rs/core/src/tasks/user_shell.rs +++ b/codex-rs/core/src/tasks/user_shell.rs @@ -224,6 +224,7 @@ pub(crate) async fn execute_user_shell_command( network_sandbox_policy: permission_profile.network_sandbox_policy(), windows_sandbox_filesystem_overrides: None, arg0: None, + exec_server_sandbox: None, }; let stdout_stream = Some(StdoutStream { diff --git a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs index 2acbf37781..d56a636af4 100644 --- a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs +++ b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs @@ -164,6 +164,7 @@ pub(super) async fn try_run_zsh_fork( network_sandbox_policy, windows_sandbox_filesystem_overrides: _windows_sandbox_filesystem_overrides, arg0, + exec_server_sandbox: _, } = sandbox_exec_request; let ParsedShellCommand { script, login, .. } = extract_shell_script(&command)?; let effective_timeout = Duration::from_millis( @@ -893,6 +894,7 @@ impl CoreShellCommandExecutor { network_sandbox_policy: self.network_sandbox_policy, windows_sandbox_filesystem_overrides: None, arg0: self.arg0.clone(), + exec_server_sandbox: None, }, /*stdout_stream*/ None, after_spawn, diff --git a/codex-rs/core/src/tools/runtimes/unified_exec.rs b/codex-rs/core/src/tools/runtimes/unified_exec.rs index 80f0990b5c..e1af9db543 100644 --- a/codex-rs/core/src/tools/runtimes/unified_exec.rs +++ b/codex-rs/core/src/tools/runtimes/unified_exec.rs @@ -382,9 +382,12 @@ impl<'a> ToolRuntime for UnifiedExecRunt error @ ToolError::Codex(_) => error, })?; let options = unified_exec_options(attempt.network_denial_cancellation_token.clone()); - let mut exec_env = attempt - .env_for(command, options, managed_network) - .map_err(ToolError::Codex)?; + let mut exec_env = if req.turn_environment.environment.is_remote() { + attempt.env_for_exec_server(command, options, managed_network) + } else { + attempt.env_for(command, options, managed_network) + } + .map_err(ToolError::Codex)?; exec_env.exec_server_env_config = req.exec_server_env_config.clone(); match zsh_fork_backend::maybe_prepare_unified_exec( req, @@ -442,9 +445,12 @@ impl<'a> ToolRuntime for UnifiedExecRunt error @ ToolError::Codex(_) => error, })?; let options = unified_exec_options(attempt.network_denial_cancellation_token.clone()); - let mut exec_env = attempt - .env_for(command, options, managed_network) - .map_err(ToolError::Codex)?; + let mut exec_env = if req.turn_environment.environment.is_remote() { + attempt.env_for_exec_server(command, options, managed_network) + } else { + attempt.env_for(command, options, managed_network) + } + .map_err(ToolError::Codex)?; exec_env.exec_server_env_config = req.exec_server_env_config.clone(); self.manager .open_session_with_exec_env( diff --git a/codex-rs/core/src/tools/sandboxing.rs b/codex-rs/core/src/tools/sandboxing.rs index e80cbbba1a..54836f7368 100644 --- a/codex-rs/core/src/tools/sandboxing.rs +++ b/codex-rs/core/src/tools/sandboxing.rs @@ -11,6 +11,7 @@ use crate::session::turn_context::TurnContext; use crate::state::SessionServices; use crate::tools::hook_names::HookToolName; use crate::tools::network_approval::NetworkApprovalSpec; +use codex_exec_server::FileSystemSandboxContext; use codex_network_proxy::NetworkProxy; use codex_protocol::approvals::ExecPolicyAmendment; use codex_protocol::approvals::NetworkApprovalContext; @@ -451,6 +452,53 @@ impl<'a> SandboxAttempt<'a> { self.workspace_roots.to_vec(), )) } + + pub fn env_for_exec_server( + &self, + command: SandboxCommand, + options: ExecOptions, + network: Option<&NetworkProxy>, + ) -> Result { + let request = self + .manager + .transform(SandboxTransformRequest { + command, + permissions: self.permissions, + // The exec-server chooses and applies its native platform sandbox. + sandbox: SandboxType::None, + enforce_managed_network: self.enforce_managed_network, + environment_id: None, + network, + sandbox_policy_cwd: self.sandbox_cwd, + codex_linux_sandbox_exe: None, + use_legacy_landlock: self.use_legacy_landlock, + windows_sandbox_level: self.windows_sandbox_level, + windows_sandbox_private_desktop: self.windows_sandbox_private_desktop, + }) + .map_err(CodexErr::from)?; + let mut exec_request = crate::sandboxing::ExecRequest::from_sandbox_exec_request( + request, + options, + self.workspace_roots.to_vec(), + ); + if self.sandbox != SandboxType::None { + exec_request.exec_server_sandbox = Some(FileSystemSandboxContext { + permissions: exec_request.permission_profile.clone().into(), + cwd: Some(exec_request.windows_sandbox_policy_cwd.clone()), + workspace_roots: self + .workspace_roots + .iter() + .map(PathUri::from_abs_path) + .collect(), + windows_sandbox_level: self.windows_sandbox_level, + windows_sandbox_private_desktop: self.windows_sandbox_private_desktop, + use_legacy_landlock: self.use_legacy_landlock, + }); + // Preserve denial classification while leaving argv native to the executor. + exec_request.sandbox = self.sandbox; + } + Ok(exec_request) + } } #[cfg(test)] diff --git a/codex-rs/core/src/tools/sandboxing_tests.rs b/codex-rs/core/src/tools/sandboxing_tests.rs index 21719e281f..281b622582 100644 --- a/codex-rs/core/src/tools/sandboxing_tests.rs +++ b/codex-rs/core/src/tools/sandboxing_tests.rs @@ -4,7 +4,13 @@ use crate::tools::hook_names::HookToolName; use codex_protocol::permissions::FileSystemAccessMode; use codex_protocol::permissions::FileSystemPath; use codex_protocol::permissions::FileSystemSandboxEntry; +use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::GranularApprovalConfig; +use codex_sandboxing::SandboxCommand; +use codex_sandboxing::SandboxManager; +use codex_sandboxing::SandboxType; +use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_path_uri::PathUri; use pretty_assertions::assert_eq; use serde_json::json; @@ -193,3 +199,66 @@ fn deny_read_blocks_explicit_escalation_and_policy_bypass() { "exec-policy allow rules would drop deny-read filesystem policy, so keep the first attempt sandboxed", ); } + +#[test] +fn exec_server_env_preserves_command_and_carries_sandbox_context() { + let cwd: AbsolutePathBuf = std::env::current_dir() + .expect("current dir") + .try_into() + .expect("absolute cwd"); + let cwd_uri = PathUri::from_abs_path(&cwd); + let permissions = codex_protocol::models::PermissionProfile::from_runtime_permissions( + &FileSystemSandboxPolicy::default(), + NetworkSandboxPolicy::Restricted, + ); + let manager = SandboxManager::new(); + let attempt = SandboxAttempt { + sandbox: SandboxType::MacosSeatbelt, + permissions: &permissions, + enforce_managed_network: false, + manager: &manager, + sandbox_cwd: &cwd_uri, + workspace_roots: std::slice::from_ref(&cwd), + codex_linux_sandbox_exe: None, + use_legacy_landlock: false, + windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled, + windows_sandbox_private_desktop: false, + network_denial_cancellation_token: None, + }; + let command = SandboxCommand { + program: "/bin/bash".into(), + args: vec!["-lc".to_string(), "pwd".to_string()], + cwd: cwd_uri.clone(), + env: HashMap::new(), + additional_permissions: None, + }; + let options = ExecOptions { + expiration: crate::exec::ExecExpiration::DefaultTimeout, + capture_policy: crate::exec::ExecCapturePolicy::ShellTool, + }; + + let request = attempt + .env_for_exec_server(command, options, /*network*/ None) + .expect("prepare remote exec request"); + + assert_eq!( + request.command, + vec![ + "/bin/bash".to_string(), + "-lc".to_string(), + "pwd".to_string() + ] + ); + assert_eq!(request.sandbox, SandboxType::MacosSeatbelt); + assert_eq!( + request.exec_server_sandbox, + Some(codex_exec_server::FileSystemSandboxContext { + permissions: request.permission_profile.clone().into(), + cwd: Some(cwd_uri.clone()), + workspace_roots: vec![cwd_uri], + windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled, + windows_sandbox_private_desktop: false, + use_legacy_landlock: false, + }) + ); +} diff --git a/codex-rs/core/src/unified_exec/process_manager.rs b/codex-rs/core/src/unified_exec/process_manager.rs index 0aac491823..337df9edca 100644 --- a/codex-rs/core/src/unified_exec/process_manager.rs +++ b/codex-rs/core/src/unified_exec/process_manager.rs @@ -162,6 +162,7 @@ fn exec_server_params_for_request( tty, pipe_stdin: false, arg0: request.arg0.clone(), + sandbox: request.exec_server_sandbox.clone(), } } diff --git a/codex-rs/core/src/unified_exec/process_manager_tests.rs b/codex-rs/core/src/unified_exec/process_manager_tests.rs index 5abd926c62..76c65c80c0 100644 --- a/codex-rs/core/src/unified_exec/process_manager_tests.rs +++ b/codex-rs/core/src/unified_exec/process_manager_tests.rs @@ -111,6 +111,7 @@ fn exec_server_params_use_path_uri_and_env_policy_overlay_contract() { network_sandbox_policy, windows_sandbox_filesystem_overrides: None, arg0: None, + exec_server_sandbox: None, }; let params = diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index 9496bb8be6..fba01b97ea 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -1156,6 +1156,7 @@ mod tests { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await .expect("start process"); diff --git a/codex-rs/exec-server/src/lib.rs b/codex-rs/exec-server/src/lib.rs index d66d2f9b7e..4ea43afbf2 100644 --- a/codex-rs/exec-server/src/lib.rs +++ b/codex-rs/exec-server/src/lib.rs @@ -16,6 +16,7 @@ mod noise_channel; mod noise_relay; mod process; mod process_id; +mod process_sandbox; mod protocol; mod regular_file; mod relay; diff --git a/codex-rs/exec-server/src/local_process.rs b/codex-rs/exec-server/src/local_process.rs index 5e1b52b11c..bd06f70a47 100644 --- a/codex-rs/exec-server/src/local_process.rs +++ b/codex-rs/exec-server/src/local_process.rs @@ -902,6 +902,7 @@ mod tests { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, } } diff --git a/codex-rs/exec-server/src/process_sandbox.rs b/codex-rs/exec-server/src/process_sandbox.rs new file mode 100644 index 0000000000..e3a32262c0 --- /dev/null +++ b/codex-rs/exec-server/src/process_sandbox.rs @@ -0,0 +1,150 @@ +use crate::ExecServerRuntimePaths; +use crate::protocol::ExecParams; +use crate::rpc::invalid_params; +use codex_app_server_protocol::JSONRPCErrorError; +use codex_protocol::models::PermissionProfile; +use codex_sandboxing::SandboxCommand; +use codex_sandboxing::SandboxDirectSpawnTransformRequest; +use codex_sandboxing::SandboxManager; +use codex_sandboxing::SandboxTransformRequest; +use codex_sandboxing::SandboxType; +use codex_sandboxing::SandboxablePreference; + +/// Converts a remote launch's sandbox policy into this host's native wrapper. +pub(crate) fn prepare_exec_params( + mut params: ExecParams, + runtime_paths: &ExecServerRuntimePaths, +) -> Result { + let Some(sandbox_context) = params.sandbox.take() else { + return Ok(params); + }; + let native_permissions: PermissionProfile = sandbox_context + .permissions + .try_into() + .map_err(|err| invalid_params(format!("invalid sandbox permission path URI: {err}")))?; + let (file_system_policy, network_policy) = native_permissions.to_runtime_permissions(); + let sandbox_manager = SandboxManager::new(); + let sandbox = sandbox_manager.select_initial( + &file_system_policy, + network_policy, + SandboxablePreference::Auto, + sandbox_context.windows_sandbox_level, + /*has_managed_network_requirements*/ false, + ); + if sandbox == SandboxType::None { + return Ok(params); + } + + let (program, args) = params + .argv + .split_first() + .ok_or_else(|| invalid_params("argv must not be empty".to_string()))?; + let command = SandboxCommand { + program: program.into(), + args: args.to_vec(), + cwd: params.cwd.clone(), + env: params.env.clone(), + additional_permissions: None, + }; + let sandbox_policy_cwd = sandbox_context.cwd.as_ref().unwrap_or(¶ms.cwd); + let native_workspace_roots = sandbox_context + .workspace_roots + .iter() + .map(|root| { + root.to_abs_path().map_err(|err| { + invalid_params(format!( + "sandbox workspace root URI `{root}` is not valid on this exec-server host: {err}" + )) + }) + }) + .collect::, _>>()?; + let command_cwd = params.cwd.to_abs_path().map_err(|err| { + invalid_params(format!( + "cwd URI `{}` is not valid on this exec-server host: {err}", + params.cwd + )) + })?; + let workspace_roots = if native_workspace_roots.is_empty() { + std::slice::from_ref(&command_cwd) + } else { + native_workspace_roots.as_slice() + }; + let request = sandbox_manager + .transform_for_direct_spawn(SandboxDirectSpawnTransformRequest { + workspace_roots, + transform: SandboxTransformRequest { + command, + permissions: &native_permissions, + sandbox, + enforce_managed_network: false, + environment_id: None, + network: None, + sandbox_policy_cwd, + codex_linux_sandbox_exe: runtime_paths.codex_linux_sandbox_exe.as_deref(), + use_legacy_landlock: sandbox_context.use_legacy_landlock, + windows_sandbox_level: sandbox_context.windows_sandbox_level, + windows_sandbox_private_desktop: sandbox_context.windows_sandbox_private_desktop, + }, + }) + .map_err(|err| invalid_params(format!("failed to prepare process sandbox: {err}")))?; + params.argv = request.command; + params.env = request.env; + params.arg0 = request.arg0; + Ok(params) +} + +#[cfg(all(test, target_os = "linux"))] +mod tests { + use std::collections::HashMap; + + use codex_protocol::permissions::FileSystemSandboxPolicy; + use codex_protocol::permissions::NetworkSandboxPolicy; + use codex_utils_path_uri::PathUri; + use pretty_assertions::assert_eq; + + use super::*; + use crate::FileSystemSandboxContext; + use crate::ProcessId; + + #[test] + fn remote_sandbox_uses_executor_linux_helper() { + let cwd = std::env::current_dir().expect("cwd"); + let cwd_uri = PathUri::from_path(&cwd).expect("cwd URI"); + let permissions = PermissionProfile::from_runtime_permissions( + &FileSystemSandboxPolicy::default(), + NetworkSandboxPolicy::Restricted, + ); + let runtime_paths = ExecServerRuntimePaths::new( + "/executor/codex".into(), + Some("/executor/codex-linux-sandbox".into()), + ) + .expect("runtime paths"); + let params = ExecParams { + process_id: ProcessId::from("sandboxed"), + argv: vec![ + "/bin/bash".to_string(), + "-lc".to_string(), + "pwd".to_string(), + ], + cwd: cwd_uri.clone(), + env_policy: None, + env: HashMap::new(), + tty: false, + pipe_stdin: false, + arg0: None, + sandbox: Some(FileSystemSandboxContext::from_permission_profile_with_cwd( + permissions, + cwd_uri, + )), + }; + + let params = prepare_exec_params(params, &runtime_paths).expect("prepare sandbox"); + + assert_eq!( + params.argv.first(), + Some(&"/executor/codex-linux-sandbox".to_string()) + ); + assert_eq!(params.arg0, Some("codex-linux-sandbox".to_string())); + assert_eq!(params.sandbox, None); + } +} diff --git a/codex-rs/exec-server/src/protocol.rs b/codex-rs/exec-server/src/protocol.rs index cde670a2ed..e40b4bfb37 100644 --- a/codex-rs/exec-server/src/protocol.rs +++ b/codex-rs/exec-server/src/protocol.rs @@ -118,6 +118,10 @@ pub struct ExecParams { /// Optional process-visible argv0 override. Values such as `codex-linux-sandbox` are command /// names rather than paths, so this is not a [`PathUri`]. pub arg0: Option, + /// Sandbox policy for this launch. The exec-server resolves this into its own platform + /// sandbox wrapper so clients never send host-local helper paths in argv. + #[serde(default)] + pub sandbox: Option, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/codex-rs/exec-server/src/server/handler.rs b/codex-rs/exec-server/src/server/handler.rs index bb13e37847..65ee2e89c7 100644 --- a/codex-rs/exec-server/src/server/handler.rs +++ b/codex-rs/exec-server/src/server/handler.rs @@ -14,6 +14,7 @@ use tokio_util::task::TaskTracker; use crate::ExecServerRuntimePaths; use crate::client::http_client::PendingReqwestHttpBodyStream; use crate::client::http_client::ReqwestHttpRequestRunner; +use crate::process_sandbox::prepare_exec_params; use crate::protocol::EnvironmentInfo; use crate::protocol::ExecParams; use crate::protocol::ExecResponse; @@ -67,6 +68,7 @@ pub(crate) struct ExecServerHandler { background_task_shutdown: CancellationToken, background_tasks: TaskTracker, file_system: FileSystemHandler, + runtime_paths: ExecServerRuntimePaths, initialize_requested: AtomicBool, initialized: AtomicBool, } @@ -84,7 +86,8 @@ impl ExecServerHandler { active_body_stream_ids: Mutex::new(HashSet::new()), background_task_shutdown: CancellationToken::new(), background_tasks: TaskTracker::new(), - file_system: FileSystemHandler::new(runtime_paths), + file_system: FileSystemHandler::new(runtime_paths.clone()), + runtime_paths, initialize_requested: AtomicBool::new(false), initialized: AtomicBool::new(false), } @@ -154,6 +157,7 @@ impl ExecServerHandler { pub(crate) async fn exec(&self, params: ExecParams) -> Result { let session = self.require_initialized_for("exec")?; + let params = prepare_exec_params(params, &self.runtime_paths)?; session.process().exec(params).await } diff --git a/codex-rs/exec-server/src/server/handler/tests.rs b/codex-rs/exec-server/src/server/handler/tests.rs index 0bd682bd96..3aac2e2c85 100644 --- a/codex-rs/exec-server/src/server/handler/tests.rs +++ b/codex-rs/exec-server/src/server/handler/tests.rs @@ -33,6 +33,7 @@ fn exec_params_with_argv(process_id: &str, argv: Vec) -> ExecParams { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, } } diff --git a/codex-rs/exec-server/src/server/processor.rs b/codex-rs/exec-server/src/server/processor.rs index d1ccd57463..8025918587 100644 --- a/codex-rs/exec-server/src/server/processor.rs +++ b/codex-rs/exec-server/src/server/processor.rs @@ -620,6 +620,7 @@ mod tests { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, } } diff --git a/codex-rs/exec-server/tests/exec_process.rs b/codex-rs/exec-server/tests/exec_process.rs index 11a3f3ece7..79f51dbde3 100644 --- a/codex-rs/exec-server/tests/exec_process.rs +++ b/codex-rs/exec-server/tests/exec_process.rs @@ -81,6 +81,7 @@ async fn assert_exec_process_starts_and_exits(use_remote: bool) -> Result<()> { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), "proc-1"); @@ -222,6 +223,7 @@ async fn assert_exec_process_streams_output(use_remote: bool) -> Result<()> { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -253,6 +255,7 @@ async fn assert_exec_process_pushes_events(use_remote: bool) -> Result<()> { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -300,6 +303,7 @@ async fn assert_exec_process_replays_events_after_close(use_remote: bool) -> Res tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -348,6 +352,7 @@ async fn assert_exec_process_retains_output_after_exit_until_streams_close( tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -421,6 +426,7 @@ async fn assert_exec_process_write_then_read(use_remote: bool) -> Result<()> { tty: true, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -458,6 +464,7 @@ async fn assert_exec_process_write_then_read_without_tty(use_remote: bool) -> Re tty: false, pipe_stdin: true, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -491,6 +498,7 @@ async fn assert_exec_process_rejects_write_without_pipe_stdin(use_remote: bool) tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -525,6 +533,7 @@ async fn assert_exec_process_signal_interrupts_process(use_remote: bool) -> Resu tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!(session.process.process_id().as_str(), process_id); @@ -578,6 +587,7 @@ async fn assert_exec_process_signal_reports_unsupported_on_windows(use_remote: b tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; @@ -618,6 +628,7 @@ async fn assert_exec_process_preserves_queued_events_before_subscribe( tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; @@ -676,6 +687,7 @@ async fn remote_exec_process_recovers_after_transport_disconnect() -> Result<()> tty: false, pipe_stdin: true, arg0: None, + sandbox: None, }) .await?; diff --git a/codex-rs/exec-server/tests/relay.rs b/codex-rs/exec-server/tests/relay.rs index 5e58618410..dd505f94b4 100644 --- a/codex-rs/exec-server/tests/relay.rs +++ b/codex-rs/exec-server/tests/relay.rs @@ -150,6 +150,7 @@ async fn remote_environment_routes_encrypted_exec_server_rpc() -> Result<()> { tty: false, pipe_stdin: false, arg0: None, + sandbox: None, }) .await?; assert_eq!( diff --git a/codex-rs/rmcp-client/src/stdio_server_launcher.rs b/codex-rs/rmcp-client/src/stdio_server_launcher.rs index 8ada806ff7..0a6ecb1527 100644 --- a/codex-rs/rmcp-client/src/stdio_server_launcher.rs +++ b/codex-rs/rmcp-client/src/stdio_server_launcher.rs @@ -503,6 +503,7 @@ impl ExecutorStdioServerLauncher { tty: false, pipe_stdin: true, arg0: None, + sandbox: None, }) .await .map_err(io::Error::other)?;