mirror of
https://github.com/openai/codex.git
synced 2026-04-27 16:15:09 +00:00
- event for compaction analytics - introduces thread-connection and thread metadata caches for data denormalization, expected to be useful for denormalization onto core emitted events in general - threads analytics event client into core (mirrors approved implementation in #16640) - denormalizes key thread metadata: thread_source, subagent_source, parent_thread_id, as well as app-server client and runtime metadata) - compaction strategy defaults to memento, forward compatible with expected prefill_compaction strategy 1. Manual standalone compact, local `INFO | 2026-04-09 17:35:50 | codex_backend.routers.analytics_events | analytics_events.track_analytics_events:526 | Tracked codex_compaction_event event params={'thread_id': '019d74d0-5cfb-70c0-bef9-165c3bf9b2df', 'turn_id': '019d74d0-d7f6-7c81-acc6-aae2030243d6', 'product_surface': 'codex', 'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name': 'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process', 'experimental_api_enabled': True}, 'runtime': {'codex_rs_version': '0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0', 'runtime_arch': 'aarch64'}, 'trigger': 'manual', 'reason': 'user_requested', 'implementation': 'responses', 'phase': 'standalone_turn', 'strategy': 'memento', 'status': 'completed', 'active_context_tokens_before': 20170, 'active_context_tokens_after': 4830, 'started_at': 1775781337, 'completed_at': 1775781350, 'thread_source': 'user', 'subagent_source': None, 'parent_thread_id': None, 'error': None, 'duration_ms': 13524} | ` 2. Auto pre-turn compact, local `INFO | 2026-04-09 17:37:30 | codex_backend.routers.analytics_events | analytics_events.track_analytics_events:526 | Tracked codex_compaction_event event params={'thread_id': '019d74d2-45ef-71d1-9c93-23cc0c13d988', 'turn_id': '019d74d2-7b42-7372-9f0e-c0da3f352328', 'product_surface': 'codex', 'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name': 'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process', 'experimental_api_enabled': True}, 'runtime': {'codex_rs_version': '0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0', 'runtime_arch': 'aarch64'}, 'trigger': 'auto', 'reason': 'context_limit', 'implementation': 'responses', 'phase': 'pre_turn', 'strategy': 'memento', 'status': 'completed', 'active_context_tokens_before': 20063, 'active_context_tokens_after': 4822, 'started_at': 1775781444, 'completed_at': 1775781449, 'thread_source': 'user', 'subagent_source': None, 'parent_thread_id': None, 'error': None, 'duration_ms': 5497} | ` 3. Auto mid-turn compact, local `INFO | 2026-04-09 17:38:28 | codex_backend.routers.analytics_events | analytics_events.track_analytics_events:526 | Tracked codex_compaction_event event params={'thread_id': '019d74d3-212f-7a20-8c0a-4816a978675e', 'turn_id': '019d74d3-3ee1-7462-89f6-2ffbeefcd5e3', 'product_surface': 'codex', 'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name': 'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process', 'experimental_api_enabled': True}, 'runtime': {'codex_rs_version': '0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0', 'runtime_arch': 'aarch64'}, 'trigger': 'auto', 'reason': 'context_limit', 'implementation': 'responses', 'phase': 'mid_turn', 'strategy': 'memento', 'status': 'completed', 'active_context_tokens_before': 20325, 'active_context_tokens_after': 14641, 'started_at': 1775781500, 'completed_at': 1775781508, 'thread_source': 'user', 'subagent_source': None, 'parent_thread_id': None, 'error': None, 'duration_ms': 7507} | ` 4. Remote /responses/compact, manual standalone `INFO | 2026-04-09 17:40:20 | codex_backend.routers.analytics_events | analytics_events.track_analytics_events:526 | Tracked codex_compaction_event event params={'thread_id': '019d74d4-7a11-78a1-89f7-0535a1149416', 'turn_id': '019d74d4-e087-7183-9c20-b1e40b7578c0', 'product_surface': 'codex', 'app_server_client': {'product_client_id': 'CODEX_CLI', 'client_name': 'codex-tui', 'client_version': '0.0.0', 'rpc_transport': 'in_process', 'experimental_api_enabled': True}, 'runtime': {'codex_rs_version': '0.0.0', 'runtime_os': 'macos', 'runtime_os_version': '26.4.0', 'runtime_arch': 'aarch64'}, 'trigger': 'manual', 'reason': 'user_requested', 'implementation': 'responses_compact', 'phase': 'standalone_turn', 'strategy': 'memento', 'status': 'completed', 'active_context_tokens_before': 23461, 'active_context_tokens_after': 6171, 'started_at': 1775781601, 'completed_at': 1775781620, 'thread_source': 'user', 'subagent_source': None, 'parent_thread_id': None, 'error': None, 'duration_ms': 18971} | `
343 lines
12 KiB
Rust
343 lines
12 KiB
Rust
use crate::facts::AppInvocation;
|
|
use crate::facts::CodexCompactionEvent;
|
|
use crate::facts::InvocationType;
|
|
use crate::facts::PluginState;
|
|
use crate::facts::SubAgentThreadStartedInput;
|
|
use crate::facts::TrackEventsContext;
|
|
use codex_login::default_client::originator;
|
|
use codex_plugin::PluginTelemetryMetadata;
|
|
use codex_protocol::protocol::SessionSource;
|
|
use codex_protocol::protocol::SubAgentSource;
|
|
use serde::Serialize;
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum AppServerRpcTransport {
|
|
Stdio,
|
|
Websocket,
|
|
InProcess,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub(crate) enum ThreadInitializationMode {
|
|
New,
|
|
Forked,
|
|
Resumed,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct TrackEventsRequest {
|
|
pub(crate) events: Vec<TrackEventRequest>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
#[serde(untagged)]
|
|
pub(crate) enum TrackEventRequest {
|
|
SkillInvocation(SkillInvocationEventRequest),
|
|
ThreadInitialized(ThreadInitializedEvent),
|
|
AppMentioned(CodexAppMentionedEventRequest),
|
|
AppUsed(CodexAppUsedEventRequest),
|
|
Compaction(Box<CodexCompactionEventRequest>),
|
|
PluginUsed(CodexPluginUsedEventRequest),
|
|
PluginInstalled(CodexPluginEventRequest),
|
|
PluginUninstalled(CodexPluginEventRequest),
|
|
PluginEnabled(CodexPluginEventRequest),
|
|
PluginDisabled(CodexPluginEventRequest),
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct SkillInvocationEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) skill_id: String,
|
|
pub(crate) skill_name: String,
|
|
pub(crate) event_params: SkillInvocationEventParams,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct SkillInvocationEventParams {
|
|
pub(crate) product_client_id: Option<String>,
|
|
pub(crate) skill_scope: Option<String>,
|
|
pub(crate) repo_url: Option<String>,
|
|
pub(crate) thread_id: Option<String>,
|
|
pub(crate) invoke_type: Option<InvocationType>,
|
|
pub(crate) model_slug: Option<String>,
|
|
}
|
|
|
|
#[derive(Clone, Serialize)]
|
|
pub(crate) struct CodexAppServerClientMetadata {
|
|
pub(crate) product_client_id: String,
|
|
pub(crate) client_name: Option<String>,
|
|
pub(crate) client_version: Option<String>,
|
|
pub(crate) rpc_transport: AppServerRpcTransport,
|
|
pub(crate) experimental_api_enabled: Option<bool>,
|
|
}
|
|
|
|
#[derive(Clone, Serialize)]
|
|
pub(crate) struct CodexRuntimeMetadata {
|
|
pub(crate) codex_rs_version: String,
|
|
pub(crate) runtime_os: String,
|
|
pub(crate) runtime_os_version: String,
|
|
pub(crate) runtime_arch: String,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct ThreadInitializedEventParams {
|
|
pub(crate) thread_id: String,
|
|
pub(crate) app_server_client: CodexAppServerClientMetadata,
|
|
pub(crate) runtime: CodexRuntimeMetadata,
|
|
pub(crate) model: String,
|
|
pub(crate) ephemeral: bool,
|
|
pub(crate) thread_source: Option<&'static str>,
|
|
pub(crate) initialization_mode: ThreadInitializationMode,
|
|
pub(crate) subagent_source: Option<String>,
|
|
pub(crate) parent_thread_id: Option<String>,
|
|
pub(crate) created_at: u64,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct ThreadInitializedEvent {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: ThreadInitializedEventParams,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexAppMetadata {
|
|
pub(crate) connector_id: Option<String>,
|
|
pub(crate) thread_id: Option<String>,
|
|
pub(crate) turn_id: Option<String>,
|
|
pub(crate) app_name: Option<String>,
|
|
pub(crate) product_client_id: Option<String>,
|
|
pub(crate) invoke_type: Option<InvocationType>,
|
|
pub(crate) model_slug: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexAppMentionedEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexAppMetadata,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexAppUsedEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexAppMetadata,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexCompactionEventParams {
|
|
pub(crate) thread_id: String,
|
|
pub(crate) turn_id: String,
|
|
pub(crate) app_server_client: CodexAppServerClientMetadata,
|
|
pub(crate) runtime: CodexRuntimeMetadata,
|
|
pub(crate) thread_source: Option<&'static str>,
|
|
pub(crate) subagent_source: Option<String>,
|
|
pub(crate) parent_thread_id: Option<String>,
|
|
pub(crate) trigger: crate::facts::CompactionTrigger,
|
|
pub(crate) reason: crate::facts::CompactionReason,
|
|
pub(crate) implementation: crate::facts::CompactionImplementation,
|
|
pub(crate) phase: crate::facts::CompactionPhase,
|
|
pub(crate) strategy: crate::facts::CompactionStrategy,
|
|
pub(crate) status: crate::facts::CompactionStatus,
|
|
pub(crate) error: Option<String>,
|
|
pub(crate) active_context_tokens_before: i64,
|
|
pub(crate) active_context_tokens_after: i64,
|
|
pub(crate) started_at: u64,
|
|
pub(crate) completed_at: u64,
|
|
pub(crate) duration_ms: Option<u64>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexCompactionEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexCompactionEventParams,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexPluginMetadata {
|
|
pub(crate) plugin_id: Option<String>,
|
|
pub(crate) plugin_name: Option<String>,
|
|
pub(crate) marketplace_name: Option<String>,
|
|
pub(crate) has_skills: Option<bool>,
|
|
pub(crate) mcp_server_count: Option<usize>,
|
|
pub(crate) connector_ids: Option<Vec<String>>,
|
|
pub(crate) product_client_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexPluginUsedMetadata {
|
|
#[serde(flatten)]
|
|
pub(crate) plugin: CodexPluginMetadata,
|
|
pub(crate) thread_id: Option<String>,
|
|
pub(crate) turn_id: Option<String>,
|
|
pub(crate) model_slug: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexPluginEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexPluginMetadata,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexPluginUsedEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexPluginUsedMetadata,
|
|
}
|
|
|
|
pub(crate) fn plugin_state_event_type(state: PluginState) -> &'static str {
|
|
match state {
|
|
PluginState::Installed => "codex_plugin_installed",
|
|
PluginState::Uninstalled => "codex_plugin_uninstalled",
|
|
PluginState::Enabled => "codex_plugin_enabled",
|
|
PluginState::Disabled => "codex_plugin_disabled",
|
|
}
|
|
}
|
|
|
|
pub(crate) fn codex_app_metadata(
|
|
tracking: &TrackEventsContext,
|
|
app: AppInvocation,
|
|
) -> CodexAppMetadata {
|
|
CodexAppMetadata {
|
|
connector_id: app.connector_id,
|
|
thread_id: Some(tracking.thread_id.clone()),
|
|
turn_id: Some(tracking.turn_id.clone()),
|
|
app_name: app.app_name,
|
|
product_client_id: Some(originator().value),
|
|
invoke_type: app.invocation_type,
|
|
model_slug: Some(tracking.model_slug.clone()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn codex_plugin_metadata(plugin: PluginTelemetryMetadata) -> CodexPluginMetadata {
|
|
let capability_summary = plugin.capability_summary;
|
|
CodexPluginMetadata {
|
|
plugin_id: Some(plugin.plugin_id.as_key()),
|
|
plugin_name: Some(plugin.plugin_id.plugin_name),
|
|
marketplace_name: Some(plugin.plugin_id.marketplace_name),
|
|
has_skills: capability_summary
|
|
.as_ref()
|
|
.map(|summary| summary.has_skills),
|
|
mcp_server_count: capability_summary
|
|
.as_ref()
|
|
.map(|summary| summary.mcp_server_names.len()),
|
|
connector_ids: capability_summary.map(|summary| {
|
|
summary
|
|
.app_connector_ids
|
|
.into_iter()
|
|
.map(|connector_id| connector_id.0)
|
|
.collect()
|
|
}),
|
|
product_client_id: Some(originator().value),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn codex_compaction_event_params(
|
|
input: CodexCompactionEvent,
|
|
app_server_client: CodexAppServerClientMetadata,
|
|
runtime: CodexRuntimeMetadata,
|
|
thread_source: Option<&'static str>,
|
|
subagent_source: Option<String>,
|
|
parent_thread_id: Option<String>,
|
|
) -> CodexCompactionEventParams {
|
|
CodexCompactionEventParams {
|
|
thread_id: input.thread_id,
|
|
turn_id: input.turn_id,
|
|
app_server_client,
|
|
runtime,
|
|
thread_source,
|
|
subagent_source,
|
|
parent_thread_id,
|
|
trigger: input.trigger,
|
|
reason: input.reason,
|
|
implementation: input.implementation,
|
|
phase: input.phase,
|
|
strategy: input.strategy,
|
|
status: input.status,
|
|
error: input.error,
|
|
active_context_tokens_before: input.active_context_tokens_before,
|
|
active_context_tokens_after: input.active_context_tokens_after,
|
|
started_at: input.started_at,
|
|
completed_at: input.completed_at,
|
|
duration_ms: input.duration_ms,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn codex_plugin_used_metadata(
|
|
tracking: &TrackEventsContext,
|
|
plugin: PluginTelemetryMetadata,
|
|
) -> CodexPluginUsedMetadata {
|
|
CodexPluginUsedMetadata {
|
|
plugin: codex_plugin_metadata(plugin),
|
|
thread_id: Some(tracking.thread_id.clone()),
|
|
turn_id: Some(tracking.turn_id.clone()),
|
|
model_slug: Some(tracking.model_slug.clone()),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn thread_source_name(thread_source: &SessionSource) -> Option<&'static str> {
|
|
match thread_source {
|
|
SessionSource::Cli | SessionSource::VSCode | SessionSource::Exec => Some("user"),
|
|
SessionSource::SubAgent(_) => Some("subagent"),
|
|
SessionSource::Mcp | SessionSource::Custom(_) | SessionSource::Unknown => None,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn current_runtime_metadata() -> CodexRuntimeMetadata {
|
|
let os_info = os_info::get();
|
|
CodexRuntimeMetadata {
|
|
codex_rs_version: env!("CARGO_PKG_VERSION").to_string(),
|
|
runtime_os: std::env::consts::OS.to_string(),
|
|
runtime_os_version: os_info.version().to_string(),
|
|
runtime_arch: std::env::consts::ARCH.to_string(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn subagent_thread_started_event_request(
|
|
input: SubAgentThreadStartedInput,
|
|
) -> ThreadInitializedEvent {
|
|
let event_params = ThreadInitializedEventParams {
|
|
thread_id: input.thread_id,
|
|
app_server_client: CodexAppServerClientMetadata {
|
|
product_client_id: input.product_client_id,
|
|
client_name: Some(input.client_name),
|
|
client_version: Some(input.client_version),
|
|
rpc_transport: AppServerRpcTransport::InProcess,
|
|
experimental_api_enabled: None,
|
|
},
|
|
runtime: current_runtime_metadata(),
|
|
model: input.model,
|
|
ephemeral: input.ephemeral,
|
|
thread_source: Some("subagent"),
|
|
initialization_mode: ThreadInitializationMode::New,
|
|
subagent_source: Some(subagent_source_name(&input.subagent_source)),
|
|
parent_thread_id: input
|
|
.parent_thread_id
|
|
.or_else(|| subagent_parent_thread_id(&input.subagent_source)),
|
|
created_at: input.created_at,
|
|
};
|
|
ThreadInitializedEvent {
|
|
event_type: "codex_thread_initialized",
|
|
event_params,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn subagent_source_name(subagent_source: &SubAgentSource) -> String {
|
|
match subagent_source {
|
|
SubAgentSource::Review => "review".to_string(),
|
|
SubAgentSource::Compact => "compact".to_string(),
|
|
SubAgentSource::ThreadSpawn { .. } => "thread_spawn".to_string(),
|
|
SubAgentSource::MemoryConsolidation => "memory_consolidation".to_string(),
|
|
SubAgentSource::Other(other) => other.clone(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn subagent_parent_thread_id(subagent_source: &SubAgentSource) -> Option<String> {
|
|
match subagent_source {
|
|
SubAgentSource::ThreadSpawn {
|
|
parent_thread_id, ..
|
|
} => Some(parent_thread_id.to_string()),
|
|
_ => None,
|
|
}
|
|
}
|