diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index d982fd533..6edd11c69 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -540,7 +540,6 @@ struct ListenerTaskContext { outgoing: Arc, pending_thread_unloads: Arc>>, analytics_events_client: AnalyticsEventsClient, - general_analytics_enabled: bool, thread_watch_manager: ThreadWatchManager, fallback_model_provider: String, codex_home: PathBuf, @@ -727,14 +726,12 @@ impl CodexMessageProcessor { error: &JSONRPCErrorError, error_type: Option, ) { - if self.config.features.enabled(Feature::GeneralAnalytics) { - self.analytics_events_client.track_error_response( - request_id.connection_id.0, - request_id.request_id.clone(), - error.clone(), - error_type, - ); - } + self.analytics_events_client.track_error_response( + request_id.connection_id.0, + request_id.request_id.clone(), + error.clone(), + error_type, + ); } async fn load_thread( @@ -2410,7 +2407,6 @@ impl CodexMessageProcessor { outgoing: Arc::clone(&self.outgoing), pending_thread_unloads: Arc::clone(&self.pending_thread_unloads), analytics_events_client: self.analytics_events_client.clone(), - general_analytics_enabled: self.config.features.enabled(Feature::GeneralAnalytics), thread_watch_manager: self.thread_watch_manager.clone(), fallback_model_provider: self.config.model_provider_id.clone(), codex_home: self.config.codex_home.to_path_buf(), @@ -2749,17 +2745,15 @@ impl CodexMessageProcessor { permission_profile, reasoning_effort: config_snapshot.reasoning_effort, }; - if listener_task_context.general_analytics_enabled { - listener_task_context - .analytics_events_client - .track_response( - request_id.connection_id.0, - ClientResponse::ThreadStart { - request_id: request_id.request_id.clone(), - response: response.clone(), - }, - ); - } + listener_task_context + .analytics_events_client + .track_response( + request_id.connection_id.0, + ClientResponse::ThreadStart { + request_id: request_id.request_id.clone(), + response: response.clone(), + }, + ); listener_task_context .outgoing @@ -4565,15 +4559,13 @@ impl CodexMessageProcessor { permission_profile, reasoning_effort: session_configured.reasoning_effort, }; - if self.config.features.enabled(Feature::GeneralAnalytics) { - self.analytics_events_client.track_response( - request_id.connection_id.0, - ClientResponse::ThreadResume { - request_id: request_id.request_id.clone(), - response: response.clone(), - }, - ); - } + self.analytics_events_client.track_response( + request_id.connection_id.0, + ClientResponse::ThreadResume { + request_id: request_id.request_id.clone(), + response: response.clone(), + }, + ); let connection_id = request_id.connection_id; let token_usage_thread = include_turns.then(|| response.thread.clone()); @@ -5296,15 +5288,13 @@ impl CodexMessageProcessor { permission_profile, reasoning_effort: session_configured.reasoning_effort, }; - if self.config.features.enabled(Feature::GeneralAnalytics) { - self.analytics_events_client.track_response( - request_id.connection_id.0, - ClientResponse::ThreadFork { - request_id: request_id.request_id.clone(), - response: response.clone(), - }, - ); - } + self.analytics_events_client.track_response( + request_id.connection_id.0, + ClientResponse::ThreadFork { + request_id: request_id.request_id.clone(), + response: response.clone(), + }, + ); let connection_id = request_id.connection_id; let token_usage_thread = include_turns.then(|| response.thread.clone()); @@ -7038,15 +7028,13 @@ impl CodexMessageProcessor { }; let response = TurnStartResponse { turn }; - if self.config.features.enabled(Feature::GeneralAnalytics) { - self.analytics_events_client.track_response( - request_id.connection_id.0, - ClientResponse::TurnStart { - request_id: request_id.request_id.clone(), - response: response.clone(), - }, - ); - } + self.analytics_events_client.track_response( + request_id.connection_id.0, + ClientResponse::TurnStart { + request_id: request_id.request_id.clone(), + response: response.clone(), + }, + ); self.outgoing.send_response(request_id, response).await; } Err(err) => { @@ -7172,15 +7160,13 @@ impl CodexMessageProcessor { { Ok(turn_id) => { let response = TurnSteerResponse { turn_id }; - if self.config.features.enabled(Feature::GeneralAnalytics) { - self.analytics_events_client.track_response( - request_id.connection_id.0, - ClientResponse::TurnSteer { - request_id: request_id.request_id.clone(), - response: response.clone(), - }, - ); - } + self.analytics_events_client.track_response( + request_id.connection_id.0, + ClientResponse::TurnSteer { + request_id: request_id.request_id.clone(), + response: response.clone(), + }, + ); self.outgoing.send_response(request_id, response).await; } Err(err) => { @@ -7813,7 +7799,6 @@ impl CodexMessageProcessor { outgoing: Arc::clone(&self.outgoing), pending_thread_unloads: Arc::clone(&self.pending_thread_unloads), analytics_events_client: self.analytics_events_client.clone(), - general_analytics_enabled: self.config.features.enabled(Feature::GeneralAnalytics), thread_watch_manager: self.thread_watch_manager.clone(), fallback_model_provider: self.config.model_provider_id.clone(), codex_home: self.config.codex_home.to_path_buf(), @@ -7931,7 +7916,6 @@ impl CodexMessageProcessor { outgoing: Arc::clone(&self.outgoing), pending_thread_unloads: Arc::clone(&self.pending_thread_unloads), analytics_events_client: self.analytics_events_client.clone(), - general_analytics_enabled: self.config.features.enabled(Feature::GeneralAnalytics), thread_watch_manager: self.thread_watch_manager.clone(), fallback_model_provider: self.config.model_provider_id.clone(), codex_home: self.config.codex_home.to_path_buf(), @@ -7980,7 +7964,6 @@ impl CodexMessageProcessor { thread_state_manager, pending_thread_unloads, analytics_events_client: _, - general_analytics_enabled: _, thread_watch_manager, fallback_model_provider, codex_home, @@ -8056,9 +8039,7 @@ impl CodexMessageProcessor { conversation_id, conversation.clone(), thread_manager.clone(), - listener_task_context - .general_analytics_enabled - .then(|| listener_task_context.analytics_events_client.clone()), + Some(listener_task_context.analytics_events_client.clone()), thread_outgoing, thread_state.clone(), thread_watch_manager.clone(), diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 6cdb93936..071f69eb6 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -653,14 +653,12 @@ impl MessageProcessor { } } } - if self.config.features.enabled(Feature::GeneralAnalytics) { - self.analytics_events_client.track_initialize( - connection_id.0, - analytics_initialize_params, - originator, - self.rpc_transport, - ); - } + self.analytics_events_client.track_initialize( + connection_id.0, + analytics_initialize_params, + originator, + self.rpc_transport, + ); set_default_client_residency_requirement(self.config.enforce_residency.value()); if let Ok(mut suffix) = USER_AGENT_SUFFIX.lock() { *suffix = Some(user_agent_suffix); @@ -716,9 +714,8 @@ impl MessageProcessor { return Err(invalid_request(experimental_required_message(reason))); } let connection_id = connection_request_id.connection_id; - if self.config.features.enabled(Feature::GeneralAnalytics) - && let ClientRequest::TurnStart { request_id, .. } - | ClientRequest::TurnSteer { request_id, .. } = &codex_request + if let ClientRequest::TurnStart { request_id, .. } + | ClientRequest::TurnSteer { request_id, .. } = &codex_request { self.analytics_events_client.track_request( connection_id.0, diff --git a/codex-rs/app-server/tests/suite/v2/analytics.rs b/codex-rs/app-server/tests/suite/v2/analytics.rs index a3ecdbc1f..862721a15 100644 --- a/codex-rs/app-server/tests/suite/v2/analytics.rs +++ b/codex-rs/app-server/tests/suite/v2/analytics.rs @@ -79,24 +79,6 @@ async fn app_server_default_analytics_enabled_with_flag() -> Result<()> { Ok(()) } -pub(crate) async fn enable_analytics_capture(server: &MockServer, codex_home: &Path) -> Result<()> { - let config_path = codex_home.join("config.toml"); - let config_toml = std::fs::read_to_string(&config_path)?; - if !config_toml.contains("[features]") { - std::fs::write( - &config_path, - format!("{config_toml}\n[features]\ngeneral_analytics = true\n"), - )?; - } else if !config_toml.contains("general_analytics") { - std::fs::write( - &config_path, - config_toml.replace("[features]\n", "[features]\ngeneral_analytics = true\n"), - )?; - } - - mount_analytics_capture(server, codex_home).await -} - pub(crate) async fn mount_analytics_capture(server: &MockServer, codex_home: &Path) -> Result<()> { Mock::given(method("POST")) .and(path("/codex/analytics-events/events")) diff --git a/codex-rs/app-server/tests/suite/v2/thread_fork.rs b/codex-rs/app-server/tests/suite/v2/thread_fork.rs index 6c43ebd62..fd773f2e3 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_fork.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_fork.rs @@ -42,7 +42,7 @@ use wiremock::matchers::method; use wiremock::matchers::path; use super::analytics::assert_basic_thread_initialized_event; -use super::analytics::enable_analytics_capture; +use super::analytics::mount_analytics_capture; use super::analytics::thread_initialized_event; use super::analytics::wait_for_analytics_payload; @@ -385,13 +385,8 @@ async fn thread_fork_tracks_thread_initialized_analytics() -> Result<()> { let server = create_mock_responses_server_repeating_assistant("Done").await; let codex_home = TempDir::new()?; - create_config_toml_with_chatgpt_base_url( - codex_home.path(), - &server.uri(), - &server.uri(), - /*general_analytics_enabled*/ true, - )?; - enable_analytics_capture(&server, codex_home.path()).await?; + create_config_toml_with_chatgpt_base_url(codex_home.path(), &server.uri(), &server.uri())?; + mount_analytics_capture(&server, codex_home.path()).await?; let conversation_id = create_fake_rollout( codex_home.path(), @@ -496,7 +491,6 @@ async fn thread_fork_surfaces_cloud_requirements_load_errors() -> Result<()> { codex_home.path(), &model_server.uri(), &chatgpt_base_url, - /*general_analytics_enabled*/ false, )?; write_chatgpt_auth( codex_home.path(), @@ -793,13 +787,7 @@ fn create_config_toml_with_chatgpt_base_url( codex_home: &Path, server_uri: &str, chatgpt_base_url: &str, - general_analytics_enabled: bool, ) -> std::io::Result<()> { - let general_analytics_toml = if general_analytics_enabled { - "\ngeneral_analytics = true".to_string() - } else { - "\ngeneral_analytics = false".to_string() - }; let config_toml = codex_home.join("config.toml"); std::fs::write( config_toml, @@ -812,9 +800,6 @@ chatgpt_base_url = "{chatgpt_base_url}" model_provider = "mock_provider" -[features] -{general_analytics_toml} - [model_providers.mock_provider] name = "Mock provider for test" base_url = "{server_uri}/v1" diff --git a/codex-rs/app-server/tests/suite/v2/thread_resume.rs b/codex-rs/app-server/tests/suite/v2/thread_resume.rs index 5044fcd11..d9f5f039d 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_resume.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_resume.rs @@ -84,7 +84,7 @@ use wiremock::matchers::method; use wiremock::matchers::path; use super::analytics::assert_basic_thread_initialized_event; -use super::analytics::enable_analytics_capture; +use super::analytics::mount_analytics_capture; use super::analytics::thread_initialized_event; use super::analytics::wait_for_analytics_payload; @@ -185,10 +185,7 @@ async fn thread_goal_get_rejects_unmaterialized_thread() -> Result<()> { let config = std::fs::read_to_string(&config_path)?; std::fs::write( &config_path, - config.replace( - "general_analytics = true\n", - "general_analytics = true\ngoals = true\n", - ), + config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; @@ -238,13 +235,8 @@ async fn thread_resume_tracks_thread_initialized_analytics() -> Result<()> { let server = create_mock_responses_server_repeating_assistant("Done").await; let codex_home = TempDir::new()?; - create_config_toml_with_chatgpt_base_url( - codex_home.path(), - &server.uri(), - &server.uri(), - /*general_analytics_enabled*/ true, - )?; - enable_analytics_capture(&server, codex_home.path()).await?; + create_config_toml_with_chatgpt_base_url(codex_home.path(), &server.uri(), &server.uri())?; + mount_analytics_capture(&server, codex_home.path()).await?; let conversation_id = create_fake_rollout_with_text_elements( codex_home.path(), @@ -400,10 +392,7 @@ async fn thread_resume_emits_active_goal_update_before_continuation() -> Result< let config = std::fs::read_to_string(&config_path)?; std::fs::write( &config_path, - config.replace( - "general_analytics = true\n", - "general_analytics = true\ngoals = true\n", - ), + config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; @@ -507,10 +496,7 @@ async fn thread_goal_set_preserves_budget_limited_same_objective() -> Result<()> let config = std::fs::read_to_string(&config_path)?; std::fs::write( &config_path, - config.replace( - "general_analytics = true\n", - "general_analytics = true\ngoals = true\n", - ), + config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; @@ -608,10 +594,7 @@ async fn thread_goal_clear_deletes_goal_and_notifies() -> Result<()> { let config = std::fs::read_to_string(&config_path)?; std::fs::write( &config_path, - config.replace( - "general_analytics = true\n", - "general_analytics = true\ngoals = true\n", - ), + config.replace("personality = true\n", "personality = true\ngoals = true\n"), )?; let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; @@ -2414,7 +2397,6 @@ async fn thread_resume_surfaces_cloud_requirements_load_errors() -> Result<()> { codex_home.path(), &model_server.uri(), &chatgpt_base_url, - /*general_analytics_enabled*/ false, )?; write_chatgpt_auth( codex_home.path(), @@ -2859,7 +2841,6 @@ model_provider = "mock_provider" [features] personality = true -general_analytics = true [model_providers.mock_provider] name = "Mock provider for test" @@ -2890,7 +2871,6 @@ model_provider = "mock_provider" [features] personality = true -general_analytics = true [model_providers.mock_provider] name = "Mock provider for test" @@ -2907,13 +2887,7 @@ fn create_config_toml_with_chatgpt_base_url( codex_home: &std::path::Path, server_uri: &str, chatgpt_base_url: &str, - general_analytics_enabled: bool, ) -> std::io::Result<()> { - let general_analytics_toml = if general_analytics_enabled { - "\ngeneral_analytics = true".to_string() - } else { - "\ngeneral_analytics = false".to_string() - }; let config_toml = codex_home.join("config.toml"); std::fs::write( config_toml, @@ -2928,7 +2902,6 @@ model_provider = "mock_provider" [features] personality = true -{general_analytics_toml} [model_providers.mock_provider] name = "Mock provider for test" diff --git a/codex-rs/app-server/tests/suite/v2/thread_start.rs b/codex-rs/app-server/tests/suite/v2/thread_start.rs index f521d5509..d8a50b88a 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_start.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_start.rs @@ -34,7 +34,6 @@ use serde_json::Value; use serde_json::json; use std::path::Path; use std::path::PathBuf; -use std::time::Duration; use tempfile::TempDir; use tokio::time::timeout; use wiremock::Mock; @@ -265,12 +264,7 @@ async fn thread_start_tracks_thread_initialized_analytics() -> Result<()> { let server = create_mock_responses_server_repeating_assistant("Done").await; let codex_home = TempDir::new()?; - create_config_toml_with_chatgpt_base_url( - codex_home.path(), - &server.uri(), - &server.uri(), - /*general_analytics_enabled*/ true, - )?; + create_config_toml_with_chatgpt_base_url(codex_home.path(), &server.uri(), &server.uri())?; mount_analytics_capture(&server, codex_home.path()).await?; let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; @@ -293,54 +287,6 @@ async fn thread_start_tracks_thread_initialized_analytics() -> Result<()> { Ok(()) } -#[tokio::test] -async fn thread_start_does_not_track_thread_initialized_analytics_without_feature() -> Result<()> { - let server = create_mock_responses_server_repeating_assistant("Done").await; - - let codex_home = TempDir::new()?; - create_config_toml_with_chatgpt_base_url( - codex_home.path(), - &server.uri(), - &server.uri(), - /*general_analytics_enabled*/ false, - )?; - mount_analytics_capture(&server, codex_home.path()).await?; - - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; - timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; - - let req_id = mcp - .send_thread_start_request(ThreadStartParams::default()) - .await?; - let resp: JSONRPCResponse = timeout( - DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(req_id)), - ) - .await??; - let _ = to_response::(resp)?; - - assert_no_thread_initialized_analytics(&server, Duration::from_millis(250)).await?; - Ok(()) -} - -async fn assert_no_thread_initialized_analytics( - server: &MockServer, - wait_duration: Duration, -) -> Result<()> { - tokio::time::sleep(wait_duration).await; - let requests = server.received_requests().await.unwrap_or_default(); - for request in requests.iter().filter(|request| { - request.method == "POST" && request.url.path() == "/codex/analytics-events/events" - }) { - let payload: Value = serde_json::from_slice(&request.body)?; - assert!( - thread_initialized_event(&payload).is_err(), - "thread analytics should be gated off when general_analytics is disabled; payload={payload}" - ); - } - Ok(()) -} - #[tokio::test] async fn thread_start_respects_project_config_from_cwd() -> Result<()> { let server = create_mock_responses_server_repeating_assistant("Done").await; @@ -643,7 +589,6 @@ async fn thread_start_surfaces_cloud_requirements_load_errors() -> Result<()> { codex_home.path(), &model_server.uri(), &chatgpt_base_url, - /*general_analytics_enabled*/ false, )?; write_chatgpt_auth( codex_home.path(), @@ -966,13 +911,7 @@ fn create_config_toml_with_chatgpt_base_url( codex_home: &Path, server_uri: &str, chatgpt_base_url: &str, - general_analytics_enabled: bool, ) -> std::io::Result<()> { - let general_analytics_toml = if general_analytics_enabled { - "\ngeneral_analytics = true".to_string() - } else { - "\ngeneral_analytics = false".to_string() - }; let config_toml = codex_home.join("config.toml"); std::fs::write( config_toml, @@ -985,9 +924,6 @@ chatgpt_base_url = "{chatgpt_base_url}" model_provider = "mock_provider" -[features] -{general_analytics_toml} - [model_providers.mock_provider] name = "Mock provider for test" base_url = "{server_uri}/v1" 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 6d66edd3d..f81ec5f16 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -78,7 +78,6 @@ use std::path::Path; use tempfile::TempDir; use tokio::time::timeout; -use super::analytics::enable_analytics_capture; use super::analytics::mount_analytics_capture; use super::analytics::wait_for_analytics_event; @@ -463,7 +462,7 @@ async fn turn_start_tracks_turn_event_analytics() -> Result<()> { &server.uri(), &server.uri(), )?; - enable_analytics_capture(&server, codex_home.path()).await?; + mount_analytics_capture(&server, codex_home.path()).await?; let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; @@ -538,77 +537,6 @@ async fn turn_start_tracks_turn_event_analytics() -> Result<()> { Ok(()) } -#[tokio::test] -async fn turn_start_does_not_track_turn_event_analytics_without_feature() -> 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()?; - write_mock_responses_config_toml_with_chatgpt_base_url( - codex_home.path(), - &server.uri(), - &server.uri(), - )?; - let config_path = codex_home.path().join("config.toml"); - let config_toml = std::fs::read_to_string(&config_path)?; - std::fs::write( - &config_path, - format!("{config_toml}\n[features]\ngeneral_analytics = false\n"), - )?; - mount_analytics_capture(&server, codex_home.path()).await?; - - let mut mcp = McpProcess::new_without_managed_config(codex_home.path()).await?; - timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).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, - input: vec![V2UserInput::Text { - text: "hello".to_string(), - text_elements: Vec::new(), - }], - ..Default::default() - }) - .await?; - let turn_resp: JSONRPCResponse = timeout( - DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_response_message(RequestId::Integer(turn_req)), - ) - .await??; - let _ = to_response::(turn_resp)?; - - timeout( - DEFAULT_READ_TIMEOUT, - mcp.read_stream_until_notification_message("turn/completed"), - ) - .await??; - - let turn_event = wait_for_analytics_event( - &server, - std::time::Duration::from_millis(250), - "codex_turn_event", - ) - .await; - assert!( - turn_event.is_err(), - "turn analytics should be gated off when general_analytics is disabled" - ); - Ok(()) -} - #[tokio::test] async fn turn_start_accepts_text_at_limit_with_mention_item() -> Result<()> { let responses = vec![create_final_assistant_message_sse_response("Done")?]; diff --git a/codex-rs/app-server/tests/suite/v2/turn_steer.rs b/codex-rs/app-server/tests/suite/v2/turn_steer.rs index 16e28d6cc..a92b2db52 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_steer.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_steer.rs @@ -24,7 +24,7 @@ use codex_protocol::user_input::MAX_USER_INPUT_TEXT_CHARS; use tempfile::TempDir; use tokio::time::timeout; -use super::analytics::enable_analytics_capture; +use super::analytics::mount_analytics_capture; use super::analytics::wait_for_analytics_event; const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10); @@ -41,7 +41,7 @@ async fn turn_steer_requires_active_turn() -> Result<()> { &server.uri(), &server.uri(), )?; - enable_analytics_capture(&server, &codex_home).await?; + mount_analytics_capture(&server, &codex_home).await?; let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; @@ -125,7 +125,7 @@ async fn turn_steer_rejects_oversized_text_input() -> Result<()> { &server.uri(), &server.uri(), )?; - enable_analytics_capture(&server, &codex_home).await?; + mount_analytics_capture(&server, &codex_home).await?; let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; @@ -234,7 +234,7 @@ async fn turn_steer_returns_active_turn_id() -> Result<()> { &server.uri(), &server.uri(), )?; - enable_analytics_capture(&server, &codex_home).await?; + mount_analytics_capture(&server, &codex_home).await?; let mut mcp = McpProcess::new_without_managed_config(&codex_home).await?; timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??; diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 5727a4bdc..e4d156c54 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -421,9 +421,6 @@ "fast_mode": { "type": "boolean" }, - "general_analytics": { - "type": "boolean" - }, "goals": { "type": "boolean" }, @@ -2631,9 +2628,6 @@ "fast_mode": { "type": "boolean" }, - "general_analytics": { - "type": "boolean" - }, "goals": { "type": "boolean" }, diff --git a/codex-rs/core/src/agent/control.rs b/codex-rs/core/src/agent/control.rs index d4ec6858d..daf1acf8f 100644 --- a/codex-rs/core/src/agent/control.rs +++ b/codex-rs/core/src/agent/control.rs @@ -262,7 +262,6 @@ impl AgentControl { parent_thread_id, .. }, )) = notification_source.as_ref() - && new_thread.thread.enabled(Feature::GeneralAnalytics) { let client_metadata = match state.get_thread(*parent_thread_id).await { Ok(parent_thread) => { diff --git a/codex-rs/core/src/codex_delegate.rs b/codex-rs/core/src/codex_delegate.rs index 1fb2f42f2..f3df3cd4c 100644 --- a/codex-rs/core/src/codex_delegate.rs +++ b/codex-rs/core/src/codex_delegate.rs @@ -104,18 +104,16 @@ pub(crate) async fn run_codex_thread_interactive( })) .or_cancel(&cancel_token) .await??; - if parent_session.enabled(codex_features::Feature::GeneralAnalytics) { - let thread_config = codex.thread_config_snapshot().await; - let client_metadata = parent_session.app_server_client_metadata().await; - emit_subagent_session_started( - &parent_session.services.analytics_events_client, - client_metadata, - codex.session.conversation_id, - Some(parent_session.conversation_id), - thread_config, - subagent_source, - ); - } + let thread_config = codex.thread_config_snapshot().await; + let client_metadata = parent_session.app_server_client_metadata().await; + emit_subagent_session_started( + &parent_session.services.analytics_events_client, + client_metadata, + codex.session.conversation_id, + Some(parent_session.conversation_id), + thread_config, + subagent_source, + ); let codex = Arc::new(codex); // Use a child token so parent cancel cascades but we can scope it to this task diff --git a/codex-rs/core/src/compact.rs b/codex-rs/core/src/compact.rs index e9218ae7f..ed7a95b96 100644 --- a/codex-rs/core/src/compact.rs +++ b/codex-rs/core/src/compact.rs @@ -18,7 +18,6 @@ use codex_analytics::CompactionStatus; use codex_analytics::CompactionStrategy; use codex_analytics::CompactionTrigger; use codex_analytics::now_unix_seconds; -use codex_features::Feature; use codex_protocol::error::CodexErr; use codex_protocol::error::Result as CodexResult; use codex_protocol::items::ContextCompactionItem; @@ -295,7 +294,6 @@ async fn run_compact_task_inner_impl( } pub(crate) struct CompactionAnalyticsAttempt { - enabled: bool, thread_id: String, turn_id: String, trigger: CompactionTrigger, @@ -316,10 +314,8 @@ impl CompactionAnalyticsAttempt { implementation: CompactionImplementation, phase: CompactionPhase, ) -> Self { - let enabled = sess.enabled(Feature::GeneralAnalytics); let active_context_tokens_before = sess.get_total_token_usage().await; Self { - enabled, thread_id: sess.conversation_id.to_string(), turn_id: turn_context.sub_id.clone(), trigger, @@ -338,9 +334,6 @@ impl CompactionAnalyticsAttempt { status: CompactionStatus, error: Option, ) { - if !self.enabled { - return; - } let active_context_tokens_after = sess.get_total_token_usage().await; sess.services .analytics_events_client diff --git a/codex-rs/core/src/guardian/review.rs b/codex-rs/core/src/guardian/review.rs index 2635d641d..850d84dd2 100644 --- a/codex-rs/core/src/guardian/review.rs +++ b/codex-rs/core/src/guardian/review.rs @@ -6,7 +6,6 @@ use codex_analytics::GuardianReviewDecision; use codex_analytics::GuardianReviewFailureReason; use codex_analytics::GuardianReviewTerminalStatus; use codex_analytics::GuardianReviewTrackContext; -use codex_features::Feature; use codex_protocol::config_types::ApprovalsReviewer; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::EventMsg; @@ -161,13 +160,9 @@ pub(crate) fn is_guardian_reviewer_source( fn track_guardian_review( session: &Session, - turn: &TurnContext, tracking: &GuardianReviewTrackContext, result: GuardianReviewAnalyticsResult, ) { - if !turn.config.features.enabled(Feature::GeneralAnalytics) { - return; - } session .services .analytics_events_client @@ -279,7 +274,6 @@ async fn run_guardian_review( { track_guardian_review( session.as_ref(), - turn.as_ref(), &review_tracking, GuardianReviewAnalyticsResult { decision: GuardianReviewDecision::Aborted, @@ -325,7 +319,6 @@ async fn run_guardian_review( let approved = matches!(assessment.outcome, GuardianAssessmentOutcome::Allow); track_guardian_review( session.as_ref(), - turn.as_ref(), &review_tracking, GuardianReviewAnalyticsResult { decision: if approved { @@ -356,7 +349,6 @@ async fn run_guardian_review( .to_string(); track_guardian_review( session.as_ref(), - turn.as_ref(), &review_tracking, GuardianReviewAnalyticsResult { decision: GuardianReviewDecision::Denied, @@ -395,7 +387,6 @@ async fn run_guardian_review( GuardianReviewError::Cancelled => { track_guardian_review( session.as_ref(), - turn.as_ref(), &review_tracking, GuardianReviewAnalyticsResult { decision: GuardianReviewDecision::Aborted, @@ -437,7 +428,6 @@ async fn run_guardian_review( let rationale = format!("Automatic approval review failed: {message}"); track_guardian_review( session.as_ref(), - turn.as_ref(), &review_tracking, GuardianReviewAnalyticsResult { decision: GuardianReviewDecision::Denied, diff --git a/codex-rs/core/src/memories/phase2.rs b/codex-rs/core/src/memories/phase2.rs index d156d1dee..bcad73831 100644 --- a/codex-rs/core/src/memories/phase2.rs +++ b/codex-rs/core/src/memories/phase2.rs @@ -163,17 +163,15 @@ pub(super) async fn run(session: &Arc, config: Arc) { .get_agent_config_snapshot(thread_id) .await { - if session.enabled(Feature::GeneralAnalytics) { - let client_metadata = session.app_server_client_metadata().await; - emit_subagent_session_started( - &session.services.analytics_events_client, - client_metadata, - thread_id, - /*parent_thread_id*/ None, - thread_config, - SubAgentSource::MemoryConsolidation, - ); - } + let client_metadata = session.app_server_client_metadata().await; + emit_subagent_session_started( + &session.services.analytics_events_client, + client_metadata, + thread_id, + /*parent_thread_id*/ None, + thread_config, + SubAgentSource::MemoryConsolidation, + ); } else { warn!("failed to load memory consolidation thread config for analytics: {thread_id}"); } diff --git a/codex-rs/core/src/session/turn.rs b/codex-rs/core/src/session/turn.rs index ff3d37912..41195490b 100644 --- a/codex-rs/core/src/session/turn.rs +++ b/codex-rs/core/src/session/turn.rs @@ -662,10 +662,6 @@ async fn track_turn_resolved_config_analytics( turn_context: &TurnContext, input: &[UserInput], ) { - if !sess.enabled(Feature::GeneralAnalytics) { - return; - } - let thread_config = { let state = sess.state.lock().await; state.session_configuration.thread_config_snapshot() diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs index 6a2a2bc71..4a4b12be3 100644 --- a/codex-rs/features/src/lib.rs +++ b/codex-rs/features/src/lib.rs @@ -128,8 +128,6 @@ pub enum Feature { CodexGitCommit, /// Enable runtime metrics snapshots via a manual reader. RuntimeMetrics, - /// Enable thread lifecycle analytics emitted via the app-server analytics pipeline. - GeneralAnalytics, /// Persist rollout metadata to a local SQLite database. Sqlite, /// Enable startup memory extraction and file-backed memory consolidation. @@ -712,12 +710,6 @@ pub const FEATURES: &[FeatureSpec] = &[ stage: Stage::UnderDevelopment, default_enabled: false, }, - FeatureSpec { - id: Feature::GeneralAnalytics, - key: "general_analytics", - stage: Stage::Stable, - default_enabled: true, - }, FeatureSpec { id: Feature::Sqlite, key: "sqlite", diff --git a/codex-rs/features/src/tests.rs b/codex-rs/features/src/tests.rs index ca05d72d2..b8ddf5350 100644 --- a/codex-rs/features/src/tests.rs +++ b/codex-rs/features/src/tests.rs @@ -156,12 +156,6 @@ fn browser_controls_are_stable_and_enabled_by_default() { assert_eq!(feature_for_key("computer_use"), Some(Feature::ComputerUse)); } -#[test] -fn general_analytics_is_stable_and_enabled_by_default() { - assert_eq!(Feature::GeneralAnalytics.stage(), Stage::Stable); - assert_eq!(Feature::GeneralAnalytics.default_enabled(), true); -} - #[test] fn use_linux_sandbox_bwrap_is_a_removed_feature_key() { assert_eq!(