Generalize product analytics event tracking

This commit is contained in:
James Chu
2026-04-27 18:26:35 -04:00
parent f26d65b9c5
commit 2575cf76a4
20 changed files with 293 additions and 158 deletions
@@ -66,6 +66,7 @@ use codex_app_server_protocol::InitializeParams;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::NonSteerableTurnKind;
use codex_app_server_protocol::PermissionProfile as AppServerPermissionProfile;
use codex_app_server_protocol::ProductAnalyticsEvent;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::SandboxPolicy as AppServerSandboxPolicy;
use codex_app_server_protocol::ServerNotification;
@@ -74,8 +75,7 @@ use codex_app_server_protocol::Thread;
use codex_app_server_protocol::ThreadResumeResponse;
use codex_app_server_protocol::ThreadStartResponse;
use codex_app_server_protocol::ThreadStatus as AppServerThreadStatus;
use codex_app_server_protocol::TrackUsageLimitBannerAction;
use codex_app_server_protocol::TrackUsageLimitBannerParams;
use codex_app_server_protocol::TrackProductAnalyticsEventParams;
use codex_app_server_protocol::Turn;
use codex_app_server_protocol::TurnCompletedNotification;
use codex_app_server_protocol::TurnError as AppServerTurnError;
@@ -84,6 +84,7 @@ use codex_app_server_protocol::TurnStartedNotification;
use codex_app_server_protocol::TurnStatus as AppServerTurnStatus;
use codex_app_server_protocol::TurnSteerParams;
use codex_app_server_protocol::TurnSteerResponse;
use codex_app_server_protocol::UsageLimitBannerAction;
use codex_app_server_protocol::UsageLimitBannerType;
use codex_app_server_protocol::UserInput;
use codex_login::default_client::DEFAULT_ORIGINATOR;
@@ -2417,11 +2418,13 @@ async fn usage_limit_banner_shown_request_emits_credits_event() {
AnalyticsFact::Request {
connection_id: 7,
request_id: RequestId::Integer(1),
request: Box::new(ClientRequest::TrackUsageLimitBanner {
request: Box::new(ClientRequest::TrackProductAnalyticsEvent {
request_id: RequestId::Integer(1),
params: TrackUsageLimitBannerParams {
action: TrackUsageLimitBannerAction::Shown,
banner_type: UsageLimitBannerType::WorkspaceMemberCreditsDepleted,
params: TrackProductAnalyticsEventParams {
event: ProductAnalyticsEvent::UsageLimitBanner {
action: UsageLimitBannerAction::Shown,
banner_type: UsageLimitBannerType::WorkspaceMemberCreditsDepleted,
},
},
}),
},
@@ -2453,11 +2456,13 @@ async fn usage_limit_banner_click_request_emits_usage_limit_event() {
AnalyticsFact::Request {
connection_id: 7,
request_id: RequestId::Integer(1),
request: Box::new(ClientRequest::TrackUsageLimitBanner {
request: Box::new(ClientRequest::TrackProductAnalyticsEvent {
request_id: RequestId::Integer(1),
params: TrackUsageLimitBannerParams {
action: TrackUsageLimitBannerAction::CtaClicked,
banner_type: UsageLimitBannerType::WorkspaceMemberUsageLimitReached,
params: TrackProductAnalyticsEventParams {
event: ProductAnalyticsEvent::UsageLimitBanner {
action: UsageLimitBannerAction::CtaClicked,
banner_type: UsageLimitBannerType::WorkspaceMemberUsageLimitReached,
},
},
}),
},
+13 -13
View File
@@ -20,8 +20,7 @@ use crate::facts::TurnSteerResult;
use crate::facts::TurnSubmissionType;
use crate::now_unix_seconds;
use codex_app_server_protocol::CodexErrorInfo;
use codex_app_server_protocol::TrackUsageLimitBannerAction;
use codex_app_server_protocol::TrackUsageLimitBannerParams;
use codex_app_server_protocol::UsageLimitBannerAction;
use codex_app_server_protocol::UsageLimitBannerType;
use codex_login::default_client::originator;
use codex_plugin::PluginTelemetryMetadata;
@@ -591,35 +590,36 @@ pub(crate) fn plugin_state_event_type(state: PluginState) -> &'static str {
}
pub(crate) fn usage_limit_banner_event_request(
params: TrackUsageLimitBannerParams,
action: UsageLimitBannerAction,
banner_type: UsageLimitBannerType,
) -> UsageLimitBannerEventRequest {
let limit_reason = match params.banner_type {
let limit_reason = match banner_type {
UsageLimitBannerType::WorkspaceMemberCreditsDepleted => "credits",
UsageLimitBannerType::WorkspaceMemberUsageLimitReached => "usage_limit",
};
let banner_type = match params.banner_type {
let banner_type_param = match banner_type {
UsageLimitBannerType::WorkspaceMemberCreditsDepleted => "workspace_member_credits_depleted",
UsageLimitBannerType::WorkspaceMemberUsageLimitReached => {
"workspace_member_usage_limit_reached"
}
};
let cta_action = match params.banner_type {
let cta_action = match banner_type {
UsageLimitBannerType::WorkspaceMemberCreditsDepleted => "notify_owner",
UsageLimitBannerType::WorkspaceMemberUsageLimitReached => "request_increase",
};
let cta_action = match params.action {
TrackUsageLimitBannerAction::Shown => None,
TrackUsageLimitBannerAction::CtaClicked => Some(cta_action),
let cta_action = match action {
UsageLimitBannerAction::Shown => None,
UsageLimitBannerAction::CtaClicked => Some(cta_action),
};
UsageLimitBannerEventRequest {
event_type: match params.action {
TrackUsageLimitBannerAction::Shown => "codex_usage_limit_banner_shown",
TrackUsageLimitBannerAction::CtaClicked => "codex_usage_limit_banner_cta_clicked",
event_type: match action {
UsageLimitBannerAction::Shown => "codex_usage_limit_banner_shown",
UsageLimitBannerAction::CtaClicked => "codex_usage_limit_banner_cta_clicked",
},
event_params: UsageLimitBannerEventParams {
platform: "codex_cli",
banner_type,
banner_type: banner_type_param,
limit_reason,
cta_action,
},
+12 -4
View File
@@ -52,6 +52,7 @@ use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::ClientResponse;
use codex_app_server_protocol::CodexErrorInfo;
use codex_app_server_protocol::InitializeParams;
use codex_app_server_protocol::ProductAnalyticsEvent;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ServerNotification;
use codex_app_server_protocol::TurnSteerResponse;
@@ -333,10 +334,17 @@ impl AnalyticsReducer {
}),
);
}
ClientRequest::TrackUsageLimitBanner { params, .. } => {
out.push(TrackEventRequest::UsageLimitBanner(
usage_limit_banner_event_request(params),
));
ClientRequest::TrackProductAnalyticsEvent { params, .. } => {
let event = match params.event {
ProductAnalyticsEvent::UsageLimitBanner {
action,
banner_type,
} => TrackEventRequest::UsageLimitBanner(usage_limit_banner_event_request(
action,
banner_type,
)),
};
out.push(event);
}
_ => {}
}
+43 -19
View File
@@ -2126,6 +2126,34 @@
],
"type": "object"
},
"ProductAnalyticsEvent": {
"oneOf": [
{
"properties": {
"action": {
"$ref": "#/definitions/UsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/UsageLimitBannerType"
},
"type": {
"enum": [
"usageLimitBanner"
],
"title": "UsageLimitBannerProductAnalyticsEventType",
"type": "string"
}
},
"required": [
"action",
"bannerType",
"type"
],
"title": "UsageLimitBannerProductAnalyticsEvent",
"type": "object"
}
]
},
"RealtimeOutputModality": {
"enum": [
"text",
@@ -4082,25 +4110,14 @@
],
"type": "object"
},
"TrackUsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"TrackUsageLimitBannerParams": {
"TrackProductAnalyticsEventParams": {
"properties": {
"action": {
"$ref": "#/definitions/TrackUsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/UsageLimitBannerType"
"event": {
"$ref": "#/definitions/ProductAnalyticsEvent"
}
},
"required": [
"action",
"bannerType"
"event"
],
"type": "object"
},
@@ -4287,6 +4304,13 @@
],
"type": "object"
},
"UsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"UsageLimitBannerType": {
"enum": [
"workspace_member_credits_depleted",
@@ -5843,13 +5867,13 @@
},
"method": {
"enum": [
"account/usageLimitBanner/track"
"analytics/productEvent/track"
],
"title": "Account/usageLimitBanner/trackRequestMethod",
"title": "Analytics/productEvent/trackRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/TrackUsageLimitBannerParams"
"$ref": "#/definitions/TrackProductAnalyticsEventParams"
}
},
"required": [
@@ -5857,7 +5881,7 @@
"method",
"params"
],
"title": "Account/usageLimitBanner/trackRequest",
"title": "Analytics/productEvent/trackRequest",
"type": "object"
},
{
@@ -1582,13 +1582,13 @@
},
"method": {
"enum": [
"account/usageLimitBanner/track"
"analytics/productEvent/track"
],
"title": "Account/usageLimitBanner/trackRequestMethod",
"title": "Analytics/productEvent/trackRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/TrackUsageLimitBannerParams"
"$ref": "#/definitions/v2/TrackProductAnalyticsEventParams"
}
},
"required": [
@@ -1596,7 +1596,7 @@
"method",
"params"
],
"title": "Account/usageLimitBanner/trackRequest",
"title": "Analytics/productEvent/trackRequest",
"type": "object"
},
{
@@ -12063,6 +12063,34 @@
],
"type": "object"
},
"ProductAnalyticsEvent": {
"oneOf": [
{
"properties": {
"action": {
"$ref": "#/definitions/v2/UsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/v2/UsageLimitBannerType"
},
"type": {
"enum": [
"usageLimitBanner"
],
"title": "UsageLimitBannerProductAnalyticsEventType",
"type": "string"
}
},
"required": [
"action",
"bannerType",
"type"
],
"title": "UsageLimitBannerProductAnalyticsEvent",
"type": "object"
}
]
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
@@ -16917,33 +16945,22 @@
},
"type": "object"
},
"TrackUsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"TrackUsageLimitBannerParams": {
"TrackProductAnalyticsEventParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"action": {
"$ref": "#/definitions/v2/TrackUsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/v2/UsageLimitBannerType"
"event": {
"$ref": "#/definitions/v2/ProductAnalyticsEvent"
}
},
"required": [
"action",
"bannerType"
"event"
],
"title": "TrackUsageLimitBannerParams",
"title": "TrackProductAnalyticsEventParams",
"type": "object"
},
"TrackUsageLimitBannerResponse": {
"TrackProductAnalyticsEventResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "TrackUsageLimitBannerResponse",
"title": "TrackProductAnalyticsEventResponse",
"type": "object"
},
"Turn": {
@@ -17370,6 +17387,13 @@
"title": "TurnSteerResponse",
"type": "object"
},
"UsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"UsageLimitBannerType": {
"enum": [
"workspace_member_credits_depleted",
@@ -2288,13 +2288,13 @@
},
"method": {
"enum": [
"account/usageLimitBanner/track"
"analytics/productEvent/track"
],
"title": "Account/usageLimitBanner/trackRequestMethod",
"title": "Analytics/productEvent/trackRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/TrackUsageLimitBannerParams"
"$ref": "#/definitions/TrackProductAnalyticsEventParams"
}
},
"required": [
@@ -2302,7 +2302,7 @@
"method",
"params"
],
"title": "Account/usageLimitBanner/trackRequest",
"title": "Analytics/productEvent/trackRequest",
"type": "object"
},
{
@@ -8737,6 +8737,34 @@
],
"type": "object"
},
"ProductAnalyticsEvent": {
"oneOf": [
{
"properties": {
"action": {
"$ref": "#/definitions/UsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/UsageLimitBannerType"
},
"type": {
"enum": [
"usageLimitBanner"
],
"title": "UsageLimitBannerProductAnalyticsEventType",
"type": "string"
}
},
"required": [
"action",
"bannerType",
"type"
],
"title": "UsageLimitBannerProductAnalyticsEvent",
"type": "object"
}
]
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
@@ -14803,33 +14831,22 @@
},
"type": "object"
},
"TrackUsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"TrackUsageLimitBannerParams": {
"TrackProductAnalyticsEventParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"action": {
"$ref": "#/definitions/TrackUsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/UsageLimitBannerType"
"event": {
"$ref": "#/definitions/ProductAnalyticsEvent"
}
},
"required": [
"action",
"bannerType"
"event"
],
"title": "TrackUsageLimitBannerParams",
"title": "TrackProductAnalyticsEventParams",
"type": "object"
},
"TrackUsageLimitBannerResponse": {
"TrackProductAnalyticsEventResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "TrackUsageLimitBannerResponse",
"title": "TrackProductAnalyticsEventResponse",
"type": "object"
},
"Turn": {
@@ -15256,6 +15273,13 @@
"title": "TurnSteerResponse",
"type": "object"
},
"UsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"UsageLimitBannerType": {
"enum": [
"workspace_member_credits_depleted",
@@ -0,0 +1,57 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ProductAnalyticsEvent": {
"oneOf": [
{
"properties": {
"action": {
"$ref": "#/definitions/UsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/UsageLimitBannerType"
},
"type": {
"enum": [
"usageLimitBanner"
],
"title": "UsageLimitBannerProductAnalyticsEventType",
"type": "string"
}
},
"required": [
"action",
"bannerType",
"type"
],
"title": "UsageLimitBannerProductAnalyticsEvent",
"type": "object"
}
]
},
"UsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"UsageLimitBannerType": {
"enum": [
"workspace_member_credits_depleted",
"workspace_member_usage_limit_reached"
],
"type": "string"
}
},
"properties": {
"event": {
"$ref": "#/definitions/ProductAnalyticsEvent"
}
},
"required": [
"event"
],
"title": "TrackProductAnalyticsEventParams",
"type": "object"
}
@@ -1,5 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "TrackUsageLimitBannerResponse",
"title": "TrackProductAnalyticsEventResponse",
"type": "object"
}
@@ -1,33 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"TrackUsageLimitBannerAction": {
"enum": [
"shown",
"cta_clicked"
],
"type": "string"
},
"UsageLimitBannerType": {
"enum": [
"workspace_member_credits_depleted",
"workspace_member_usage_limit_reached"
],
"type": "string"
}
},
"properties": {
"action": {
"$ref": "#/definitions/TrackUsageLimitBannerAction"
},
"bannerType": {
"$ref": "#/definitions/UsageLimitBannerType"
}
},
"required": [
"action",
"bannerType"
],
"title": "TrackUsageLimitBannerParams",
"type": "object"
}
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { TrackUsageLimitBannerAction } from "./TrackUsageLimitBannerAction";
import type { UsageLimitBannerAction } from "./UsageLimitBannerAction";
import type { UsageLimitBannerType } from "./UsageLimitBannerType";
export type TrackUsageLimitBannerParams = { action: TrackUsageLimitBannerAction, bannerType: UsageLimitBannerType, };
export type ProductAnalyticsEvent = { "type": "usageLimitBanner", action: UsageLimitBannerAction, bannerType: UsageLimitBannerType, };
@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ProductAnalyticsEvent } from "./ProductAnalyticsEvent";
export type TrackProductAnalyticsEventParams = { event: ProductAnalyticsEvent, };
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type TrackUsageLimitBannerAction = "shown" | "cta_clicked";
export type TrackProductAnalyticsEventResponse = Record<string, never>;
@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type TrackUsageLimitBannerResponse = Record<string, never>;
export type UsageLimitBannerAction = "shown" | "cta_clicked";
+4 -3
View File
@@ -271,6 +271,7 @@ export type { PluginSummary } from "./PluginSummary";
export type { PluginUninstallParams } from "./PluginUninstallParams";
export type { PluginUninstallResponse } from "./PluginUninstallResponse";
export type { PluginsMigration } from "./PluginsMigration";
export type { ProductAnalyticsEvent } from "./ProductAnalyticsEvent";
export type { ProfileV2 } from "./ProfileV2";
export type { RateLimitReachedType } from "./RateLimitReachedType";
export type { RateLimitSnapshot } from "./RateLimitSnapshot";
@@ -386,9 +387,8 @@ export type { ToolRequestUserInputParams } from "./ToolRequestUserInputParams";
export type { ToolRequestUserInputQuestion } from "./ToolRequestUserInputQuestion";
export type { ToolRequestUserInputResponse } from "./ToolRequestUserInputResponse";
export type { ToolsV2 } from "./ToolsV2";
export type { TrackUsageLimitBannerAction } from "./TrackUsageLimitBannerAction";
export type { TrackUsageLimitBannerParams } from "./TrackUsageLimitBannerParams";
export type { TrackUsageLimitBannerResponse } from "./TrackUsageLimitBannerResponse";
export type { TrackProductAnalyticsEventParams } from "./TrackProductAnalyticsEventParams";
export type { TrackProductAnalyticsEventResponse } from "./TrackProductAnalyticsEventResponse";
export type { Turn } from "./Turn";
export type { TurnCompletedNotification } from "./TurnCompletedNotification";
export type { TurnDiffUpdatedNotification } from "./TurnDiffUpdatedNotification";
@@ -405,6 +405,7 @@ export type { TurnStartedNotification } from "./TurnStartedNotification";
export type { TurnStatus } from "./TurnStatus";
export type { TurnSteerParams } from "./TurnSteerParams";
export type { TurnSteerResponse } from "./TurnSteerResponse";
export type { UsageLimitBannerAction } from "./UsageLimitBannerAction";
export type { UsageLimitBannerType } from "./UsageLimitBannerType";
export type { UserInput } from "./UserInput";
export type { WarningNotification } from "./WarningNotification";
@@ -573,9 +573,9 @@ client_request_definitions! {
response: v2::SendAddCreditsNudgeEmailResponse,
},
TrackUsageLimitBanner => "account/usageLimitBanner/track" {
params: v2::TrackUsageLimitBannerParams,
response: v2::TrackUsageLimitBannerResponse,
TrackProductAnalyticsEvent => "analytics/productEvent/track" {
params: v2::TrackProductAnalyticsEventParams,
response: v2::TrackProductAnalyticsEventResponse,
},
FeedbackUpload => "feedback/upload" {
@@ -1468,23 +1468,28 @@ mod tests {
}
#[test]
fn serialize_track_usage_limit_banner() -> Result<()> {
let request = ClientRequest::TrackUsageLimitBanner {
fn serialize_track_product_analytics_event() -> Result<()> {
let request = ClientRequest::TrackProductAnalyticsEvent {
request_id: RequestId::Integer(2),
params: v2::TrackUsageLimitBannerParams {
action: v2::TrackUsageLimitBannerAction::CtaClicked,
banner_type: v2::UsageLimitBannerType::WorkspaceMemberUsageLimitReached,
params: v2::TrackProductAnalyticsEventParams {
event: v2::ProductAnalyticsEvent::UsageLimitBanner {
action: v2::UsageLimitBannerAction::CtaClicked,
banner_type: v2::UsageLimitBannerType::WorkspaceMemberUsageLimitReached,
},
},
};
assert_eq!(request.id(), &RequestId::Integer(2));
assert_eq!(request.method(), "account/usageLimitBanner/track");
assert_eq!(request.method(), "analytics/productEvent/track");
assert_eq!(
json!({
"method": "account/usageLimitBanner/track",
"method": "analytics/productEvent/track",
"id": 2,
"params": {
"action": "cta_clicked",
"bannerType": "workspace_member_usage_limit_reached",
"event": {
"type": "usageLimitBanner",
"action": "cta_clicked",
"bannerType": "workspace_member_usage_limit_reached",
},
},
}),
serde_json::to_value(&request)?,
@@ -2253,15 +2253,26 @@ pub struct SendAddCreditsNudgeEmailParams {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TrackUsageLimitBannerParams {
pub action: TrackUsageLimitBannerAction,
pub banner_type: UsageLimitBannerType,
pub struct TrackProductAnalyticsEventParams {
pub event: ProductAnalyticsEvent,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase")]
#[ts(export_to = "v2/", tag = "type", rename_all = "camelCase")]
pub enum ProductAnalyticsEvent {
UsageLimitBanner {
action: UsageLimitBannerAction,
#[serde(rename = "bannerType")]
#[ts(rename = "bannerType")]
banner_type: UsageLimitBannerType,
},
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
#[ts(export_to = "v2/", rename_all = "snake_case")]
pub enum TrackUsageLimitBannerAction {
pub enum UsageLimitBannerAction {
Shown,
CtaClicked,
}
@@ -2292,7 +2303,7 @@ pub struct SendAddCreditsNudgeEmailResponse {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct TrackUsageLimitBannerResponse {}
pub struct TrackProductAnalyticsEventResponse {}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
+7 -4
View File
@@ -1559,7 +1559,6 @@ Codex supports these authentication modes. The current mode is surfaced in `acco
- `account/rateLimits/read` — fetch ChatGPT rate limits; updates arrive via `account/rateLimits/updated` (notify).
- `account/rateLimits/updated` (notify) — emitted whenever a user's ChatGPT rate limits change.
- `account/sendAddCreditsNudgeEmail` — ask ChatGPT to email the workspace owner about depleted credits or a reached usage limit.
- `account/usageLimitBanner/track` — record that the CLI workspace-owner nudge prompt was shown or its CTA was clicked.
- `mcpServer/oauthLogin/completed` (notify) — emitted after a `mcpServer/oauth/login` flow finishes for a server; payload includes `{ name, success, error? }`.
- `mcpServer/startupStatus/updated` (notify) — emitted when a configured MCP server's startup status changes for a loaded thread; payload includes `{ name, status, error }` where `status` is `starting`, `ready`, `failed`, or `cancelled`.
@@ -1672,14 +1671,18 @@ Field notes:
Use `creditType: "credits"` when workspace credits are depleted, or `creditType: "usage_limit"` when the workspace usage limit has been reached. If the owner was already notified recently, the response status is `cooldown_active`.
### 9) Track a usage-limit banner
## Product analytics endpoints
- `analytics/productEvent/track` — record a typed client-side product analytics event.
### Track a product analytics event
```json
{ "method": "account/usageLimitBanner/track", "id": 9, "params": { "action": "shown", "bannerType": "workspace_member_usage_limit_reached" } }
{ "method": "analytics/productEvent/track", "id": 9, "params": { "event": { "type": "usageLimitBanner", "action": "shown", "bannerType": "workspace_member_usage_limit_reached" } } }
{ "id": 9, "result": {} }
```
Use `action: "shown"` when the banner is displayed, and `action: "cta_clicked"` when the user confirms it. The current workspace-owner nudge variants are `workspace_member_credits_depleted` and `workspace_member_usage_limit_reached`.
The current `usageLimitBanner` product event records when the CLI workspace-owner nudge prompt is shown or its CTA is clicked. Use `action: "shown"` when the banner is displayed, and `action: "cta_clicked"` when the user confirms it. The current workspace-owner nudge variants are `workspace_member_credits_depleted` and `workspace_member_usage_limit_reached`.
## Experimental API Opt-in
@@ -211,7 +211,7 @@ use codex_app_server_protocol::ThreadUnarchivedNotification;
use codex_app_server_protocol::ThreadUnsubscribeParams;
use codex_app_server_protocol::ThreadUnsubscribeResponse;
use codex_app_server_protocol::ThreadUnsubscribeStatus;
use codex_app_server_protocol::TrackUsageLimitBannerResponse;
use codex_app_server_protocol::TrackProductAnalyticsEventResponse;
use codex_app_server_protocol::Turn;
use codex_app_server_protocol::TurnError;
use codex_app_server_protocol::TurnInterruptParams;
@@ -1275,14 +1275,14 @@ impl CodexMessageProcessor {
self.send_add_credits_nudge_email(to_connection_request_id(request_id), params)
.await;
}
ClientRequest::TrackUsageLimitBanner {
ClientRequest::TrackProductAnalyticsEvent {
request_id,
params: _,
} => {
self.outgoing
.send_response(
to_connection_request_id(request_id),
TrackUsageLimitBannerResponse {},
TrackProductAnalyticsEventResponse {},
)
.await;
}
+1 -1
View File
@@ -716,7 +716,7 @@ impl MessageProcessor {
let connection_id = connection_request_id.connection_id;
if let ClientRequest::TurnStart { request_id, .. }
| ClientRequest::TurnSteer { request_id, .. }
| ClientRequest::TrackUsageLimitBanner { request_id, .. } = &codex_request
| ClientRequest::TrackProductAnalyticsEvent { request_id, .. } = &codex_request
{
self.analytics_events_client.track_request(
connection_id.0,