diff --git a/codex-rs/core/src/client.rs b/codex-rs/core/src/client.rs index 3631f3afb..b8568a104 100644 --- a/codex-rs/core/src/client.rs +++ b/codex-rs/core/src/client.rs @@ -82,6 +82,8 @@ use crate::model_provider_info::ModelProviderInfo; use crate::model_provider_info::WireApi; use crate::tools::spec::create_tools_json_for_responses_api; +pub const OPENAI_BETA_HEADER: &str = "OpenAI-Beta"; +pub const OPENAI_BETA_RESPONSES_WEBSOCKETS: &str = "responses_websockets=2026-02-04"; pub const X_CODEX_TURN_STATE_HEADER: &str = "x-codex-turn-state"; pub const X_CODEX_TURN_METADATA_HEADER: &str = "x-codex-turn-metadata"; pub const X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER: &str = @@ -477,14 +479,8 @@ impl ModelClientSession { }; if needs_new { - let mut headers = options.extra_headers.clone(); - headers.extend(build_conversation_headers(options.conversation_id.clone())); - if self.client.state.include_timing_metrics { - headers.insert( - X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER, - HeaderValue::from_static("true"), - ); - } + let headers = + build_websocket_connect_headers(options, self.client.state.include_timing_metrics); let websocket_telemetry = Self::build_websocket_telemetry(otel_manager); let new_conn: ApiWebSocketConnection = ApiWebSocketResponsesClient::new(api_provider, api_auth) @@ -787,6 +783,25 @@ fn build_responses_headers( headers } +fn build_websocket_connect_headers( + options: &ApiResponsesOptions, + include_timing_metrics: bool, +) -> ApiHeaderMap { + let mut headers = options.extra_headers.clone(); + headers.extend(build_conversation_headers(options.conversation_id.clone())); + headers.insert( + OPENAI_BETA_HEADER, + HeaderValue::from_static(OPENAI_BETA_RESPONSES_WEBSOCKETS), + ); + if include_timing_metrics { + headers.insert( + X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER, + HeaderValue::from_static("true"), + ); + } + headers +} + fn map_response_stream(api_stream: S, otel_manager: OtelManager) -> ResponseStream where S: futures::Stream> diff --git a/codex-rs/core/tests/suite/client_websockets.rs b/codex-rs/core/tests/suite/client_websockets.rs index e58a512f9..76a1f0b3c 100644 --- a/codex-rs/core/tests/suite/client_websockets.rs +++ b/codex-rs/core/tests/suite/client_websockets.rs @@ -40,6 +40,8 @@ use tempfile::TempDir; use tracing_test::traced_test; const MODEL: &str = "gpt-5.2-codex"; +const OPENAI_BETA_HEADER: &str = "OpenAI-Beta"; +const OPENAI_BETA_RESPONSES_WEBSOCKETS: &str = "responses_websockets=2026-02-04"; struct WebsocketTestHarness { _codex_home: TempDir, @@ -74,6 +76,11 @@ async fn responses_websocket_streams_request() { assert_eq!(body["model"].as_str(), Some(MODEL)); assert_eq!(body["stream"], serde_json::Value::Bool(true)); assert_eq!(body["input"].as_array().map(Vec::len), Some(1)); + let handshake = server.single_handshake(); + assert_eq!( + handshake.header(OPENAI_BETA_HEADER), + Some(OPENAI_BETA_RESPONSES_WEBSOCKETS.to_string()) + ); server.shutdown().await; }