Route realtime V3 handoffs by response channel (#33903)

## What changed

- Add `codexResponseHandoffMode` to `thread/realtime/start` for V3 sessions, with `thinking` as the default and `commentary` and `bemTags` routing modes.
- Map BEM `analysis` and `commentary` output to the commentary channel and `final` output to the speakable channel, while preserving the original BEM envelope. Unrecognized output falls back to speakable.
- Mark explicit speech appends as speakable and stop adding the legacy `"Agent Final Message"` prefix to V3 handoffs. V1 and V2 continue to ignore the new setting.

## Testing

- Cover channel selection for every routing mode, streamed BEM header parsing, fallback behavior, explicit speech, and V1 compatibility.

GitOrigin-RevId: 1974578257488f2914b252c9c1990ba38831c96b
This commit is contained in:
jiayuhuang-openai authored and copyberry committed 2026-07-17 21:12:40 +00:00
1 parent 161748a68e
commit 025db22058
26 files changed
+851 -167

No files matched your search

@@ -1,3 +1,4 @@
use codex_protocol::protocol::CodexResponseHandoffMode;
use codex_protocol::protocol::ConversationTextRole;
use codex_protocol::protocol::RealtimeAudioFrame as CoreRealtimeAudioFrame;
use codex_protocol::protocol::RealtimeConversationVersion;
@@ -74,12 +75,18 @@ pub struct ThreadRealtimeStartParams {
/// TODO: Remove this rollout knob once transcript-tail flushing is always enabled.
#[ts(optional = nullable)]
pub flush_transcript_tail_on_session_end: Option<bool>,
// TODO: Remove this experiment-only delivery path after response-item testing is complete.
/// Sends automatic Codex responses as realtime conversation items instead of handoff appends.
#[ts(optional = nullable)]
pub codex_responses_as_items: Option<bool>,
// TODO: Remove this experiment-only prefix with `codex_responses_as_items`.
/// Optional prefix added to automatic Codex response items when `codexResponsesAsItems` is true.
#[ts(optional = nullable)]
pub codex_response_item_prefix: Option<String>,
/// Selects how automatic Codex responses are routed in Frameless Bidi sessions. Omitted values
/// default to `thinking`. Realtime V1 and V2 ignore this setting.
#[ts(optional = nullable)]
pub codex_response_handoff_mode: Option<CodexResponseHandoffMode>,
/// Overrides the configured realtime model for this session only.
#[ts(optional = nullable)]
pub model: Option<String>,