remove sandbox denied swallowing code for being bad

This commit is contained in:
easong-openai
2025-08-04 11:24:19 -07:00
parent 804d1c2ead
commit 8c86009483

View File

@@ -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}: <empty>");
}
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::<String>();
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)]