diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 40551bfe50..d5d3e22830 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -906,7 +906,11 @@ impl Session { &self, turn_diff_tracker: &mut TurnDiffTracker, begin_ctx: ExecCommandContext, - request: ExecutionRequest, + params: ExecParams, + approval_command: Vec, + mode: ExecutionMode, + stdout_stream: Option, + use_shell_profile: bool, approval_policy: AskForApproval, ) -> Result { let is_apply_patch = begin_ctx.apply_patch.is_some(); @@ -916,6 +920,14 @@ impl Session { self.on_exec_command_begin(turn_diff_tracker, begin_ctx.clone()) .await; + let request = ExecutionRequest { + params, + approval_command, + mode, + stdout_stream, + use_shell_profile, + }; + let result = self .services .executor @@ -1030,7 +1042,7 @@ impl Session { &self.services.notifier } - fn user_shell(&self) -> &shell::Shell { + pub(crate) fn user_shell(&self) -> &shell::Shell { &self.services.user_shell } @@ -2508,24 +2520,6 @@ fn parse_container_exec_arguments( }) } -fn maybe_translate_shell_command( - params: ExecParams, - sess: &Session, - turn_context: &TurnContext, -) -> ExecParams { - let should_translate = matches!(sess.user_shell(), crate::shell::Shell::PowerShell(_)) - || turn_context.shell_environment_policy.use_profile; - - if should_translate - && let Some(command) = sess - .user_shell() - .format_default_shell_invocation(params.command.clone()) - { - return ExecParams { command, ..params }; - } - params -} - async fn handle_container_exec_with_params( params: ExecParams, sess: &Session, @@ -2590,29 +2584,11 @@ async fn handle_container_exec_with_params( ), }; - let translated_params = maybe_translate_shell_command(params, sess, turn_context); - let stdout_stream = if exec_command_context.apply_patch.is_some() { - None - } else { - Some(StdoutStream { - sub_id: sub_id.clone(), - call_id: call_id.clone(), - tx_event: sess.tx_event.clone(), - }) - }; - let mode = match apply_patch_exec { Some(exec) => ExecutionMode::ApplyPatch(exec), None => ExecutionMode::Shell, }; - let request = ExecutionRequest { - params: translated_params, - approval_command: command_for_display, - mode, - stdout_stream, - }; - sess.services.executor.update_environment( turn_context.sandbox_policy.clone(), turn_context.cwd.clone(), @@ -2622,7 +2598,15 @@ async fn handle_container_exec_with_params( .run_exec_with_events( turn_diff_tracker, exec_command_context, - request, + params, + command_for_display, + mode, + Some(StdoutStream { + sub_id: sub_id.clone(), // todo we should not nead the ids + call_id: call_id.clone(), + tx_event: sess.tx_event.clone(), + }), + turn_context.shell_environment_policy.use_profile, turn_context.approval_policy, ) .await; diff --git a/codex-rs/core/src/executor/backends.rs b/codex-rs/core/src/executor/backends.rs index aa744b8174..95cdb3cacb 100644 --- a/codex-rs/core/src/executor/backends.rs +++ b/codex-rs/core/src/executor/backends.rs @@ -23,6 +23,10 @@ pub(crate) trait ExecutionBackend: Send + Sync { // Required for downcasting the apply_patch. mode: &ExecutionMode, ) -> Result; + + fn stream_stdout(&self, _mode: &ExecutionMode) -> bool { + true + } } static SHELL_BACKEND: ShellBackend = ShellBackend; @@ -90,4 +94,8 @@ impl ExecutionBackend for ApplyPatchBackend { )), } } + + fn stream_stdout(&self, _mode: &ExecutionMode) -> bool { + false + } } diff --git a/codex-rs/core/src/executor/runner.rs b/codex-rs/core/src/executor/runner.rs index 9161e78659..086bdcf2f4 100644 --- a/codex-rs/core/src/executor/runner.rs +++ b/codex-rs/core/src/executor/runner.rs @@ -23,6 +23,7 @@ use crate::function_tool::FunctionCallError; use crate::protocol::AskForApproval; use crate::protocol::ReviewDecision; use crate::protocol::SandboxPolicy; +use crate::shell; #[derive(Clone, Debug)] pub(crate) struct ExecutorConfig { @@ -94,8 +95,18 @@ impl Executor { sub_id: &str, call_id: &str, ) -> Result { + if matches!(request.mode, ExecutionMode::Shell) { + request.params = + maybe_translate_shell_command(request.params, session, request.use_shell_profile); + } + // Step 1: Normalise parameters via the selected backend. let backend = backend_for_mode(&request.mode); + let stdout_stream = if backend.stream_stdout(&request.mode) { + request.stdout_stream.clone() + } else { + None + }; request.params = backend .prepare(request.params, &request.mode) .map_err(ExecError::from)?; @@ -128,7 +139,7 @@ impl Executor { request.params.clone(), sandbox_decision.initial_sandbox, &config, - request.stdout_stream.clone(), + stdout_stream.clone(), ) .await; @@ -140,8 +151,16 @@ impl Executor { } Err(CodexErr::Sandbox(error @ SandboxErr::Denied { .. })) => { return if sandbox_decision.escalate_on_failure { - self.retry_without_sandbox(&request, &config, session, sub_id, call_id, error) - .await + self.retry_without_sandbox( + &request, + &config, + session, + sub_id, + call_id, + stdout_stream.clone(), + error, + ) + .await } else { Err(ExecError::rejection(format!( "failed in sandbox {:?} with execution error: {error:?}", @@ -165,6 +184,7 @@ impl Executor { session: &Session, sub_id: &str, call_id: &str, + stdout_stream: Option, sandbox_error: SandboxErr, ) -> Result { session @@ -194,7 +214,7 @@ impl Executor { request.params.clone(), SandboxType::None, config, - request.stdout_stream.clone(), + stdout_stream, ) .await?; @@ -225,11 +245,31 @@ impl Executor { } } +fn maybe_translate_shell_command( + params: ExecParams, + session: &Session, + use_shell_profile: bool, +) -> ExecParams { + let should_translate = + matches!(session.user_shell(), shell::Shell::PowerShell(_)) || use_shell_profile; + + if should_translate + && let Some(command) = session + .user_shell() + .format_default_shell_invocation(params.command.clone()) + { + return ExecParams { command, ..params }; + } + + params +} + pub(crate) struct ExecutionRequest { pub params: ExecParams, pub approval_command: Vec, pub mode: ExecutionMode, pub stdout_stream: Option, + pub use_shell_profile: bool, } pub(crate) struct NormalizedExecOutput<'a> { diff --git a/codex-rs/core/src/executor/sandbox.rs b/codex-rs/core/src/executor/sandbox.rs index 87fdc0f00a..99507fc446 100644 --- a/codex-rs/core/src/executor/sandbox.rs +++ b/codex-rs/core/src/executor/sandbox.rs @@ -196,6 +196,7 @@ mod tests { approval_command: vec!["apply_patch".into()], mode: ExecutionMode::ApplyPatch(exec), stdout_stream: None, + use_shell_profile: false, }; let decision = select_sandbox( &request, @@ -236,6 +237,7 @@ mod tests { approval_command: vec!["apply_patch".into()], mode: ExecutionMode::ApplyPatch(exec), stdout_stream: None, + use_shell_profile: false, }; let decision = select_sandbox( &request, @@ -277,6 +279,7 @@ mod tests { approval_command: vec!["apply_patch".into()], mode: ExecutionMode::ApplyPatch(exec), stdout_stream: None, + use_shell_profile: false, }; let result = select_sandbox( &request, @@ -313,6 +316,7 @@ mod tests { approval_command: vec!["some-unknown".into()], mode: ExecutionMode::Shell, stdout_stream: None, + use_shell_profile: false, }; let decision = select_sandbox( &request, @@ -347,6 +351,7 @@ mod tests { approval_command: vec!["some-unknown".into()], mode: ExecutionMode::Shell, stdout_stream: None, + use_shell_profile: false, }; let decision = select_sandbox( &request,