mirror of
https://github.com/openai/codex.git
synced 2026-04-26 23:55:25 +00:00
## Why Guardian review analytics needs a Rust event shape that matches the backend schema while avoiding unnecessary PII exposure from reviewed tool calls. This PR narrows the analytics payload to the fields we intend to emit and keeps shared Guardian assessment enums in protocol instead of duplicating equivalent analytics-only enums. ## What changed - Uses protocol Guardian enums directly for `risk_level`, `user_authorization`, `outcome`, and command source values. - Removes high-risk reviewed-action fields from the analytics payload, including raw commands, display strings, working directories, file paths, network targets/hosts, justification text, retry reason, and rationale text. - Makes `target_item_id` and `tool_call_count` nullable so the Codex event can represent cases where the app-server protocol or producer does not have those values. - Keeps lower-risk structured reviewed-action metadata such as sandbox permissions, permission profile, `tty`, `execve` source/program, network protocol/port, and MCP connector/tool labels. - Adds an analytics reducer/client test covering `codex_guardian_review` serialization with an optional `target_item_id` and absent removed fields. ## Verification - `cargo test -p codex-analytics guardian_review_event_ingests_custom_fact_with_optional_target_item` - `cargo fmt --check` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/17692). * #17696 * #17695 * #17693 * __->__ #17692
617 lines
21 KiB
Rust
617 lines
21 KiB
Rust
use crate::facts::AppInvocation;
|
|
use crate::facts::CodexCompactionEvent;
|
|
use crate::facts::CompactionImplementation;
|
|
use crate::facts::CompactionPhase;
|
|
use crate::facts::CompactionReason;
|
|
use crate::facts::CompactionStatus;
|
|
use crate::facts::CompactionStrategy;
|
|
use crate::facts::CompactionTrigger;
|
|
use crate::facts::HookRunFact;
|
|
use crate::facts::InvocationType;
|
|
use crate::facts::PluginState;
|
|
use crate::facts::SubAgentThreadStartedInput;
|
|
use crate::facts::ThreadInitializationMode;
|
|
use crate::facts::TrackEventsContext;
|
|
use crate::facts::TurnStatus;
|
|
use crate::facts::TurnSteerRejectionReason;
|
|
use crate::facts::TurnSteerResult;
|
|
use crate::facts::TurnSubmissionType;
|
|
use codex_app_server_protocol::CodexErrorInfo;
|
|
use codex_login::default_client::originator;
|
|
use codex_plugin::PluginTelemetryMetadata;
|
|
use codex_protocol::approvals::NetworkApprovalProtocol;
|
|
use codex_protocol::models::PermissionProfile;
|
|
use codex_protocol::models::SandboxPermissions;
|
|
use codex_protocol::protocol::GuardianAssessmentOutcome;
|
|
use codex_protocol::protocol::GuardianCommandSource;
|
|
use codex_protocol::protocol::GuardianRiskLevel;
|
|
use codex_protocol::protocol::GuardianUserAuthorization;
|
|
use codex_protocol::protocol::HookEventName;
|
|
use codex_protocol::protocol::HookRunStatus;
|
|
use codex_protocol::protocol::HookSource;
|
|
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(Serialize)]
|
|
pub(crate) struct TrackEventsRequest {
|
|
pub(crate) events: Vec<TrackEventRequest>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
#[serde(untagged)]
|
|
pub(crate) enum TrackEventRequest {
|
|
SkillInvocation(SkillInvocationEventRequest),
|
|
ThreadInitialized(ThreadInitializedEvent),
|
|
GuardianReview(Box<GuardianReviewEventRequest>),
|
|
AppMentioned(CodexAppMentionedEventRequest),
|
|
AppUsed(CodexAppUsedEventRequest),
|
|
HookRun(CodexHookRunEventRequest),
|
|
Compaction(Box<CodexCompactionEventRequest>),
|
|
TurnEvent(Box<CodexTurnEventRequest>),
|
|
TurnSteer(CodexTurnSteerEventRequest),
|
|
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 GuardianReviewEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: GuardianReviewEventPayload,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum GuardianReviewDecision {
|
|
Approved,
|
|
Denied,
|
|
Aborted,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum GuardianReviewTerminalStatus {
|
|
Approved,
|
|
Denied,
|
|
Aborted,
|
|
TimedOut,
|
|
FailedClosed,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum GuardianReviewFailureReason {
|
|
Timeout,
|
|
Cancelled,
|
|
PromptBuildError,
|
|
SessionError,
|
|
ParseError,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum GuardianReviewSessionKind {
|
|
TrunkNew,
|
|
TrunkReused,
|
|
EphemeralForked,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum GuardianApprovalRequestSource {
|
|
/// Approval requested directly by the main Codex turn.
|
|
MainTurn,
|
|
/// Approval requested by a delegated subagent and routed through the parent
|
|
/// session for guardian review.
|
|
DelegatedSubagent,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize)]
|
|
#[serde(tag = "type", rename_all = "snake_case")]
|
|
pub enum GuardianReviewedAction {
|
|
Shell {
|
|
sandbox_permissions: SandboxPermissions,
|
|
additional_permissions: Option<PermissionProfile>,
|
|
},
|
|
UnifiedExec {
|
|
sandbox_permissions: SandboxPermissions,
|
|
additional_permissions: Option<PermissionProfile>,
|
|
tty: bool,
|
|
},
|
|
Execve {
|
|
source: GuardianCommandSource,
|
|
program: String,
|
|
additional_permissions: Option<PermissionProfile>,
|
|
},
|
|
ApplyPatch {},
|
|
NetworkAccess {
|
|
protocol: NetworkApprovalProtocol,
|
|
port: u16,
|
|
},
|
|
McpToolCall {
|
|
server: String,
|
|
tool_name: String,
|
|
connector_id: Option<String>,
|
|
connector_name: Option<String>,
|
|
tool_title: Option<String>,
|
|
},
|
|
}
|
|
|
|
#[derive(Clone, Serialize)]
|
|
pub struct GuardianReviewEventParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub review_id: String,
|
|
pub target_item_id: Option<String>,
|
|
pub approval_request_source: GuardianApprovalRequestSource,
|
|
pub reviewed_action: GuardianReviewedAction,
|
|
pub reviewed_action_truncated: bool,
|
|
pub decision: GuardianReviewDecision,
|
|
pub terminal_status: GuardianReviewTerminalStatus,
|
|
pub failure_reason: Option<GuardianReviewFailureReason>,
|
|
pub risk_level: Option<GuardianRiskLevel>,
|
|
pub user_authorization: Option<GuardianUserAuthorization>,
|
|
pub outcome: Option<GuardianAssessmentOutcome>,
|
|
pub guardian_thread_id: Option<String>,
|
|
pub guardian_session_kind: Option<GuardianReviewSessionKind>,
|
|
pub guardian_model: Option<String>,
|
|
pub guardian_reasoning_effort: Option<String>,
|
|
pub had_prior_review_context: Option<bool>,
|
|
pub review_timeout_ms: u64,
|
|
pub tool_call_count: Option<u64>,
|
|
pub time_to_first_token_ms: Option<u64>,
|
|
pub completion_latency_ms: Option<u64>,
|
|
pub started_at: u64,
|
|
pub completed_at: Option<u64>,
|
|
pub input_tokens: Option<i64>,
|
|
pub cached_input_tokens: Option<i64>,
|
|
pub output_tokens: Option<i64>,
|
|
pub reasoning_output_tokens: Option<i64>,
|
|
pub total_tokens: Option<i64>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct GuardianReviewEventPayload {
|
|
pub(crate) app_server_client: CodexAppServerClientMetadata,
|
|
pub(crate) runtime: CodexRuntimeMetadata,
|
|
#[serde(flatten)]
|
|
pub(crate) guardian_review: GuardianReviewEventParams,
|
|
}
|
|
|
|
#[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 CodexHookRunMetadata {
|
|
pub(crate) thread_id: Option<String>,
|
|
pub(crate) turn_id: Option<String>,
|
|
pub(crate) model_slug: Option<String>,
|
|
pub(crate) hook_name: Option<String>,
|
|
pub(crate) hook_source: Option<&'static str>,
|
|
pub(crate) status: Option<HookRunStatus>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexHookRunEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexHookRunMetadata,
|
|
}
|
|
|
|
#[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: CompactionTrigger,
|
|
pub(crate) reason: CompactionReason,
|
|
pub(crate) implementation: CompactionImplementation,
|
|
pub(crate) phase: CompactionPhase,
|
|
pub(crate) strategy: CompactionStrategy,
|
|
pub(crate) status: 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 CodexTurnEventParams {
|
|
pub(crate) thread_id: String,
|
|
pub(crate) turn_id: String,
|
|
// TODO(rhan-oai): Populate once queued/default submission type is plumbed from
|
|
// the turn/start callsites instead of always being reported as None.
|
|
pub(crate) submission_type: Option<TurnSubmissionType>,
|
|
pub(crate) app_server_client: CodexAppServerClientMetadata,
|
|
pub(crate) runtime: CodexRuntimeMetadata,
|
|
pub(crate) ephemeral: bool,
|
|
pub(crate) thread_source: Option<String>,
|
|
pub(crate) initialization_mode: ThreadInitializationMode,
|
|
pub(crate) subagent_source: Option<String>,
|
|
pub(crate) parent_thread_id: Option<String>,
|
|
pub(crate) model: Option<String>,
|
|
pub(crate) model_provider: String,
|
|
pub(crate) sandbox_policy: Option<&'static str>,
|
|
pub(crate) reasoning_effort: Option<String>,
|
|
pub(crate) reasoning_summary: Option<String>,
|
|
pub(crate) service_tier: String,
|
|
pub(crate) approval_policy: String,
|
|
pub(crate) approvals_reviewer: String,
|
|
pub(crate) sandbox_network_access: bool,
|
|
pub(crate) collaboration_mode: Option<&'static str>,
|
|
pub(crate) personality: Option<String>,
|
|
pub(crate) num_input_images: usize,
|
|
pub(crate) is_first_turn: bool,
|
|
pub(crate) status: Option<TurnStatus>,
|
|
pub(crate) turn_error: Option<CodexErrorInfo>,
|
|
pub(crate) steer_count: Option<usize>,
|
|
// TODO(rhan-oai): Populate these once tool-call accounting is emitted from
|
|
// core; the schema is reserved but these fields are currently always None.
|
|
pub(crate) total_tool_call_count: Option<usize>,
|
|
pub(crate) shell_command_count: Option<usize>,
|
|
pub(crate) file_change_count: Option<usize>,
|
|
pub(crate) mcp_tool_call_count: Option<usize>,
|
|
pub(crate) dynamic_tool_call_count: Option<usize>,
|
|
pub(crate) subagent_tool_call_count: Option<usize>,
|
|
pub(crate) web_search_count: Option<usize>,
|
|
pub(crate) image_generation_count: Option<usize>,
|
|
pub(crate) input_tokens: Option<i64>,
|
|
pub(crate) cached_input_tokens: Option<i64>,
|
|
pub(crate) output_tokens: Option<i64>,
|
|
pub(crate) reasoning_output_tokens: Option<i64>,
|
|
pub(crate) total_tokens: Option<i64>,
|
|
pub(crate) duration_ms: Option<u64>,
|
|
pub(crate) started_at: Option<u64>,
|
|
pub(crate) completed_at: Option<u64>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexTurnEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexTurnEventParams,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexTurnSteerEventParams {
|
|
pub(crate) thread_id: String,
|
|
pub(crate) expected_turn_id: Option<String>,
|
|
pub(crate) accepted_turn_id: Option<String>,
|
|
pub(crate) app_server_client: CodexAppServerClientMetadata,
|
|
pub(crate) runtime: CodexRuntimeMetadata,
|
|
pub(crate) thread_source: Option<String>,
|
|
pub(crate) subagent_source: Option<String>,
|
|
pub(crate) parent_thread_id: Option<String>,
|
|
pub(crate) num_input_images: usize,
|
|
pub(crate) result: TurnSteerResult,
|
|
pub(crate) rejection_reason: Option<TurnSteerRejectionReason>,
|
|
pub(crate) created_at: u64,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
pub(crate) struct CodexTurnSteerEventRequest {
|
|
pub(crate) event_type: &'static str,
|
|
pub(crate) event_params: CodexTurnSteerEventParams,
|
|
}
|
|
|
|
#[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 codex_hook_run_metadata(
|
|
tracking: &TrackEventsContext,
|
|
hook: HookRunFact,
|
|
) -> CodexHookRunMetadata {
|
|
CodexHookRunMetadata {
|
|
thread_id: Some(tracking.thread_id.clone()),
|
|
turn_id: Some(tracking.turn_id.clone()),
|
|
model_slug: Some(tracking.model_slug.clone()),
|
|
hook_name: Some(analytics_hook_event_name(hook.event_name).to_owned()),
|
|
hook_source: Some(analytics_hook_source(hook.hook_source)),
|
|
status: Some(analytics_hook_status(hook.status)),
|
|
}
|
|
}
|
|
|
|
fn analytics_hook_event_name(event_name: HookEventName) -> &'static str {
|
|
match event_name {
|
|
HookEventName::PreToolUse => "PreToolUse",
|
|
HookEventName::PermissionRequest => "PermissionRequest",
|
|
HookEventName::PostToolUse => "PostToolUse",
|
|
HookEventName::SessionStart => "SessionStart",
|
|
HookEventName::UserPromptSubmit => "UserPromptSubmit",
|
|
HookEventName::Stop => "Stop",
|
|
}
|
|
}
|
|
|
|
fn analytics_hook_source(source: HookSource) -> &'static str {
|
|
match source {
|
|
HookSource::System => "system",
|
|
HookSource::User => "user",
|
|
HookSource::Project => "project",
|
|
HookSource::Mdm => "mdm",
|
|
HookSource::SessionFlags => "session_flags",
|
|
HookSource::LegacyManagedConfigFile => "legacy_managed_config_file",
|
|
HookSource::LegacyManagedConfigMdm => "legacy_managed_config_mdm",
|
|
HookSource::Unknown => "unknown",
|
|
}
|
|
}
|
|
|
|
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,
|
|
}
|
|
}
|
|
|
|
fn analytics_hook_status(status: HookRunStatus) -> HookRunStatus {
|
|
match status {
|
|
// Running is unexpected here and normalized defensively.
|
|
HookRunStatus::Running => HookRunStatus::Failed,
|
|
other => other,
|
|
}
|
|
}
|