[codex] Send realtime transcript deltas on handoff (#18761)

## Summary
- Track how many realtime transcript entries have already been attached
to a background-agent handoff.
- Attach only entries added since the previous handoff as
`<transcript_delta>` instead of resending the accumulated transcript
snapshot.
- Update the realtime integration test so the second delegation carries
only the second transcript delta.

## Validation
- `just fmt`
- `cargo test -p codex-api`
- `cargo test -p codex-core
inbound_handoff_request_sends_transcript_delta_after_each_handoff`
- `cargo build -p codex-cli -p codex-app-server`

## Manual testing
Built local debug binaries at:
- `codex-rs/target/debug/codex`
- `codex-rs/target/debug/codex-app-server`
This commit is contained in:
guinness-oai
2026-04-20 16:46:15 -07:00
committed by GitHub
Unverified
parent 216e7a0a56
commit ca3246f77a
2 changed files with 8 additions and 4 deletions
@@ -217,6 +217,7 @@ pub struct RealtimeWebsocketEvents {
#[derive(Default)]
struct ActiveTranscriptState {
entries: Vec<RealtimeTranscriptEntry>,
last_handoff_entry_count: usize,
new_input_entry: bool,
new_output_entry: bool,
}
@@ -459,7 +460,10 @@ impl RealtimeWebsocketEvents {
}
RealtimeEvent::HandoffRequested(handoff) => {
append_handoff_input(&mut active_transcript.entries, &handoff.input_transcript);
handoff.active_transcript = active_transcript.entries.clone();
handoff.active_transcript = active_transcript.entries
[active_transcript.last_handoff_entry_count..]
.to_vec();
active_transcript.last_handoff_entry_count = active_transcript.entries.len();
active_transcript.new_input_entry = true;
active_transcript.new_output_entry = true;
}
@@ -2661,7 +2661,7 @@ async fn inbound_handoff_request_uses_active_transcript() -> Result<()> {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn inbound_handoff_request_keeps_transcript_history_after_each_handoff() -> Result<()> {
async fn inbound_handoff_request_sends_transcript_delta_after_each_handoff() -> Result<()> {
skip_if_no_network!(Ok(()));
let api_server = start_mock_server().await;
@@ -2773,9 +2773,9 @@ async fn inbound_handoff_request_keeps_transcript_history_after_each_handoff() -
let second_user_texts = requests[1].message_input_texts("user");
assert!(second_user_texts.iter().any(|text| text
== "<realtime_delegation>\n <input>second question</input>\n <transcript_delta>user: first question\nuser: second question</transcript_delta>\n</realtime_delegation>"));
== "<realtime_delegation>\n <input>second question</input>\n <transcript_delta>user: second question</transcript_delta>\n</realtime_delegation>"));
assert!(!second_user_texts.iter().any(|text| text
== "<realtime_delegation>\n <input>second question</input>\n <transcript_delta>user: first questionsecond question</transcript_delta>\n</realtime_delegation>"));
== "<realtime_delegation>\n <input>second question</input>\n <transcript_delta>user: first question\nuser: second question</transcript_delta>\n</realtime_delegation>"));
realtime_server.shutdown().await;
Ok(())