Event Schema
Every interceptor writes the same event format, so detection, the dashboards, and your own tools can treat events from Claude Code, Codex, Cowork, and Antigravity the same way.
Common fields
Section titled “Common fields”Every event has these fields:
interface BaseEvent { schema_version: 1; // Event format version event_id: string; // Unique event ID session_id: string; // Session the event belongs to occurred_at: string; // When it happened (ISO 8601) recorded_at: string; // When Omnodex recorded it (ISO 8601) interceptor: Interceptor; // What captured it event_type: EventType; // What kind of event it is platform?: string; // Agent runtime, when known (for example "codex") agent_id?: string; // The subagent the event happened in, when it ran inside one}interceptor
Section titled “interceptor”| Value | Source |
|---|---|
claude-code-hook | Claude Code hooks |
codex-hook | Codex hooks |
antigravity-hook | Antigravity hooks |
mcp-proxy | The Omnodex MCP proxy (Cowork, Codex MCP, Antigravity MCP, and any other MCP client) |
analyzer | Risk detection |
mock | omnodex spike test sessions |
Event types
Section titled “Event types”session.started
Section titled “session.started”{ event_type: "session.started"; user: string; // OS user running the agent or proxy project_path: string; // Working directory of the session project_remote?: string; // Its git origin remote as host/owner/repo, without credentials mcp_servers: string[]; // Upstream servers (proxy sessions) model?: string; // The model the platform reported for the first turn, when it says}session.ended
Section titled “session.ended”{ event_type: "session.ended"; duration_ms: number; status: "completed" | "errored" | "interrupted";}session.renamed
Section titled “session.renamed”Written when a session’s title changes: you renamed it, or the agent named it. The latest one is the session’s title. Claude Code and Codex hooks write it; see Session titles for where each title comes from.
{ event_type: "session.renamed"; title: string;}session.model_changed
Section titled “session.model_changed”Written when the model a session uses changes, or is first reported after the session started. The latest one, or session.started if there is none, names the session’s current model. The name is recorded as the platform reports it, never checked. See Which model a session used for which agents report it.
{ event_type: "session.model_changed"; model: string; previous?: string; // The model recorded before; absent the first time source: "hook" | "transcript"; // Read from the hook input or the agent's transcript}tool.invoked
Section titled “tool.invoked”Written when a tool call starts.
{ event_type: "tool.invoked"; tool_call_id: string; // Links to the matching tool.completed tool_name: string; // For example "Bash", "Edit", or "filesystem__read_file" mcp_server: string; // Owning MCP server, or "builtin" for the agent's own tools parameters: Record<string, unknown>; // Values may be "[REDACTED]" if redaction is on cwd?: string; // Working directory, when the agent reports it}tool.completed
Section titled “tool.completed”Written when a tool call finishes. Tool output is not stored, only its size.
{ event_type: "tool.completed"; tool_call_id: string; duration_ms: number; status: "success" | "error"; response_bytes: number; error_message?: string;}file.read and file.written
Section titled “file.read and file.written”Written for file reads and writes the interceptor can identify, currently Claude Code’s file tools.
{ event_type: "file.read" | "file.written"; path: string; // File path, or the search pattern for Grep and Glob bytes: number;}prompt.submitted
Section titled “prompt.submitted”Written when you submit a prompt. The full text is kept, as tool parameters are. Claude Code hooks write it.
{ event_type: "prompt.submitted"; prompt: string; prompt_id?: string; // The agent's own id for the turn, when it sends one}subagent.started and subagent.stopped
Section titled “subagent.started and subagent.stopped”Written when the agent starts a subagent and when it finishes. On these two events agent_id names the subagent itself; the tool calls it makes carry the same agent_id. The subagent’s final message is not stored, only its size.
{ event_type: "subagent.started" | "subagent.stopped"; agent_id: string; agent_type?: string; // For example "Explore" duration_ms?: number; // subagent.stopped only status?: "completed" | "errored" | "interrupted"; // subagent.stopped only response_bytes?: number; // subagent.stopped only}permission.requested and permission.denied
Section titled “permission.requested and permission.denied”Written when the agent asks you to allow a tool call, and when a tool call is refused. Whether a call asks depends on your permission settings, so not every tool call has one.
{ event_type: "permission.requested" | "permission.denied"; tool_name: string; mcp_server: string; // As on tool.invoked parameters: Record<string, unknown>; tool_call_id?: string; // permission.denied, when the agent sends it reason?: string; // permission.denied only}server.observed
Section titled “server.observed”Written by the MCP proxy each time an upstream server lists its tools: when it connects, and again after it announces a change mid-session.
{ event_type: "server.observed"; mcp_server: string; // the server's configured name transport: "stdio" | "http"; trigger: "connect" | "list_changed"; config_fingerprint: string; // command, arguments and working directory, or URL, // plus environment and header names, never values reported?: { name: string; version?: string }; // from the MCP handshake installed_version?: string; // npx package version from the npx cache, when readable tool_list_fingerprint: string; tools: { name: string; fingerprint: string }[]; // hash of name, description and input schema}Fingerprints are SHA-256 hashes of canonical JSON, cut to 32 hex characters. The event carries no tool descriptions.
risk.detected
Section titled “risk.detected”Written by detection when a rule matches.
{ event_type: "risk.detected"; severity: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL"; category: string; description: string; related_event_id: string; // tool_call_id of the call that triggered the rule, // or the event_id of a server.observed event for a drift finding rule_id: string; enforcement?: { // shadow and guardrails modes only mode: "shadow" | "guardrails"; candidate: "deny" | "confirm" | "alert_only"; // what the rule itself suggests action: "deny" | "confirm" | "alert_only"; // what your settings make of it held: boolean; // the call was stopped or held (guardrails) source: string; // which setting decided, such as "user-rule" muted?: boolean; };}risk.enforcement_error
Section titled “risk.enforcement_error”Written in guardrails mode when Omnodex could not check a call (its rules failed to load, failed, or took too long). Carries no captured content.
{ event_type: "risk.enforcement_error"; related_event_id: string; // tool_call_id of the call reason: "timeout" | "error"; outcome: "allowed" | "blocked"; // blocked only with on_error: "block"}Storage format
Section titled “Storage format”Events are stored as JSON Lines under the Omnodex home, one file per session:
$OMNODEX_HOME/event-log/ index.jsonl one line per session: session_id, registered_at, file sessions/<session_id>.jsonlEach line is one complete event:
{"schema_version":1,"event_id":"7c4137d2-1d73-44f4-89d6-312c04250883","session_id":"2bf17678-7b08-4f3f-805d-5058426114b5","occurred_at":"2026-09-16T20:20:39.350Z","recorded_at":"2026-09-16T20:20:39.350Z","interceptor":"mcp-proxy","event_type":"tool.invoked","tool_call_id":"f2cc6e27-457a-4b23-9643-77af2a68a534","tool_name":"filesystem__list_directory","mcp_server":"filesystem","parameters":{"path":"/home/case/project"}}Standard tools can process the files:
# Count events across all sessionscat ~/.omnodex/event-log/sessions/*.jsonl | wc -l
# Tool calls onlycat ~/.omnodex/event-log/sessions/*.jsonl | jq -c 'select(.event_type == "tool.invoked") | {tool_name, mcp_server}'
# Shell commandscat ~/.omnodex/event-log/sessions/*.jsonl | jq -c 'select(.event_type == "tool.invoked" and .tool_name == "Bash") | .parameters'Next steps
Section titled “Next steps”- Events and Traces - How events and sessions fit together
- Configuration - Where event data is stored