From fbe883318d89c320903261cd6ab228d7ebcbd2aa Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Fri, 9 Jan 2026 12:09:30 -0800 Subject: [PATCH] fix(app-server): set originator header from initialize (re-revert) (#8988) Reapplies https://github.com/openai/codex/pull/8873 which was reverted due to merge conflicts --- codex-rs/app-server/README.md | 6 +- codex-rs/app-server/src/lib.rs | 19 ++- codex-rs/app-server/src/message_processor.rs | 23 +++ codex-rs/app-server/tests/common/lib.rs | 1 + .../app-server/tests/common/mcp_process.rs | 71 ++++++--- codex-rs/app-server/tests/suite/user_agent.rs | 5 +- .../app-server/tests/suite/v2/initialize.rs | 137 ++++++++++++++++++ codex-rs/app-server/tests/suite/v2/mod.rs | 1 + .../app-server/tests/suite/v2/turn_start.rs | 71 +++++++++ codex-rs/core/src/default_client.rs | 48 ++++-- codex-rs/core/src/otel_init.rs | 6 +- codex-rs/core/src/rollout/recorder.rs | 2 +- codex-rs/exec/src/lib.rs | 3 +- codex-rs/tui/src/lib.rs | 3 +- codex-rs/tui2/src/lib.rs | 3 +- 15 files changed, 353 insertions(+), 46 deletions(-) create mode 100644 codex-rs/app-server/tests/suite/v2/initialize.rs diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 04e97ed62..597f002c0 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -52,6 +52,10 @@ Clients must send a single `initialize` request before invoking any other method Applications building on top of `codex app-server` should identify themselves via the `clientInfo` parameter. +**Important**: `clientInfo.name` is used to identify the client for the OpenAI Compliance Logs Platform. If +you are developing a new Codex integration that is intended for enterprise use, please contact us to get it +added to a known clients list. For more context: https://chatgpt.com/admin/api-reference#tag/Logs:-Codex + Example (from OpenAI's official VSCode extension): ```json @@ -60,7 +64,7 @@ Example (from OpenAI's official VSCode extension): "id": 0, "params": { "clientInfo": { - "name": "codex-vscode", + "name": "codex_vscode", "title": "Codex VS Code Extension", "version": "0.1.0" } diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index a2d14bf7d..68739c008 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -92,13 +92,18 @@ pub async fn run_main( let feedback = CodexFeedback::new(); - let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), false) - .map_err(|e| { - std::io::Error::new( - ErrorKind::InvalidData, - format!("error loading otel config: {e}"), - ) - })?; + let otel = codex_core::otel_init::build_provider( + &config, + env!("CARGO_PKG_VERSION"), + Some("codex_app_server"), + false, + ) + .map_err(|e| { + std::io::Error::new( + ErrorKind::InvalidData, + format!("error loading otel config: {e}"), + ) + })?; // Install a simple subscriber so `tracing` output is visible. Users can // control the log level with `RUST_LOG`. diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 60e938bb1..1f442b995 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -21,8 +21,10 @@ use codex_core::AuthManager; use codex_core::ThreadManager; use codex_core::config::Config; use codex_core::config_loader::LoaderOverrides; +use codex_core::default_client::SetOriginatorError; use codex_core::default_client::USER_AGENT_SUFFIX; use codex_core::default_client::get_codex_user_agent; +use codex_core::default_client::set_default_originator; use codex_feedback::CodexFeedback; use codex_protocol::protocol::SessionSource; use toml::Value as TomlValue; @@ -121,6 +123,27 @@ impl MessageProcessor { title: _title, version, } = params.client_info; + if let Err(error) = set_default_originator(name.clone()) { + match error { + SetOriginatorError::InvalidHeaderValue => { + let error = JSONRPCErrorError { + code: INVALID_REQUEST_ERROR_CODE, + message: format!( + "Invalid clientInfo.name: '{name}'. Must be a valid HTTP header value." + ), + data: None, + }; + self.outgoing.send_error(request_id, error).await; + return; + } + SetOriginatorError::AlreadyInitialized => { + // No-op. This is expected to happen if the originator is already set via env var. + // TODO(owen): Once we remove support for CODEX_INTERNAL_ORIGINATOR_OVERRIDE, + // this will be an unexpected state and we can return a JSON-RPC error indicating + // internal server error. + } + } + } let user_agent_suffix = format!("{name}; {version}"); if let Ok(mut suffix) = USER_AGENT_SUFFIX.lock() { *suffix = Some(user_agent_suffix); diff --git a/codex-rs/app-server/tests/common/lib.rs b/codex-rs/app-server/tests/common/lib.rs index 594bb78c2..af4982b84 100644 --- a/codex-rs/app-server/tests/common/lib.rs +++ b/codex-rs/app-server/tests/common/lib.rs @@ -17,6 +17,7 @@ pub use core_test_support::format_with_current_shell_non_login; pub use core_test_support::test_path_buf_with_windows; pub use core_test_support::test_tmp_path; pub use core_test_support::test_tmp_path_buf; +pub use mcp_process::DEFAULT_CLIENT_NAME; pub use mcp_process::McpProcess; pub use mock_model_server::create_mock_responses_server_repeating_assistant; pub use mock_model_server::create_mock_responses_server_sequence; diff --git a/codex-rs/app-server/tests/common/mcp_process.rs b/codex-rs/app-server/tests/common/mcp_process.rs index 36880228d..cde6ff391 100644 --- a/codex-rs/app-server/tests/common/mcp_process.rs +++ b/codex-rs/app-server/tests/common/mcp_process.rs @@ -66,6 +66,8 @@ pub struct McpProcess { pending_messages: VecDeque, } +pub const DEFAULT_CLIENT_NAME: &str = "codex-app-server-tests"; + impl McpProcess { pub async fn new(codex_home: &Path) -> anyhow::Result { Self::new_with_env(codex_home, &[]).await @@ -136,33 +138,62 @@ impl McpProcess { /// Performs the initialization handshake with the MCP server. pub async fn initialize(&mut self) -> anyhow::Result<()> { - let params = Some(serde_json::to_value(InitializeParams { - client_info: ClientInfo { - name: "codex-app-server-tests".to_string(), + let initialized = self + .initialize_with_client_info(ClientInfo { + name: DEFAULT_CLIENT_NAME.to_string(), title: None, version: "0.1.0".to_string(), - }, - })?); - let req_id = self.send_request("initialize", params).await?; - let initialized = self.read_jsonrpc_message().await?; - let JSONRPCMessage::Response(response) = initialized else { + }) + .await?; + let JSONRPCMessage::Response(_) = initialized else { unreachable!("expected JSONRPCMessage::Response for initialize, got {initialized:?}"); }; - if response.id != RequestId::Integer(req_id) { - anyhow::bail!( - "initialize response id mismatch: expected {}, got {:?}", - req_id, - response.id - ); - } - - // Send notifications/initialized to ack the response. - self.send_notification(ClientNotification::Initialized) - .await?; - Ok(()) } + /// Sends initialize with the provided client info and returns the response/error message. + pub async fn initialize_with_client_info( + &mut self, + client_info: ClientInfo, + ) -> anyhow::Result { + let params = Some(serde_json::to_value(InitializeParams { client_info })?); + let request_id = self.send_request("initialize", params).await?; + let message = self.read_jsonrpc_message().await?; + match message { + JSONRPCMessage::Response(response) => { + if response.id != RequestId::Integer(request_id) { + anyhow::bail!( + "initialize response id mismatch: expected {}, got {:?}", + request_id, + response.id + ); + } + + // Send notifications/initialized to ack the response. + self.send_notification(ClientNotification::Initialized) + .await?; + + Ok(JSONRPCMessage::Response(response)) + } + JSONRPCMessage::Error(error) => { + if error.id != RequestId::Integer(request_id) { + anyhow::bail!( + "initialize error id mismatch: expected {}, got {:?}", + request_id, + error.id + ); + } + Ok(JSONRPCMessage::Error(error)) + } + JSONRPCMessage::Notification(notification) => { + anyhow::bail!("unexpected JSONRPCMessage::Notification: {notification:?}"); + } + JSONRPCMessage::Request(request) => { + anyhow::bail!("unexpected JSONRPCMessage::Request: {request:?}"); + } + } + } + /// Send a `newConversation` JSON-RPC request. pub async fn send_new_conversation_request( &mut self, diff --git a/codex-rs/app-server/tests/suite/user_agent.rs b/codex-rs/app-server/tests/suite/user_agent.rs index 5ed6cafde..9178a3ef5 100644 --- a/codex-rs/app-server/tests/suite/user_agent.rs +++ b/codex-rs/app-server/tests/suite/user_agent.rs @@ -1,4 +1,5 @@ use anyhow::Result; +use app_test_support::DEFAULT_CLIENT_NAME; use app_test_support::McpProcess; use app_test_support::to_response; use codex_app_server_protocol::GetUserAgentResponse; @@ -25,13 +26,13 @@ async fn get_user_agent_returns_current_codex_user_agent() -> Result<()> { .await??; let os_info = os_info::get(); - let originator = codex_core::default_client::originator().value.as_str(); + let originator = DEFAULT_CLIENT_NAME; let os_type = os_info.os_type(); let os_version = os_info.version(); let architecture = os_info.architecture().unwrap_or("unknown"); let terminal_ua = codex_core::terminal::user_agent(); let user_agent = format!( - "{originator}/0.0.0 ({os_type} {os_version}; {architecture}) {terminal_ua} (codex-app-server-tests; 0.1.0)" + "{originator}/0.0.0 ({os_type} {os_version}; {architecture}) {terminal_ua} ({DEFAULT_CLIENT_NAME}; 0.1.0)" ); let received: GetUserAgentResponse = to_response(response)?; diff --git a/codex-rs/app-server/tests/suite/v2/initialize.rs b/codex-rs/app-server/tests/suite/v2/initialize.rs new file mode 100644 index 000000000..b31a68833 --- /dev/null +++ b/codex-rs/app-server/tests/suite/v2/initialize.rs @@ -0,0 +1,137 @@ +use anyhow::Result; +use app_test_support::McpProcess; +use app_test_support::create_mock_responses_server_sequence_unchecked; +use app_test_support::to_response; +use codex_app_server_protocol::ClientInfo; +use codex_app_server_protocol::InitializeResponse; +use codex_app_server_protocol::JSONRPCMessage; +use pretty_assertions::assert_eq; +use std::path::Path; +use tempfile::TempDir; +use tokio::time::timeout; + +const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); + +#[tokio::test] +async fn initialize_uses_client_info_name_as_originator() -> Result<()> { + let responses = Vec::new(); + let server = create_mock_responses_server_sequence_unchecked(responses).await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri(), "never")?; + let mut mcp = McpProcess::new(codex_home.path()).await?; + + let message = timeout( + DEFAULT_READ_TIMEOUT, + mcp.initialize_with_client_info(ClientInfo { + name: "codex_vscode".to_string(), + title: Some("Codex VS Code Extension".to_string()), + version: "0.1.0".to_string(), + }), + ) + .await??; + + let JSONRPCMessage::Response(response) = message else { + anyhow::bail!("expected initialize response, got {message:?}"); + }; + let InitializeResponse { user_agent } = to_response::(response)?; + + assert!(user_agent.starts_with("codex_vscode/")); + Ok(()) +} + +#[tokio::test] +async fn initialize_respects_originator_override_env_var() -> Result<()> { + let responses = Vec::new(); + let server = create_mock_responses_server_sequence_unchecked(responses).await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri(), "never")?; + let mut mcp = McpProcess::new_with_env( + codex_home.path(), + &[( + "CODEX_INTERNAL_ORIGINATOR_OVERRIDE", + Some("codex_originator_via_env_var"), + )], + ) + .await?; + + let message = timeout( + DEFAULT_READ_TIMEOUT, + mcp.initialize_with_client_info(ClientInfo { + name: "codex_vscode".to_string(), + title: Some("Codex VS Code Extension".to_string()), + version: "0.1.0".to_string(), + }), + ) + .await??; + + let JSONRPCMessage::Response(response) = message else { + anyhow::bail!("expected initialize response, got {message:?}"); + }; + let InitializeResponse { user_agent } = to_response::(response)?; + + assert!(user_agent.starts_with("codex_originator_via_env_var/")); + Ok(()) +} + +#[tokio::test] +async fn initialize_rejects_invalid_client_name() -> Result<()> { + let responses = Vec::new(); + let server = create_mock_responses_server_sequence_unchecked(responses).await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri(), "never")?; + let mut mcp = McpProcess::new_with_env( + codex_home.path(), + &[("CODEX_INTERNAL_ORIGINATOR_OVERRIDE", None)], + ) + .await?; + + let message = timeout( + DEFAULT_READ_TIMEOUT, + mcp.initialize_with_client_info(ClientInfo { + name: "bad\rname".to_string(), + title: Some("Bad Client".to_string()), + version: "0.1.0".to_string(), + }), + ) + .await??; + + let JSONRPCMessage::Error(error) = message else { + anyhow::bail!("expected initialize error, got {message:?}"); + }; + + assert_eq!(error.error.code, -32600); + assert_eq!( + error.error.message, + "Invalid clientInfo.name: 'bad\rname'. Must be a valid HTTP header value." + ); + assert_eq!(error.error.data, None); + Ok(()) +} + +// Helper to create a config.toml pointing at the mock model server. +fn create_config_toml( + codex_home: &Path, + server_uri: &str, + approval_policy: &str, +) -> std::io::Result<()> { + let config_toml = codex_home.join("config.toml"); + std::fs::write( + config_toml, + format!( + r#" +model = "mock-model" +approval_policy = "{approval_policy}" +sandbox_mode = "read-only" + +model_provider = "mock_provider" + +[model_providers.mock_provider] +name = "Mock provider for test" +base_url = "{server_uri}/v1" +wire_api = "responses" +request_max_retries = 0 +stream_max_retries = 0 +"# + ), + ) +} diff --git a/codex-rs/app-server/tests/suite/v2/mod.rs b/codex-rs/app-server/tests/suite/v2/mod.rs index 44f417d8b..5c40c5fc1 100644 --- a/codex-rs/app-server/tests/suite/v2/mod.rs +++ b/codex-rs/app-server/tests/suite/v2/mod.rs @@ -1,5 +1,6 @@ mod account; mod config_rpc; +mod initialize; mod model_list; mod output_schema; mod rate_limits; diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 1a3643374..d798e7d34 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -8,6 +8,7 @@ use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::create_shell_command_sse_response; use app_test_support::format_with_current_shell_display; use app_test_support::to_response; +use codex_app_server_protocol::ClientInfo; use codex_app_server_protocol::CommandExecutionApprovalDecision; use codex_app_server_protocol::CommandExecutionRequestApprovalResponse; use codex_app_server_protocol::CommandExecutionStatus; @@ -40,6 +41,76 @@ use tempfile::TempDir; use tokio::time::timeout; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); +const TEST_ORIGINATOR: &str = "codex_vscode"; + +#[tokio::test] +async fn turn_start_sends_originator_header() -> Result<()> { + let responses = vec![create_final_assistant_message_sse_response("Done")?]; + let server = create_mock_responses_server_sequence_unchecked(responses).await; + + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri(), "never")?; + + let mut mcp = McpProcess::new(codex_home.path()).await?; + timeout( + DEFAULT_READ_TIMEOUT, + mcp.initialize_with_client_info(ClientInfo { + name: TEST_ORIGINATOR.to_string(), + title: Some("Codex VS Code Extension".to_string()), + version: "0.1.0".to_string(), + }), + ) + .await??; + + let thread_req = mcp + .send_thread_start_request(ThreadStartParams { + model: Some("mock-model".to_string()), + ..Default::default() + }) + .await?; + let thread_resp: JSONRPCResponse = timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(thread_req)), + ) + .await??; + let ThreadStartResponse { thread, .. } = to_response::(thread_resp)?; + + let turn_req = mcp + .send_turn_start_request(TurnStartParams { + thread_id: thread.id.clone(), + input: vec![V2UserInput::Text { + text: "Hello".to_string(), + }], + ..Default::default() + }) + .await?; + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), + ) + .await??; + + timeout( + DEFAULT_READ_TIMEOUT, + mcp.read_stream_until_notification_message("turn/completed"), + ) + .await??; + + let requests = server + .received_requests() + .await + .expect("failed to fetch received requests"); + assert!(!requests.is_empty()); + for request in requests { + let originator = request + .headers + .get("originator") + .expect("originator header missing"); + assert_eq!(originator.to_str()?, TEST_ORIGINATOR); + } + + Ok(()) +} #[tokio::test] async fn turn_start_emits_notifications_and_accepts_model_override() -> Result<()> { diff --git a/codex-rs/core/src/default_client.rs b/codex-rs/core/src/default_client.rs index 3cd882489..4ded10a3d 100644 --- a/codex-rs/core/src/default_client.rs +++ b/codex-rs/core/src/default_client.rs @@ -4,7 +4,7 @@ pub use codex_client::CodexRequestBuilder; use reqwest::header::HeaderValue; use std::sync::LazyLock; use std::sync::Mutex; -use std::sync::OnceLock; +use std::sync::RwLock; /// Set this to add a suffix to the User-Agent string. /// @@ -30,7 +30,7 @@ pub struct Originator { pub value: String, pub header_value: HeaderValue, } -static ORIGINATOR: OnceLock = OnceLock::new(); +static ORIGINATOR: LazyLock>> = LazyLock::new(|| RwLock::new(None)); #[derive(Debug)] pub enum SetOriginatorError { @@ -60,22 +60,48 @@ fn get_originator_value(provided: Option) -> Originator { } pub fn set_default_originator(value: String) -> Result<(), SetOriginatorError> { + if HeaderValue::from_str(&value).is_err() { + return Err(SetOriginatorError::InvalidHeaderValue); + } let originator = get_originator_value(Some(value)); - ORIGINATOR - .set(originator) - .map_err(|_| SetOriginatorError::AlreadyInitialized) + let Ok(mut guard) = ORIGINATOR.write() else { + return Err(SetOriginatorError::AlreadyInitialized); + }; + if guard.is_some() { + return Err(SetOriginatorError::AlreadyInitialized); + } + *guard = Some(originator); + Ok(()) } -pub fn originator() -> &'static Originator { - ORIGINATOR.get_or_init(|| get_originator_value(None)) +pub fn originator() -> Originator { + if let Ok(guard) = ORIGINATOR.read() + && let Some(originator) = guard.as_ref() + { + return originator.clone(); + } + + if std::env::var(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR).is_ok() { + let originator = get_originator_value(None); + if let Ok(mut guard) = ORIGINATOR.write() { + match guard.as_ref() { + Some(originator) => return originator.clone(), + None => *guard = Some(originator.clone()), + } + } + return originator; + } + + get_originator_value(None) } pub fn get_codex_user_agent() -> String { let build_version = env!("CARGO_PKG_VERSION"); let os_info = os_info::get(); + let originator = originator(); let prefix = format!( "{}/{build_version} ({} {}; {}) {}", - originator().value.as_str(), + originator.value.as_str(), os_info.os_type(), os_info.version(), os_info.architecture().unwrap_or("unknown"), @@ -123,7 +149,7 @@ fn sanitize_user_agent(candidate: String, fallback: &str) -> String { tracing::warn!( "Falling back to default Codex originator because base user agent string is invalid" ); - originator().value.clone() + originator().value } } @@ -137,7 +163,7 @@ pub fn build_reqwest_client() -> reqwest::Client { use reqwest::header::HeaderMap; let mut headers = HeaderMap::new(); - headers.insert("originator", originator().header_value.clone()); + headers.insert("originator", originator().header_value); let ua = get_codex_user_agent(); let mut builder = reqwest::Client::builder() @@ -163,7 +189,7 @@ mod tests { #[test] fn test_get_codex_user_agent() { let user_agent = get_codex_user_agent(); - let originator = originator().value.as_str(); + let originator = originator().value; let prefix = format!("{originator}/"); assert!(user_agent.starts_with(&prefix)); } diff --git a/codex-rs/core/src/otel_init.rs b/codex-rs/core/src/otel_init.rs index 9a05a29b9..9177409b7 100644 --- a/codex-rs/core/src/otel_init.rs +++ b/codex-rs/core/src/otel_init.rs @@ -15,6 +15,7 @@ use std::error::Error; pub fn build_provider( config: &Config, service_version: &str, + service_name_override: Option<&str>, default_analytics_enabled: bool, ) -> Result, Box> { let to_otel_exporter = |kind: &Kind| match kind { @@ -74,8 +75,11 @@ pub fn build_provider( OtelExporter::None }; + let originator = originator(); + let service_name = service_name_override.unwrap_or(originator.value.as_str()); + OtelProvider::from(&OtelSettings { - service_name: originator().value.to_owned(), + service_name: service_name.to_string(), service_version: service_version.to_string(), codex_home: config.codex_home.clone(), environment: config.otel.environment.to_string(), diff --git a/codex-rs/core/src/rollout/recorder.rs b/codex-rs/core/src/rollout/recorder.rs index d571ad191..80d95e625 100644 --- a/codex-rs/core/src/rollout/recorder.rs +++ b/codex-rs/core/src/rollout/recorder.rs @@ -143,7 +143,7 @@ impl RolloutRecorder { id: session_id, timestamp, cwd: config.cwd.clone(), - originator: originator().value.clone(), + originator: originator().value, cli_version: env!("CARGO_PKG_VERSION").to_string(), instructions, source, diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index da3389c46..7b80f64c5 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -223,7 +223,8 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option) -> any std::process::exit(1); } - let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), false); + let otel = + codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None, false); #[allow(clippy::print_stderr)] let otel = match otel { diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index cf9c30c8c..0f38c5ee7 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -300,7 +300,8 @@ pub async fn run_main( ensure_oss_provider_ready(provider_id, &config).await?; } - let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), true); + let otel = + codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None, true); #[allow(clippy::print_stderr)] let otel = match otel { diff --git a/codex-rs/tui2/src/lib.rs b/codex-rs/tui2/src/lib.rs index f09fffe3f..e111af5b1 100644 --- a/codex-rs/tui2/src/lib.rs +++ b/codex-rs/tui2/src/lib.rs @@ -315,7 +315,8 @@ pub async fn run_main( ensure_oss_provider_ready(provider_id, &config).await?; } - let otel = codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), true); + let otel = + codex_core::otel_init::build_provider(&config, env!("CARGO_PKG_VERSION"), None, true); #[allow(clippy::print_stderr)] let otel = match otel {