From 8c8600948349667fa7d8de3c472b9d5353817a12 Mon Sep 17 00:00:00 2001 From: easong-openai Date: Mon, 4 Aug 2025 11:24:19 -0700 Subject: [PATCH] remove sandbox denied swallowing code for being bad --- codex-rs/core/src/codex.rs | 90 ++------------------------------------ 1 file changed, 4 insertions(+), 86 deletions(-) diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 6824e2093d..ab8c7f6177 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -1869,20 +1869,10 @@ async fn handle_sandbox_error( // include additional metadata on the command to indicate whether non-zero // exit codes merit a retry. - // For now, we categorically ask the user to retry without sandbox. - // - // The raw error for a sandbox denial includes full stdout/stderr which can be very long. - // Emit a compressed background event to keep the transcript readable. - match &error { - SandboxErr::Denied(exit_code, stdout, stderr) => { - let summary = summarize_sandbox_denied_streams(*exit_code, stdout, stderr); - sess.notify_background_event(&sub_id, summary).await; - } - _ => { - sess.notify_background_event(&sub_id, format!("Execution failed: {error}")) - .await; - } - } + // For now, we categorically ask the user to retry without sandbox and + // emit the raw error as a background event. + sess.notify_background_event(&sub_id, format!("Execution failed: {error}")) + .await; let rx_approve = sess .request_command_approval( @@ -1975,78 +1965,6 @@ async fn handle_sandbox_error( } } -/// Create a compact, human-friendly summary for sandbox-denied executions. -/// -/// This avoids dumping potentially thousands of characters of stdout/stderr in -/// the background event stream by: -/// - limiting the number of lines shown per stream -/// - collapsing consecutive duplicate lines into a single line with a repeat count -/// - truncating overly long lines -fn summarize_sandbox_denied_streams(exit_code: i32, stdout: &str, stderr: &str) -> String { - const MAX_LINES_PER_STREAM: usize = 8; - const MAX_LINE_LEN: usize = 160; - - fn summarize_stream(label: &str, s: &str) -> String { - if s.trim().is_empty() { - return format!("{label}: "); - } - - let total_lines = s.lines().count(); - let mut lines = Vec::new(); - let mut iter = s.lines().peekable(); - let mut consumed_lines = 0usize; - while lines.len() < MAX_LINES_PER_STREAM { - let line = match iter.next() { - Some(l) => l, - None => break, - }; - // Count consecutive duplicates so we can collapse noisy spam. - let mut repeat = 1usize; - while let Some(&next) = iter.peek() { - if next == line { - repeat += 1; - iter.next(); - } else { - break; - } - } - consumed_lines += repeat; - - let mut display = if line.len() > MAX_LINE_LEN { - // Truncate long lines to keep the summary compact. - let mut truncated = line.chars().take(MAX_LINE_LEN).collect::(); - truncated.push('…'); - truncated - } else { - line.to_string() - }; - if repeat > 1 { - display.push_str(&format!(" (repeated {repeat} times)")); - } - lines.push(display); - } - - let omitted = total_lines.saturating_sub(consumed_lines); - if omitted > 0 { - lines.push(format!("… +{omitted} more lines omitted")); - } - format!("{label}:\n{}", lines.join("\n")) - } - - // Prefer stderr as it usually contains the failure context. - let mut parts = Vec::with_capacity(3); - parts.push(format!( - "Execution failed in sandbox: exit code {exit_code}" - )); - if !stderr.is_empty() { - parts.push(summarize_stream("stderr", stderr)); - } - if !stdout.is_empty() { - parts.push(summarize_stream("stdout", stdout)); - } - parts.join("\n") -} - /// Exec output is a pre-serialized JSON payload fn format_exec_output(output: &str, exit_code: i32, duration: Duration) -> String { #[derive(Serialize)]