Track TUI command completion separately from output (#34218)

## Why

App-server command output deltas populate an exec cell before the command
finishes. Treating the presence of output as completion can therefore mark a
streaming command inactive too early and mishandle it when the turn is
interrupted.

## What changed

- Use the recorded duration, which is set when a command ends, as the exec
  call's completion marker.
- Preserve streamed output when marking unfinished calls as failed and render
  status lines only after completion.
- Keep a single aggregated output representation and render truncated output
  without collecting every line first.
- Append grouped exploration calls in place and coalesce adjacent reads while
  rendering without cloning the calls.

## Testing

- Add snapshots covering active streamed output, transcript output, and the
  resulting history entry after interruption.
- Add coverage for truncating newline-dense output.

GitOrigin-RevId: 420ac1e5fe1b9fcdbdaa5a8998f3e43a5b720254
This commit is contained in:
Charlie Marsh authored and copyberry committed 2026-07-19 19:44:10 +00:00
1 parent 74bfbda9b5
commit 854a82dbfd
10 files changed
+179 -109

No files matched your search

@@ -284,7 +284,7 @@ impl ChatWidget {
.active_cell
.as_mut()
.and_then(|c| c.as_any_mut().downcast_mut::<ExecCell>())
&& let Some(new_exec) = cell.with_added_call(
&& cell.add_call(
id.clone(),
command.clone(),
parsed_cmd.clone(),
@@ -292,7 +292,6 @@ impl ChatWidget {
/*interaction_input*/ None,
)
{
*cell = new_exec;
self.bump_active_cell_revision();
} else {
self.flush_active_cell();
@@ -383,13 +382,11 @@ impl ChatWidget {
let output = if is_unified_exec_interaction {
CommandOutput {
exit_code,
formatted_output: String::new(),
aggregated_output: String::new(),
}
} else {
CommandOutput {
exit_code,
formatted_output: aggregated_output.clone(),
aggregated_output,
}
};