Compare commits

...

1 Commits

Author SHA1 Message Date
Jiaming Zhang
2dab970ee4 Send installation id in Responses headers 2026-05-29 17:26:59 -07:00
5 changed files with 56 additions and 4 deletions

View File

@@ -507,9 +507,6 @@ impl ModelClient {
};
let mut extra_headers = ApiHeaderMap::new();
if let Ok(header_value) = HeaderValue::from_str(&self.state.installation_id) {
extra_headers.insert(X_CODEX_INSTALLATION_ID_HEADER, header_value);
}
extra_headers.extend(build_responses_headers(
self.state.beta_features_header.as_deref(),
/*turn_state*/ None,
@@ -637,6 +634,11 @@ impl ModelClient {
fn build_responses_identity_headers(&self) -> ApiHeaderMap {
let mut extra_headers = self.build_subagent_headers();
if !self.state.installation_id.is_empty()
&& let Ok(val) = HeaderValue::from_str(&self.state.installation_id)
{
extra_headers.insert(X_CODEX_INSTALLATION_ID_HEADER, val);
}
if let Some(parent_thread_id) = parent_thread_id_header_value(&self.state.session_source)
&& let Ok(val) = HeaderValue::from_str(&parent_thread_id)
{

View File

@@ -61,13 +61,20 @@ use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::util::SubscriberInitExt;
fn test_model_client(session_source: SessionSource) -> ModelClient {
test_model_client_with_installation_id(session_source, "11111111-1111-4111-8111-111111111111")
}
fn test_model_client_with_installation_id(
session_source: SessionSource,
installation_id: &str,
) -> ModelClient {
let provider = create_oss_provider_with_base_url("https://example.com/v1", WireApi::Responses);
let thread_id = ThreadId::new();
ModelClient::new(
/*auth_manager*/ None,
thread_id.into(),
thread_id,
/*installation_id*/ "11111111-1111-4111-8111-111111111111".to_string(),
/*installation_id*/ installation_id.to_string(),
provider,
session_source,
/*model_verbosity*/ None,
@@ -269,6 +276,23 @@ fn build_subagent_headers_sets_internal_memory_consolidation_label() {
assert_eq!(value, Some("memory_consolidation"));
}
#[test]
fn build_responses_identity_headers_includes_installation_id_when_present() {
let client = test_model_client(SessionSource::Exec);
let headers = client.build_responses_identity_headers();
let value = headers
.get(X_CODEX_INSTALLATION_ID_HEADER)
.and_then(|value| value.to_str().ok());
assert_eq!(value, Some("11111111-1111-4111-8111-111111111111"));
}
#[test]
fn build_responses_identity_headers_omits_installation_id_when_empty() {
let client = test_model_client_with_installation_id(SessionSource::Exec, "");
let headers = client.build_responses_identity_headers();
assert_eq!(headers.get(X_CODEX_INSTALLATION_ID_HEADER), None);
}
#[test]
fn build_ws_client_metadata_includes_window_lineage_and_turn_metadata() {
let parent_thread_id = ThreadId::new();
@@ -549,6 +573,17 @@ async fn websocket_handshake_includes_attestation_for_chatgpt_codex_responses()
assert_eq!(attestation_calls.load(Ordering::Relaxed), 1);
}
#[tokio::test]
async fn websocket_handshake_omits_installation_id_when_empty() {
let model_client = test_model_client_with_installation_id(SessionSource::Exec, "");
let headers = model_client
.build_websocket_headers(/*turn_state*/ None, /*turn_metadata_header*/ None)
.await;
assert_eq!(headers.get(X_CODEX_INSTALLATION_ID_HEADER), None);
}
#[tokio::test]
async fn non_chatgpt_codex_endpoints_omit_attestation_generation() {
let (model_client, attestation_calls) =

View File

@@ -152,6 +152,10 @@ async fn responses_stream_includes_subagent_header_on_review() {
request.header("x-codex-window-id").as_deref(),
Some(expected_window_id.as_str())
);
assert_eq!(
request.header("x-codex-installation-id").as_deref(),
Some(TEST_INSTALLATION_ID)
);
assert_eq!(request.header("x-codex-parent-thread-id"), None);
assert_eq!(
request.body_json()["client_metadata"]["x-codex-installation-id"].as_str(),

View File

@@ -148,6 +148,10 @@ async fn responses_websocket_streams_request() {
handshake.header(USER_AGENT_HEADER),
Some(codex_login::default_client::get_codex_user_agent())
);
assert_eq!(
handshake.header("x-codex-installation-id"),
Some(TEST_INSTALLATION_ID.to_string())
);
assert_eq!(
body["client_metadata"]["x-codex-installation-id"].as_str(),
Some(TEST_INSTALLATION_ID)

View File

@@ -368,6 +368,13 @@ async fn remote_compact_replaces_history_for_followups() -> Result<()> {
compact_request.header("thread-id").as_deref(),
Some(thread_id.as_str())
);
assert!(
compact_request
.header("x-codex-installation-id")
.as_deref()
.is_some_and(|value| !value.is_empty()),
"compact request should include a non-empty installation id header"
);
let compact_metadata: Value = serde_json::from_str(
&compact_request
.header("x-codex-turn-metadata")