Include the final agent message in turn completion summaries (#34777)

## Why

Streamed message deltas can be dropped when the transport is saturated, leaving
clients with a truncated final response.

## What changed

- Include the last non-empty final agent message in successful `turn/completed`
  notifications and mark the items as a summary.
- Treat completed message content as authoritative in the TUI, repairing an
  incomplete stream while avoiding duplicate messages when `item/completed` was
  already received.
- Continue backfilling full turn items for persisted `codex exec` threads when a
  completion contains only the summary view.

## Testing

Added coverage for completion summaries, dropped stream deltas, duplicate item
handling, and directive-only responses.

GitOrigin-RevId: ffe9d36d683d6b6690e4a468ff68aa9189fe6ab7
This commit is contained in:
Eric Traut authored and copyberry committed 2026-07-22 15:41:47 +00:00
1 parent 730ec92003
commit 80f3c3141e
15 files changed
+275 -56

No files matched your search

+33 -2
View File
@@ -243,12 +243,43 @@ impl ChatWidget {
self.last_rendered_user_message_display = None;
match notification.turn.status {
TurnStatus::Completed => {
let last_agent_message =
notification
.turn
.items
.iter()
.rev()
.find_map(|item| match item {
ThreadItem::AgentMessage {
id,
text,
phase: Some(MessagePhase::FinalAnswer) | None,
..
} => Some((item.clone(), id.clone(), text.clone())),
_ => None,
});
if let Some((item, id, _)) = &last_agent_message
&& self
.transcript
.last_completed_agent_message
.as_ref()
.is_none_or(|(turn_id, item_id)| {
turn_id != &notification.turn.id || item_id != id
})
{
self.handle_thread_item(
item.clone(),
notification.turn.id.clone(),
replay_kind
.map_or(ThreadItemRenderSource::Live, ThreadItemRenderSource::Replay),
);
}
self.last_non_retry_error = None;
self.on_task_complete(
/*last_agent_message*/ None,
last_agent_message.map(|(_, _, text)| text),
notification.turn.duration_ms,
replay_kind.is_some(),
)
);
}
TurnStatus::Interrupted => {
self.last_non_retry_error = None;