mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Support attaching to existing realtime calls (#39876)
## Why Clients that create and negotiate realtime calls themselves need Codex to join those calls without creating another call or overwriting the client-owned session configuration. ## What changed - Add the `existingCall` transport to `thread/realtime/start`, accepting a client-provided `callId` and optional `realtimeSessionId`. - Attach Codex over the call's sideband WebSocket without SDP negotiation or a session update, and reconnect that sideband while preserving transcript state. - Default existing calls to realtime v1, support v1 and v3, and reject v2 or options that would reconfigure the client-owned session. - Encode call IDs as a single URL path segment for v3 sideband connections. ## Testing Add protocol, app-server, core, reconnection, authentication-header, and URL encoding coverage for existing-call attachments. GitOrigin-RevId: 2046b70566f98efa55381bb1e461ea3cc68256c5
This commit is contained in:
@@ -1082,6 +1082,36 @@ impl TurnRequestProcessor {
|
||||
request_id: &ConnectionRequestId,
|
||||
params: ThreadRealtimeStartParams,
|
||||
) -> Result<Option<ThreadRealtimeStartResponse>, JSONRPCErrorError> {
|
||||
let attaches_existing_call = matches!(
|
||||
¶ms.transport,
|
||||
Some(ThreadRealtimeStartTransport::ExistingCall { .. })
|
||||
);
|
||||
if attaches_existing_call {
|
||||
let unsupported_option = if params.include_startup_context == Some(true) {
|
||||
Some("includeStartupContext")
|
||||
} else if params.prompt.is_some() {
|
||||
Some("prompt")
|
||||
} else if params
|
||||
.initial_items
|
||||
.as_ref()
|
||||
.is_some_and(|items| !items.is_empty())
|
||||
{
|
||||
Some("initialItems")
|
||||
} else if params.model.is_some() {
|
||||
Some("model")
|
||||
} else if params.voice.is_some() {
|
||||
Some("voice")
|
||||
} else if params.delegation_ack_filler.is_some() {
|
||||
Some("delegationAckFiller")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(option) = unsupported_option {
|
||||
return Err(invalid_request(format!(
|
||||
"existingCall transport does not support {option}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
let Some((_, thread)) = self
|
||||
.prepare_realtime_conversation_thread(request_id, ¶ms.thread_id)
|
||||
.await?
|
||||
@@ -1104,7 +1134,9 @@ impl TurnRequestProcessor {
|
||||
.codex_response_handoff_channel_prefixes,
|
||||
model: params.model,
|
||||
output_modality: params.output_modality,
|
||||
include_startup_context: params.include_startup_context.unwrap_or(true),
|
||||
include_startup_context: params
|
||||
.include_startup_context
|
||||
.unwrap_or(!attaches_existing_call),
|
||||
initial_items: params
|
||||
.initial_items
|
||||
.unwrap_or_default()
|
||||
@@ -1125,6 +1157,9 @@ impl TurnRequestProcessor {
|
||||
ThreadRealtimeStartTransport::Webrtc { sdp } => {
|
||||
ConversationStartTransport::Webrtc { sdp }
|
||||
}
|
||||
ThreadRealtimeStartTransport::ExistingCall { call_id } => {
|
||||
ConversationStartTransport::ExistingCall { call_id }
|
||||
}
|
||||
}),
|
||||
version: params.version,
|
||||
voice: params.voice,
|
||||
|
||||
Reference in New Issue
Block a user