diff --git a/codex-rs/analytics/src/analytics_client.rs b/codex-rs/analytics/src/analytics_client.rs index f75b455197..99c40f28ed 100644 --- a/codex-rs/analytics/src/analytics_client.rs +++ b/codex-rs/analytics/src/analytics_client.rs @@ -51,15 +51,21 @@ pub struct CodexTurnEvent { } #[derive(Clone)] -pub struct CodexThreadInitializedEvent { +pub struct CodexThreadInitializedInput { pub thread_id: String, pub model: String, + pub product_client_id: String, + pub created_at: u64, + pub thread_context: CodexThreadContext, +} + +#[derive(Clone)] +pub struct CodexThreadContext { pub ephemeral: bool, pub session_source: SessionSource, pub initialization_mode: InitializationMode, pub subagent_source: Option, pub parent_thread_id: Option, - pub created_at: u64, } #[derive(Clone, Copy, Debug, Serialize)] @@ -91,24 +97,6 @@ pub struct CodexTurnSteerEvent { pub created_at: u64, } -#[derive(Clone)] -pub struct CodexThreadInitializedInput { - pub thread_id: String, - pub model: String, - pub product_client_id: String, - pub created_at: u64, - pub thread_context: CodexThreadContext, -} - -#[derive(Clone)] -pub struct CodexThreadContext { - pub ephemeral: bool, - pub session_source: SessionSource, - pub initialization_mode: InitializationMode, - pub subagent_source: Option, - pub parent_thread_id: Option, -} - #[derive(Clone, Copy, Debug, Serialize)] #[serde(rename_all = "snake_case")] pub enum InitializationMode { @@ -151,7 +139,7 @@ pub struct AppInvocation { } pub enum AnalyticsInput { - ThreadInitialized(CodexThreadInitializedInput), + CodexThreadInitialized(CodexThreadInitializedInput), TurnEvent(TurnEventInput), TurnSteer(TurnSteerInput), SkillInvoked(SkillInvokedInput), @@ -210,7 +198,7 @@ pub struct AnalyticsReducer { } struct ThreadState { - _initialized_event: CodexThreadInitializedEvent, + _initialized_input: CodexThreadInitializedInput, } #[derive(Clone)] @@ -308,7 +296,7 @@ impl AnalyticsEventsClient { } pub fn track_thread_initialized(&self, input: CodexThreadInitializedInput) { - self.record(AnalyticsInput::ThreadInitialized(input)); + self.record(AnalyticsInput::CodexThreadInitialized(input)); } pub fn track_app_mentioned(&self, tracking: TrackEventsContext, mentions: Vec) { @@ -409,7 +397,7 @@ struct TrackEventsRequest { #[serde(untagged)] enum TrackEventRequest { SkillInvocation(SkillInvocationEventRequest), - ThreadInitialized(CodexThreadInitializedEventRequest), + CodexThreadInitialized(CodexThreadInitializedEvent), AppMentioned(CodexAppMentionedEventRequest), AppUsed(CodexAppUsedEventRequest), TurnEvent(CodexTurnEventRequest), @@ -453,7 +441,7 @@ struct CodexThreadInitializedEventParams { } #[derive(Serialize)] -struct CodexThreadInitializedEventRequest { +struct CodexThreadInitializedEvent { event_type: &'static str, event_params: CodexThreadInitializedEventParams, } @@ -560,7 +548,7 @@ struct CodexPluginUsedEventRequest { impl AnalyticsReducer { async fn ingest(&mut self, input: AnalyticsInput, out: &mut Vec) { match input { - AnalyticsInput::ThreadInitialized(input) => { + AnalyticsInput::CodexThreadInitialized(input) => { self.ingest_thread_initialized(input, out); } AnalyticsInput::TurnEvent(input) => { @@ -592,25 +580,14 @@ impl AnalyticsReducer { input: CodexThreadInitializedInput, out: &mut Vec, ) { - let product_client_id = input.product_client_id.clone(); - let event = CodexThreadInitializedEvent { - thread_id: input.thread_id, - model: input.model, - ephemeral: input.thread_context.ephemeral, - session_source: input.thread_context.session_source, - initialization_mode: input.thread_context.initialization_mode, - subagent_source: input.thread_context.subagent_source, - parent_thread_id: input.thread_context.parent_thread_id, - created_at: input.created_at, - }; self.threads.insert( - event.thread_id.clone(), + input.thread_id.clone(), ThreadState { - _initialized_event: event.clone(), + _initialized_input: input.clone(), }, ); - out.push(TrackEventRequest::ThreadInitialized( - codex_thread_initialized_event_request(product_client_id, event), + out.push(TrackEventRequest::CodexThreadInitialized( + codex_thread_initialized_event_request(input), )); } @@ -620,7 +597,7 @@ impl AnalyticsReducer { turn_event, } = input; out.push(TrackEventRequest::TurnEvent(CodexTurnEventRequest { - event_type: "codex_turn_steer_event", + event_type: "codex_turn_event", event_params: codex_turn_event_params(&tracking, turn_event), })); } @@ -631,7 +608,7 @@ impl AnalyticsReducer { turn_steer, } = input; out.push(TrackEventRequest::TurnSteer(CodexTurnSteerEventRequest { - event_type: "codex_turn_event", + event_type: "codex_turn_steer_event", event_params: codex_turn_steer_event_params(&tracking, turn_steer), })); } @@ -823,33 +800,32 @@ fn personality_mode(personality: Option) -> Option { Some(personality) => Some(personality.to_string()), } } + fn codex_thread_initialized_event_request( - product_client_id: String, - thread_event: CodexThreadInitializedEvent, -) -> CodexThreadInitializedEventRequest { - CodexThreadInitializedEventRequest { + input: CodexThreadInitializedInput, +) -> CodexThreadInitializedEvent { + CodexThreadInitializedEvent { event_type: "codex_thread_initialized", - event_params: codex_thread_initialized_event_params_with_product_client_id( - product_client_id, - thread_event, - ), + event_params: codex_thread_initialized_event_params(input), } } -fn codex_thread_initialized_event_params_with_product_client_id( - product_client_id: String, - thread_event: CodexThreadInitializedEvent, +fn codex_thread_initialized_event_params( + input: CodexThreadInitializedInput, ) -> CodexThreadInitializedEventParams { CodexThreadInitializedEventParams { - thread_id: thread_event.thread_id, - product_client_id, - model: thread_event.model, - ephemeral: thread_event.ephemeral, - session_source: session_source_name(&thread_event.session_source), - initialization_mode: thread_event.initialization_mode, - subagent_source: thread_event.subagent_source.map(subagent_source_name), - parent_thread_id: thread_event.parent_thread_id, - created_at: thread_event.created_at, + thread_id: input.thread_id, + product_client_id: input.product_client_id, + model: input.model, + ephemeral: input.thread_context.ephemeral, + session_source: session_source_name(&input.thread_context.session_source), + initialization_mode: input.thread_context.initialization_mode, + subagent_source: input + .thread_context + .subagent_source + .map(subagent_source_name), + parent_thread_id: input.thread_context.parent_thread_id, + created_at: input.created_at, } } diff --git a/codex-rs/analytics/src/analytics_client_tests.rs b/codex-rs/analytics/src/analytics_client_tests.rs index 111f34ee2e..154a5ff7be 100644 --- a/codex-rs/analytics/src/analytics_client_tests.rs +++ b/codex-rs/analytics/src/analytics_client_tests.rs @@ -4,7 +4,8 @@ use super::CodexAppMentionedEventRequest; use super::CodexAppUsedEventRequest; use super::CodexPluginEventRequest; use super::CodexPluginUsedEventRequest; -use super::CodexThreadInitializedEvent; +use super::CodexThreadContext; +use super::CodexThreadInitializedInput; use super::CodexTurnEvent; use super::CodexTurnEventRequest; use super::CodexTurnSteerEvent; @@ -306,17 +307,19 @@ fn turn_steer_event_serializes_expected_shape() { #[test] fn thread_initialized_event_serializes_expected_shape() { - let event = TrackEventRequest::ThreadInitialized(codex_thread_initialized_event_request( - originator().value, - CodexThreadInitializedEvent { + let event = TrackEventRequest::CodexThreadInitialized(codex_thread_initialized_event_request( + CodexThreadInitializedInput { thread_id: "thread-0".to_string(), model: "gpt-5".to_string(), - ephemeral: true, - session_source: SessionSource::Exec, - initialization_mode: InitializationMode::New, - subagent_source: None, - parent_thread_id: None, + product_client_id: originator().value, created_at: 1_716_000_000, + thread_context: CodexThreadContext { + ephemeral: true, + session_source: SessionSource::Exec, + initialization_mode: InitializationMode::New, + subagent_source: None, + parent_thread_id: None, + }, }, )); @@ -343,17 +346,19 @@ fn thread_initialized_event_serializes_expected_shape() { #[test] fn thread_initialized_event_serializes_subagent_source() { - let event = TrackEventRequest::ThreadInitialized(codex_thread_initialized_event_request( - originator().value, - CodexThreadInitializedEvent { + let event = TrackEventRequest::CodexThreadInitialized(codex_thread_initialized_event_request( + CodexThreadInitializedInput { thread_id: "thread-1".to_string(), model: "gpt-5".to_string(), - ephemeral: false, - session_source: SessionSource::SubAgent(SubAgentSource::Review), - initialization_mode: InitializationMode::New, - subagent_source: Some(SubAgentSource::Review), - parent_thread_id: None, + product_client_id: originator().value, created_at: 1, + thread_context: CodexThreadContext { + ephemeral: false, + session_source: SessionSource::SubAgent(SubAgentSource::Review), + initialization_mode: InitializationMode::New, + subagent_source: Some(SubAgentSource::Review), + parent_thread_id: None, + }, }, )); @@ -365,17 +370,19 @@ fn thread_initialized_event_serializes_subagent_source() { #[test] fn thread_initialized_event_omits_non_user_non_subagent_session_source() { - let event = TrackEventRequest::ThreadInitialized(codex_thread_initialized_event_request( - originator().value, - CodexThreadInitializedEvent { + let event = TrackEventRequest::CodexThreadInitialized(codex_thread_initialized_event_request( + CodexThreadInitializedInput { thread_id: "thread-2".to_string(), model: "gpt-5".to_string(), - ephemeral: false, - session_source: SessionSource::Mcp, - initialization_mode: InitializationMode::New, - subagent_source: None, - parent_thread_id: None, + product_client_id: originator().value, created_at: 1, + thread_context: CodexThreadContext { + ephemeral: false, + session_source: SessionSource::Mcp, + initialization_mode: InitializationMode::New, + subagent_source: None, + parent_thread_id: None, + }, }, )); diff --git a/codex-rs/analytics/src/lib.rs b/codex-rs/analytics/src/lib.rs index 0e2b23d795..caface527c 100644 --- a/codex-rs/analytics/src/lib.rs +++ b/codex-rs/analytics/src/lib.rs @@ -7,7 +7,6 @@ pub use analytics_client::AppInvocation; pub use analytics_client::AppMentionedInput; pub use analytics_client::AppUsedInput; pub use analytics_client::CodexThreadContext; -pub use analytics_client::CodexThreadInitializedEvent; pub use analytics_client::CodexThreadInitializedInput; pub use analytics_client::CodexTurnEvent; pub use analytics_client::CodexTurnSteerEvent; diff --git a/codex-rs/core/tests/suite/thread_metadata.rs b/codex-rs/core/tests/suite/analytics.rs similarity index 100% rename from codex-rs/core/tests/suite/thread_metadata.rs rename to codex-rs/core/tests/suite/analytics.rs diff --git a/codex-rs/core/tests/suite/mod.rs b/codex-rs/core/tests/suite/mod.rs index 4011e692a8..ff2da246fc 100644 --- a/codex-rs/core/tests/suite/mod.rs +++ b/codex-rs/core/tests/suite/mod.rs @@ -58,6 +58,7 @@ pub static CODEX_ALIASES_TEMP_DIR: TestCodexAliasesGuard = unsafe { mod abort_tasks; mod agent_jobs; mod agent_websocket; +mod analytics; mod apply_patch_cli; #[cfg(not(target_os = "windows"))] mod approvals; @@ -127,7 +128,6 @@ mod stream_error_allows_next_turn; mod stream_no_completed; mod subagent_notifications; mod text_encoding_fix; -mod thread_metadata; mod tool_harness; mod tool_parallelism; mod tool_suggest;