tui: coalesce command output; show unabridged commands in transcript (#2590)

https://github.com/user-attachments/assets/effec7c7-732a-4b61-a2ae-3cb297b6b19b
This commit is contained in:
Jeremy Rose
2025-08-22 16:32:31 -07:00
committed by GitHub
parent 6de9541f0a
commit d994019f3f
13 changed files with 394 additions and 209 deletions

View File

@@ -701,7 +701,6 @@ impl Session {
let _ = self.tx_event.send(event).await;
}
#[allow(clippy::too_many_arguments)]
async fn on_exec_command_end(
&self,
turn_diff_tracker: &mut TurnDiffTracker,
@@ -721,6 +720,7 @@ impl Session {
const MAX_STREAM_OUTPUT: usize = 5 * 1024; // 5KiB
let stdout = stdout.text.chars().take(MAX_STREAM_OUTPUT).collect();
let stderr = stderr.text.chars().take(MAX_STREAM_OUTPUT).collect();
let formatted_output = format_exec_output_str(output);
let msg = if is_apply_patch {
EventMsg::PatchApplyEnd(PatchApplyEndEvent {
@@ -734,6 +734,7 @@ impl Session {
call_id: call_id.to_string(),
stdout,
stderr,
formatted_output,
duration: *duration,
exit_code: *exit_code,
})
@@ -2357,7 +2358,7 @@ async fn handle_container_exec_with_params(
let ExecToolCallOutput { exit_code, .. } = &output;
let is_success = *exit_code == 0;
let content = format_exec_output(output);
let content = format_exec_output(&output);
ResponseInputItem::FunctionCallOutput {
call_id: call_id.clone(),
output: FunctionCallOutputPayload {
@@ -2490,7 +2491,7 @@ async fn handle_sandbox_error(
let ExecToolCallOutput { exit_code, .. } = &retry_output;
let is_success = *exit_code == 0;
let content = format_exec_output(retry_output);
let content = format_exec_output(&retry_output);
ResponseInputItem::FunctionCallOutput {
call_id: call_id.clone(),
@@ -2522,13 +2523,33 @@ async fn handle_sandbox_error(
}
}
/// Exec output is a pre-serialized JSON payload
fn format_exec_output(exec_output: ExecToolCallOutput) -> String {
fn format_exec_output_str(exec_output: &ExecToolCallOutput) -> String {
let ExecToolCallOutput {
exit_code,
stdout,
stderr,
..
} = exec_output;
let is_success = *exit_code == 0;
let output = if is_success { stdout } else { stderr };
let mut formatted_output = output.text.clone();
if let Some(truncated_after_lines) = output.truncated_after_lines {
formatted_output.push_str(&format!(
"\n\n[Output truncated after {truncated_after_lines} lines: too many lines or bytes.]",
));
}
formatted_output
}
/// Exec output is a pre-serialized JSON payload
fn format_exec_output(exec_output: &ExecToolCallOutput) -> String {
let ExecToolCallOutput {
exit_code,
duration,
..
} = exec_output;
#[derive(Serialize)]
@@ -2546,20 +2567,12 @@ fn format_exec_output(exec_output: ExecToolCallOutput) -> String {
// round to 1 decimal place
let duration_seconds = ((duration.as_secs_f32()) * 10.0).round() / 10.0;
let is_success = exit_code == 0;
let output = if is_success { stdout } else { stderr };
let mut formatted_output = output.text;
if let Some(truncated_after_lines) = output.truncated_after_lines {
formatted_output.push_str(&format!(
"\n\n[Output truncated after {truncated_after_lines} lines: too many lines or bytes.]",
));
}
let formatted_output = format_exec_output_str(exec_output);
let payload = ExecOutput {
output: &formatted_output,
metadata: ExecMetadata {
exit_code,
exit_code: *exit_code,
duration_seconds,
},
};