From f69f3a9928de996809208fd880561a5677b27140 Mon Sep 17 00:00:00 2001 From: James Chu Date: Mon, 27 Apr 2026 18:31:50 -0400 Subject: [PATCH] Use generic product analytics event tracking in TUI --- codex-rs/tui/src/app.rs | 7 +++-- codex-rs/tui/src/app/background_requests.rs | 27 ++++++++----------- codex-rs/tui/src/app/event_dispatch.rs | 7 ++--- codex-rs/tui/src/app_event.rs | 10 +++---- codex-rs/tui/src/chatwidget.rs | 22 +++++++++------ codex-rs/tui/src/chatwidget/tests.rs | 3 ++- .../src/chatwidget/tests/status_and_layout.rs | 27 ++++++++++--------- 7 files changed, 51 insertions(+), 52 deletions(-) diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index c43c86e215..cfe88740a8 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -101,6 +101,7 @@ use codex_app_server_protocol::PluginReadParams; use codex_app_server_protocol::PluginReadResponse; use codex_app_server_protocol::PluginUninstallParams; use codex_app_server_protocol::PluginUninstallResponse; +use codex_app_server_protocol::ProductAnalyticsEvent; use codex_app_server_protocol::RequestId; use codex_app_server_protocol::SendAddCreditsNudgeEmailParams; use codex_app_server_protocol::ServerNotification; @@ -112,13 +113,11 @@ use codex_app_server_protocol::ThreadLoadedListParams; use codex_app_server_protocol::ThreadMemoryMode; use codex_app_server_protocol::ThreadRollbackResponse; use codex_app_server_protocol::ThreadStartSource; -use codex_app_server_protocol::TrackUsageLimitBannerAction; -use codex_app_server_protocol::TrackUsageLimitBannerParams; -use codex_app_server_protocol::TrackUsageLimitBannerResponse; +use codex_app_server_protocol::TrackProductAnalyticsEventParams; +use codex_app_server_protocol::TrackProductAnalyticsEventResponse; use codex_app_server_protocol::Turn; use codex_app_server_protocol::TurnError as AppServerTurnError; use codex_app_server_protocol::TurnStatus; -use codex_app_server_protocol::UsageLimitBannerType; use codex_config::ConfigLayerStackOrdering; use codex_config::types::ApprovalsReviewer; use codex_config::types::ModelAvailabilityNuxConfig; diff --git a/codex-rs/tui/src/app/background_requests.rs b/codex-rs/tui/src/app/background_requests.rs index 6cb544ad27..577f897f00 100644 --- a/codex-rs/tui/src/app/background_requests.rs +++ b/codex-rs/tui/src/app/background_requests.rs @@ -59,16 +59,15 @@ impl App { }); } - pub(super) fn track_usage_limit_banner( + pub(super) fn track_product_analytics_event( &mut self, app_server: &AppServerSession, - action: TrackUsageLimitBannerAction, - banner_type: UsageLimitBannerType, + event: ProductAnalyticsEvent, ) { let request_handle = app_server.request_handle(); tokio::spawn(async move { - if let Err(err) = track_usage_limit_banner(request_handle, action, banner_type).await { - tracing::warn!("failed to track usage limit banner event: {err:#}"); + if let Err(err) = track_product_analytics_event(request_handle, event).await { + tracing::warn!("failed to track product analytics event: {err:#}"); } }); } @@ -465,22 +464,18 @@ pub(super) async fn send_add_credits_nudge_email( Ok(response.status) } -pub(super) async fn track_usage_limit_banner( +pub(super) async fn track_product_analytics_event( request_handle: AppServerRequestHandle, - action: TrackUsageLimitBannerAction, - banner_type: UsageLimitBannerType, + event: ProductAnalyticsEvent, ) -> Result<()> { - let request_id = RequestId::String(format!("usage-limit-banner-{}", Uuid::new_v4())); - let _: TrackUsageLimitBannerResponse = request_handle - .request_typed(ClientRequest::TrackUsageLimitBanner { + let request_id = RequestId::String(format!("product-analytics-event-{}", Uuid::new_v4())); + let _: TrackProductAnalyticsEventResponse = request_handle + .request_typed(ClientRequest::TrackProductAnalyticsEvent { request_id, - params: TrackUsageLimitBannerParams { - action, - banner_type, - }, + params: TrackProductAnalyticsEventParams { event }, }) .await - .wrap_err("account/usageLimitBanner/track failed in TUI")?; + .wrap_err("analytics/productEvent/track failed in TUI")?; Ok(()) } diff --git a/codex-rs/tui/src/app/event_dispatch.rs b/codex-rs/tui/src/app/event_dispatch.rs index 35eba7ad0a..d23226b970 100644 --- a/codex-rs/tui/src/app/event_dispatch.rs +++ b/codex-rs/tui/src/app/event_dispatch.rs @@ -564,11 +564,8 @@ impl App { self.send_add_credits_nudge_email(app_server, credit_type); } } - AppEvent::TrackUsageLimitBanner { - action, - banner_type, - } => { - self.track_usage_limit_banner(app_server, action, banner_type); + AppEvent::TrackProductAnalyticsEvent { event } => { + self.track_product_analytics_event(app_server, event); } AppEvent::AddCreditsNudgeEmailFinished { result } => { self.chat_widget diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index cd8a6fe2a8..c9db5da88b 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -20,10 +20,9 @@ use codex_app_server_protocol::PluginListResponse; use codex_app_server_protocol::PluginReadParams; use codex_app_server_protocol::PluginReadResponse; use codex_app_server_protocol::PluginUninstallResponse; +use codex_app_server_protocol::ProductAnalyticsEvent; use codex_app_server_protocol::SkillsListResponse; use codex_app_server_protocol::ThreadGoalStatus; -use codex_app_server_protocol::TrackUsageLimitBannerAction; -use codex_app_server_protocol::UsageLimitBannerType; use codex_file_search::FileMatch; use codex_protocol::ThreadId; use codex_protocol::openai_models::ModelPreset; @@ -236,10 +235,9 @@ pub(crate) enum AppEvent { credit_type: AddCreditsNudgeCreditType, }, - /// Track an analytics event for the workspace-owner nudge prompt. - TrackUsageLimitBanner { - action: TrackUsageLimitBannerAction, - banner_type: UsageLimitBannerType, + /// Track a client-side product analytics event. + TrackProductAnalyticsEvent { + event: ProductAnalyticsEvent, }, /// Result of notifying the workspace owner. diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index b487321f28..f330e4a36a 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -99,6 +99,7 @@ use codex_app_server_protocol::McpServerStartupState; use codex_app_server_protocol::McpServerStatusDetail; use codex_app_server_protocol::McpServerStatusUpdatedNotification; use codex_app_server_protocol::ModelVerification as AppServerModelVerification; +use codex_app_server_protocol::ProductAnalyticsEvent; use codex_app_server_protocol::ServerNotification; use codex_app_server_protocol::ServerRequest; use codex_app_server_protocol::ThreadGoal as AppThreadGoal; @@ -106,11 +107,11 @@ use codex_app_server_protocol::ThreadGoalStatus as AppThreadGoalStatus; use codex_app_server_protocol::ThreadItem; use codex_app_server_protocol::ThreadTokenUsage; use codex_app_server_protocol::ToolRequestUserInputParams; -use codex_app_server_protocol::TrackUsageLimitBannerAction; use codex_app_server_protocol::Turn; use codex_app_server_protocol::TurnCompletedNotification; use codex_app_server_protocol::TurnPlanStepStatus; use codex_app_server_protocol::TurnStatus; +use codex_app_server_protocol::UsageLimitBannerAction; use codex_app_server_protocol::UsageLimitBannerType; use codex_chatgpt::connectors; use codex_config::ConfigLayerStackOrdering; @@ -8592,9 +8593,11 @@ impl ChatWidget { } }; let send_actions: Vec = vec![Box::new(move |tx| { - tx.send(AppEvent::TrackUsageLimitBanner { - action: TrackUsageLimitBannerAction::CtaClicked, - banner_type, + tx.send(AppEvent::TrackProductAnalyticsEvent { + event: ProductAnalyticsEvent::UsageLimitBanner { + action: UsageLimitBannerAction::CtaClicked, + banner_type, + }, }); tx.send(AppEvent::SendAddCreditsNudgeEmail { credit_type }); })]; @@ -8623,10 +8626,13 @@ impl ChatWidget { initial_selected_idx: Some(1), ..Default::default() }); - self.app_event_tx.send(AppEvent::TrackUsageLimitBanner { - action: TrackUsageLimitBannerAction::Shown, - banner_type, - }); + self.app_event_tx + .send(AppEvent::TrackProductAnalyticsEvent { + event: ProductAnalyticsEvent::UsageLimitBanner { + action: UsageLimitBannerAction::Shown, + banner_type, + }, + }); } pub(crate) fn start_add_credits_nudge_email_request( diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index caef74684e..985be6bf1f 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -83,17 +83,18 @@ pub(super) use codex_app_server_protocol::PluginMarketplaceEntry; pub(super) use codex_app_server_protocol::PluginReadResponse; pub(super) use codex_app_server_protocol::PluginSource; pub(super) use codex_app_server_protocol::PluginSummary; +pub(super) use codex_app_server_protocol::ProductAnalyticsEvent; pub(super) use codex_app_server_protocol::ReasoningSummaryTextDeltaNotification; pub(super) use codex_app_server_protocol::ServerNotification; pub(super) use codex_app_server_protocol::SkillSummary; pub(super) use codex_app_server_protocol::ThreadClosedNotification; pub(super) use codex_app_server_protocol::ThreadItem as AppServerThreadItem; -pub(super) use codex_app_server_protocol::TrackUsageLimitBannerAction; pub(super) use codex_app_server_protocol::Turn as AppServerTurn; pub(super) use codex_app_server_protocol::TurnCompletedNotification; pub(super) use codex_app_server_protocol::TurnError as AppServerTurnError; pub(super) use codex_app_server_protocol::TurnStartedNotification; pub(super) use codex_app_server_protocol::TurnStatus as AppServerTurnStatus; +pub(super) use codex_app_server_protocol::UsageLimitBannerAction; pub(super) use codex_app_server_protocol::UsageLimitBannerType; pub(super) use codex_app_server_protocol::UserInput as AppServerUserInput; pub(super) use codex_app_server_protocol::WarningNotification; diff --git a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs index 6b1807b63d..0e47ff4732 100644 --- a/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs +++ b/codex-rs/tui/src/chatwidget/tests/status_and_layout.rs @@ -607,7 +607,7 @@ async fn workspace_member_credits_depleted_prompts_and_sends_credits() { assert_eq!( event, ( - TrackUsageLimitBannerAction::Shown, + UsageLimitBannerAction::Shown, UsageLimitBannerType::WorkspaceMemberCreditsDepleted ) ); @@ -617,7 +617,7 @@ async fn workspace_member_credits_depleted_prompts_and_sends_credits() { assert_eq!( event, ( - TrackUsageLimitBannerAction::CtaClicked, + UsageLimitBannerAction::CtaClicked, UsageLimitBannerType::WorkspaceMemberCreditsDepleted ) ); @@ -643,7 +643,7 @@ async fn workspace_member_usage_limit_prompts_and_sends_usage_limit() { assert_eq!( event, ( - TrackUsageLimitBannerAction::Shown, + UsageLimitBannerAction::Shown, UsageLimitBannerType::WorkspaceMemberUsageLimitReached ) ); @@ -653,7 +653,7 @@ async fn workspace_member_usage_limit_prompts_and_sends_usage_limit() { assert_eq!( event, ( - TrackUsageLimitBannerAction::CtaClicked, + UsageLimitBannerAction::CtaClicked, UsageLimitBannerType::WorkspaceMemberUsageLimitReached ) ); @@ -812,7 +812,7 @@ async fn workspace_owner_nudge_default_no_dismisses_without_sending() { assert_eq!( event, ( - TrackUsageLimitBannerAction::Shown, + UsageLimitBannerAction::Shown, UsageLimitBannerType::WorkspaceMemberCreditsDepleted ) ); @@ -837,7 +837,7 @@ async fn workspace_owner_nudge_reappears_after_dismissing_no() { assert_eq!( event, ( - TrackUsageLimitBannerAction::Shown, + UsageLimitBannerAction::Shown, UsageLimitBannerType::WorkspaceMemberUsageLimitReached ) ); @@ -942,17 +942,20 @@ fn next_send_add_credits_nudge_email_event( fn next_track_usage_limit_banner_event( rx: &mut tokio::sync::mpsc::UnboundedReceiver, -) -> (TrackUsageLimitBannerAction, UsageLimitBannerType) { +) -> (UsageLimitBannerAction, UsageLimitBannerType) { while let Ok(event) = rx.try_recv() { - if let AppEvent::TrackUsageLimitBanner { - action, - banner_type, + if let AppEvent::TrackProductAnalyticsEvent { + event: + ProductAnalyticsEvent::UsageLimitBanner { + action, + banner_type, + }, } = event { return (action, banner_type); } } - panic!("expected TrackUsageLimitBanner app event"); + panic!("expected usage-limit product analytics app event"); } fn assert_no_owner_nudge_or_rate_limit_refresh( @@ -964,7 +967,7 @@ fn assert_no_owner_nudge_or_rate_limit_refresh( event, AppEvent::SendAddCreditsNudgeEmail { .. } | AppEvent::RefreshRateLimits { .. } - | AppEvent::TrackUsageLimitBanner { .. } + | AppEvent::TrackProductAnalyticsEvent { .. } ), "unexpected event: {event:?}" );