Add async delivery metadata to agent messages (#39312)

## What changed

- Add an optional `delivery` field to agent message events and app-server
  `agentMessage` items, with `"async"` identifying a user-visible message sent
  without ending the current turn.
- Preserve the marker through legacy event conversion, thread history
  materialization, replay, and generated JSON and TypeScript schemas.
- Require delivery of async agent message completion notifications on the
  in-process app-server transport.

## Testing

- Cover preservation of async delivery metadata in thread history.
- Cover delivery-required classification for async agent messages.

GitOrigin-RevId: f92ba25de1293ee271404badfdf0d117d6530329
This commit is contained in:
rka-oai authored and copyberry committed 2026-08-18 23:05:52 +00:00
1 parent 7d9990fa30
commit fb356f3d2c
64 files changed
+467 -21

No files matched your search

@@ -37,6 +37,7 @@ fn agent_status_uses_bounded_buffered_activity() {
text: "Finished checking the focused TUI tests.".to_string(),
phase: None,
memory_citation: None,
delivery: None,
},
thread_id: "thread-child".to_string(),
turn_id: "turn-1".to_string(),
+2
View File
@@ -3497,6 +3497,7 @@ async fn active_thread_file_change_approval_recovers_buffered_changes() {
text: "streaming".to_string(),
phase: None,
memory_citation: None,
delivery: None,
},
}),
/*replay_kind*/ None,
@@ -7235,6 +7236,7 @@ async fn replay_thread_snapshot_replays_turn_history_in_order() {
text: "done".to_string(),
phase: None,
memory_citation: None,
delivery: None,
},
],
status: TurnStatus::Completed,
@@ -366,6 +366,7 @@ async fn older_pagination_reconciles_review_prompts_across_page_boundaries() ->
}],
phase: None,
memory_citation: None,
delivery: None,
})
}));
items.extend([
@@ -554,6 +555,7 @@ async fn transcript_home_loads_every_older_history_page() -> Result<()> {
}],
phase: None,
memory_citation: None,
delivery: None,
}),
started_at_ms: None,
completed_at_ms: 0,
@@ -919,6 +921,7 @@ async fn underfilled_scrollback_fetches_older_pages_without_opening_the_transcri
}],
phase: None,
memory_citation: None,
delivery: None,
}),
started_at_ms: None,
completed_at_ms: 0,
+1
View File
@@ -3360,6 +3360,7 @@ mod tests {
text: "assistant reply".to_string(),
phase: None,
memory_citation: None,
delivery: None,
},
],
status: TurnStatus::Completed,
+3
View File
@@ -94,6 +94,8 @@ impl ChatWidget {
text,
phase,
memory_citation,
delivery,
..
} => {
self.on_agent_message_item_completed(
AgentMessageItem {
@@ -117,6 +119,7 @@ impl ChatWidget {
rollout_ids: citation.thread_ids,
}
}),
delivery,
},
&turn_id,
from_replay,
@@ -621,6 +621,7 @@ async fn live_app_server_turn_completed_clears_working_status_after_answer_item(
text: "Yes. What do you need?".to_string(),
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
};
chat.handle_server_notification(
ServerNotification::ItemCompleted(ItemCompletedNotification {
@@ -1236,6 +1237,7 @@ async fn live_app_server_turn_completion_repairs_dropped_message_deltas() {
.to_string(),
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
}];
chat.handle_server_notification(
ServerNotification::TurnCompleted(TurnCompletedNotification {
@@ -767,6 +767,7 @@ pub(super) fn replay_agent_message(
text: text.into(),
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
},
"turn-1".to_string(),
replay_kind,
@@ -919,6 +920,7 @@ pub(super) fn complete_assistant_message(
text: text.to_string(),
phase,
memory_citation: None,
delivery: None,
},
}),
/*replay_kind*/ None,
@@ -332,6 +332,7 @@ async fn replayed_nested_review_prompts_do_not_render_or_seed_composer_history()
text: "review result is retained".to_string(),
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
},
],
..app_server_turn(
@@ -869,6 +869,7 @@ async fn plan_implementation_popup_skips_replayed_turn_complete() {
text: "Plan details".to_string(),
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
}],
status: AppServerTurnStatus::Completed,
error: None,
@@ -907,6 +908,7 @@ async fn plan_implementation_popup_shows_once_when_replay_precedes_live_turn_com
text: "Plan details".to_string(),
phase: Some(MessagePhase::FinalAnswer),
memory_citation: None,
delivery: None,
}],
status: AppServerTurnStatus::Completed,
error: None,
+1
View File
@@ -6332,6 +6332,7 @@ session_picker_view = "dense"
text: String::from("hello from assistant"),
phase: None,
memory_citation: None,
delivery: None,
},
ThreadItem::Plan {
id: String::from("plan-1"),
@@ -41,6 +41,7 @@ fn rollout_agent_message(text: &str) -> RolloutItem {
message: text.to_string(),
phase: None,
memory_citation: None,
delivery: None,
}))
}
@@ -258,6 +259,7 @@ fn transcript_preview_reverse_scan_stops_before_older_items() {
text: String::from("one\ntwo\nthree\nfour\nfive\nsix"),
phase: None,
memory_citation: None,
delivery: None,
};
let mut lines = Vec::new();