Compare commits

..

1 Commits

Author SHA1 Message Date
rhan-oai
6ac57418f4 [codex-analytics] add tool call event schema 2026-04-08 14:20:15 -07:00
2 changed files with 164 additions and 0 deletions

View File

@@ -6,9 +6,14 @@ use crate::events::CodexAppUsedEventRequest;
use crate::events::CodexPluginEventRequest;
use crate::events::CodexPluginUsedEventRequest;
use crate::events::CodexRuntimeMetadata;
use crate::events::CodexToolCallEventParams;
use crate::events::CodexToolCallEventRequest;
use crate::events::ThreadInitializationMode;
use crate::events::ThreadInitializedEvent;
use crate::events::ThreadInitializedEventParams;
use crate::events::ToolCallFinalReviewOutcome;
use crate::events::ToolCallTerminalStatus;
use crate::events::ToolKind;
use crate::events::TrackEventRequest;
use crate::events::codex_app_metadata;
use crate::events::codex_plugin_metadata;
@@ -346,6 +351,89 @@ fn thread_initialized_event_serializes_expected_shape() {
);
}
#[test]
fn tool_call_event_serializes_expected_shape() {
let event = TrackEventRequest::ToolCall(CodexToolCallEventRequest {
event_type: "codex_tool_call_event",
event_params: CodexToolCallEventParams {
thread_id: "thread-1".to_string(),
turn_id: "turn-1".to_string(),
tool_call_id: "tool-call-1".to_string(),
app_server_client: CodexAppServerClientMetadata {
product_client_id: "codex_tui".to_string(),
client_name: Some("codex-tui".to_string()),
client_version: Some("1.2.3".to_string()),
rpc_transport: AppServerRpcTransport::Websocket,
experimental_api_enabled: Some(true),
},
runtime: CodexRuntimeMetadata {
codex_rs_version: "0.99.0".to_string(),
runtime_os: "macos".to_string(),
runtime_os_version: "15.3.1".to_string(),
runtime_arch: "aarch64".to_string(),
},
tool_name: "shell".to_string(),
tool_kind: ToolKind::Shell,
started_at: 123,
completed_at: Some(125),
duration_ms: Some(2000),
execution_started: true,
review_count: 0,
guardian_review_count: 0,
user_review_count: 0,
final_review_outcome: ToolCallFinalReviewOutcome::NotNeeded,
terminal_status: ToolCallTerminalStatus::Completed,
failure_kind: None,
exit_code: Some(0),
requested_additional_permissions: false,
requested_network_access: false,
retry_count: 0,
},
});
let payload = serde_json::to_value(&event).expect("serialize tool call event");
assert_eq!(
payload,
json!({
"event_type": "codex_tool_call_event",
"event_params": {
"thread_id": "thread-1",
"turn_id": "turn-1",
"tool_call_id": "tool-call-1",
"app_server_client": {
"product_client_id": "codex_tui",
"client_name": "codex-tui",
"client_version": "1.2.3",
"rpc_transport": "websocket",
"experimental_api_enabled": true
},
"runtime": {
"codex_rs_version": "0.99.0",
"runtime_os": "macos",
"runtime_os_version": "15.3.1",
"runtime_arch": "aarch64"
},
"tool_name": "shell",
"tool_kind": "shell",
"started_at": 123,
"completed_at": 125,
"duration_ms": 2000,
"execution_started": true,
"review_count": 0,
"guardian_review_count": 0,
"user_review_count": 0,
"final_review_outcome": "not_needed",
"terminal_status": "completed",
"failure_kind": null,
"exit_code": 0,
"requested_additional_permissions": false,
"requested_network_access": false,
"retry_count": 0
}
})
);
}
#[tokio::test]
async fn initialize_caches_client_and_thread_lifecycle_publishes_once_initialized() {
let mut reducer = AnalyticsReducer::default();

View File

@@ -37,6 +37,7 @@ pub(crate) enum TrackEventRequest {
ThreadInitialized(ThreadInitializedEvent),
AppMentioned(CodexAppMentionedEventRequest),
AppUsed(CodexAppUsedEventRequest),
ToolCall(CodexToolCallEventRequest),
PluginUsed(CodexPluginUsedEventRequest),
PluginInstalled(CodexPluginEventRequest),
PluginUninstalled(CodexPluginEventRequest),
@@ -99,6 +100,81 @@ pub(crate) struct ThreadInitializedEvent {
pub(crate) event_params: ThreadInitializedEventParams,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ToolKind {
Shell,
UnifiedExec,
ApplyPatch,
Mcp,
Dynamic,
Other,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ToolCallFinalReviewOutcome {
NotNeeded,
GuardianApproved,
GuardianDenied,
GuardianAborted,
UserApproved,
UserApprovedForSession,
UserDenied,
UserAborted,
ConfigAllowed,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ToolCallTerminalStatus {
Completed,
Failed,
Rejected,
Interrupted,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ToolCallFailureKind {
ToolError,
ApprovalDenied,
ApprovalAborted,
SandboxDenied,
PolicyForbidden,
}
#[derive(Serialize)]
pub(crate) struct CodexToolCallEventParams {
pub(crate) thread_id: String,
pub(crate) turn_id: String,
pub(crate) tool_call_id: String,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
pub(crate) tool_name: String,
pub(crate) tool_kind: ToolKind,
pub(crate) started_at: u64,
pub(crate) completed_at: Option<u64>,
pub(crate) duration_ms: Option<u64>,
pub(crate) execution_started: bool,
pub(crate) review_count: u64,
pub(crate) guardian_review_count: u64,
pub(crate) user_review_count: u64,
pub(crate) final_review_outcome: ToolCallFinalReviewOutcome,
pub(crate) terminal_status: ToolCallTerminalStatus,
pub(crate) failure_kind: Option<ToolCallFailureKind>,
pub(crate) exit_code: Option<i32>,
pub(crate) requested_additional_permissions: bool,
pub(crate) requested_network_access: bool,
pub(crate) retry_count: u64,
}
#[derive(Serialize)]
pub(crate) struct CodexToolCallEventRequest {
pub(crate) event_type: &'static str,
pub(crate) event_params: CodexToolCallEventParams,
}
#[derive(Serialize)]
pub(crate) struct CodexAppMetadata {
pub(crate) connector_id: Option<String>,