# CLI Schema Field Comparison

**Last Updated**: 2026-02-04

This document compares the fields available across all three CLI schemas (Claude Code, Codex CLI, Gemini CLI) to inform the design of the unified schema.

## Legend

| Symbol | Meaning |
|--------|---------|
| ✓ | Field present |
| ❌ | Field not present |
| (name) | Field present with different name |

---

## Session-Level Metadata

Fields that describe the session context, typically set once at session start.

| Field | Claude Code | Codex CLI | Gemini CLI | Notes |
|-------|:-----------:|:---------:|:----------:|-------|
| `session_id` | ✓ | ✓ | ✓ | Universal identifier |
| `cwd` | ✓ | ✓ | ❌ | Working directory |
| `cli_version` | ✓ (`version`) | ✓ | ❌ | e.g., "2.1.31", "0.95.0" |
| `git_branch` | ✓ (`gitBranch`) | ✓ | ❌ | Current branch |
| `git_commit` | ❌ | ✓ (`commit_hash`) | ❌ | Commit SHA |
| `git_repo_url` | ❌ | ✓ (`repository_url`) | ❌ | Remote URL |
| `model_provider` | ❌ | ✓ | ❌ | e.g., "openai" |
| `project_hash` | ❌ | ❌ | ✓ | SHA-256 of project path |
| `start_time` | ✓ (`timestamp`) | ✓ | ✓ | Session start |
| `last_updated` | ❌ | ❌ | ✓ | Last activity |

### Claude Code Specific

| Field | Type | Description |
|-------|------|-------------|
| `slug` | `string` | Human-readable session name (e.g., "ethereal-giggling-crown") |
| `userType` | `string` | User type (e.g., "external") |
| `permissionMode` | `string` | Permission setting (e.g., "acceptEdits") |

### Codex CLI Specific

| Field | Type | Description |
|-------|------|-------------|
| `originator` | `string` | Client identifier (e.g., "codex_cli_rs") |
| `source` | `string` | Session source (e.g., "cli", "exec") |
| `base_instructions` | `object` | System prompt configuration |
| `dynamic_tools` | `array` | Custom tool definitions |
| `sandbox_policy` | `object` | Security sandbox configuration |
| `approval_policy` | `string` | Tool approval mode: "untrusted", "on-failure", "on-request", "never" |
| `collaboration_mode` | `object` | Multi-agent configuration |
| `truncation_policy` | `object` | Context truncation settings |

### Gemini CLI Specific

| Field | Type | Description |
|-------|------|-------------|
| `projectHash` | `string` | SHA-256 hash of project root path |
| `directories` | `string[]` | Workspace directories added via `/dir add` |
| `summary` | `string` | Optional session summary |

---

## Token Usage

Fields tracking API token consumption.

| Field | Claude Code | Codex CLI | Gemini CLI | Notes |
|-------|:-----------:|:---------:|:----------:|-------|
| `input_tokens` | ✓ | ✓ | ✓ (`input`) | Prompt tokens |
| `output_tokens` | ✓ | ✓ | ✓ (`output`) | Completion tokens |
| `cached_tokens` | ✓ (`cache_read_input_tokens`) | ✓ (`cached_input_tokens`) | ✓ (`cached`) | Cache hits |
| `thinking_tokens` | ❌ | ✓ (`reasoning_output_tokens`) | ✓ (`thoughts`) | Reasoning tokens |
| `total_tokens` | ❌ | ✓ | ✓ (`total`) | Sum of all tokens |
| `tool_tokens` | ❌ | ❌ | ✓ (`tool`) | Tool use tokens |

### Claude Code Specific

| Field | Type | Description |
|-------|------|-------------|
| `cache_creation_input_tokens` | `int` | Tokens used to create cache |
| `cache_creation.ephemeral_5m_input_tokens` | `int` | 5-minute ephemeral cache |
| `cache_creation.ephemeral_1h_input_tokens` | `int` | 1-hour ephemeral cache |
| `service_tier` | `string` | API tier (e.g., "standard") |

### Codex CLI Specific

| Field | Type | Description |
|-------|------|-------------|
| `model_context_window` | `int` | Maximum context size |
| `total_token_usage` | `object` | Cumulative session tokens |
| `last_token_usage` | `object` | Most recent turn tokens |

---

## Message Threading

Fields for tracking conversation structure.

| Field | Claude Code | Codex CLI | Gemini CLI | Notes |
|-------|:-----------:|:---------:|:----------:|-------|
| `message_id` | ✓ (`uuid`) | ✓ (in payload) | ✓ (`id`) | Unique message ID |
| `parent_id` | ✓ (`parentUuid`) | ❌ | ❌ | Parent message reference |
| `is_sidechain` | ✓ (`isSidechain`) | ❌ | ❌ | Branch conversation flag |
| `timestamp` | ✓ | ✓ | ✓ | ISO 8601 timestamp |

---

## Message Types

Core message type support.

| Type | Claude Code | Codex CLI | Gemini CLI | Notes |
|------|:-----------:|:---------:|:----------:|-------|
| `user` | ✓ | ✓ | ✓ | User input |
| `assistant` | ✓ | ✓ | ✓ (`gemini`) | Model response |
| `system` | ✓ | ✓ (`developer`) | ❌ | System prompt |
| `tool_call` | ✓ (`tool_use`) | ✓ (`function_call`) | ✓ (in `toolCalls`) | Tool invocation |
| `tool_result` | ✓ | ✓ (`function_call_output`) | ✓ (in `toolCalls`) | Tool response |
| `error` | ❌ | ✓ | ✓ | Error message |
| `warning` | ❌ | ✓ | ✓ | Warning message |
| `info` | ❌ | ✓ | ✓ | Info message |

---

## Thinking / Reasoning

Fields for model reasoning visibility.

| Field | Claude Code | Codex CLI | Gemini CLI | Notes |
|-------|:-----------:|:---------:|:----------:|-------|
| `thinking_text` | ✓ (`thinking`) | ❌ (encrypted) | ✓ (`description`) | Reasoning content |
| `thinking_subject` | ❌ | ❌ | ✓ (`subject`) | Reasoning header |
| `thinking_signature` | ✓ (`signature`) | ❌ | ❌ | Cryptographic verification |
| `thinking_timestamp` | ❌ | ❌ | ✓ | Per-thought timestamp |
| `encrypted_content` | ❌ | ✓ | ❌ | Encrypted reasoning |
| `max_thinking_tokens` | ✓ (`thinkingMetadata.maxThinkingTokens`) | ❌ | ❌ | Token budget |

---

## Tool Calls

Fields for tool/function invocations.

| Field | Claude Code | Codex CLI | Gemini CLI | Notes |
|-------|:-----------:|:---------:|:----------:|-------|
| `tool_id` | ✓ (`id`) | ✓ (`call_id`) | ✓ (`id`) | Call identifier |
| `tool_name` | ✓ (`name`) | ✓ (`name`) | ✓ (`name`) | Tool name |
| `tool_input` | ✓ (`input`) | ✓ (`arguments` - JSON string) | ✓ (`args`) | Tool arguments |
| `display_name` | ❌ | ❌ | ✓ (`displayName`) | UI-friendly name |
| `description` | ❌ | ❌ | ✓ | Tool description |

---

## Tool Results

Fields for tool execution results.

| Field | Claude Code | Codex CLI | Gemini CLI | Notes |
|-------|:-----------:|:---------:|:----------:|-------|
| `call_id` | ✓ (`tool_use_id`) | ✓ | ✓ (`id`) | Reference to tool call |
| `output` | ✓ (`content`) | ✓ | ✓ | Result content |
| `status` | ✓ (`is_error`) | ✓ (inferred) | ✓ | success/error/cancelled |
| `duration_ms` | ✓ (`durationMs`) | ✓ (`duration_seconds`) | ❌ | Execution time |

### Gemini CLI Specific (ToolResultDisplay)

| Field | Type | Description |
|-------|------|-------------|
| `resultDisplay` | `string \| FileDiff \| AnsiOutput \| TodoList` | Rich result display |
| `renderOutputAsMarkdown` | `boolean` | Markdown rendering flag |

#### FileDiff Structure

| Field | Type | Description |
|-------|------|-------------|
| `fileName` | `string` | File name |
| `filePath` | `string` | Full path |
| `isNewFile` | `boolean` | New file flag |
| `originalContent` | `string \| null` | Content before edit |
| `newContent` | `string` | Content after edit |
| `fileDiff` | `string` | Unified diff format |
| `diffStat` | `DiffStat` | Change statistics |

#### DiffStat Structure

| Field | Type | Description |
|-------|------|-------------|
| `model_added_lines` | `int` | Lines added by model |
| `model_removed_lines` | `int` | Lines removed by model |
| `model_added_chars` | `int` | Characters added by model |
| `model_removed_chars` | `int` | Characters removed by model |
| `user_added_lines` | `int` | Lines added by user |
| `user_removed_lines` | `int` | Lines removed by user |
| `user_added_chars` | `int` | Characters added by user |
| `user_removed_chars` | `int` | Characters removed by user |

#### AnsiOutput Structure

Terminal-formatted output with styling.

```
AnsiOutput = AnsiLine[]
AnsiLine = AnsiToken[]

AnsiToken {
  text: string
  bold?: boolean
  italic?: boolean
  underline?: boolean
  dim?: boolean
  inverse?: boolean
  fg?: string  // Foreground color
  bg?: string  // Background color
}
```

---

## Rate Limits (Codex CLI Only)

| Field | Type | Description |
|-------|------|-------------|
| `rate_limits.primary` | `RateLimitSnapshot` | Primary rate limit |
| `rate_limits.secondary` | `RateLimitSnapshot` | Secondary rate limit |
| `rate_limits.credits` | `CreditsSnapshot` | Credit balance |
| `rate_limits.plan_type` | `string` | API plan type |

### RateLimitSnapshot

| Field | Type | Description |
|-------|------|-------------|
| `limit` | `int` | Maximum allowed |
| `remaining` | `int` | Remaining quota |
| `reset_at` | `string` | Reset timestamp |
| `window` | `string` | Time window |

---

## Observability Events

Events beyond core conversation flow.

| Event Type | Claude Code | Codex CLI | Gemini CLI | Notes |
|------------|:-----------:|:---------:|:----------:|-------|
| Progress (hooks) | ✓ | ❌ | ❌ | Hook execution progress |
| Progress (bash) | ✓ | ✓ (`exec_command_*`) | ❌ | Command output streaming |
| Turn duration | ✓ | ✓ (`turn_complete`) | ❌ | Turn timing |
| Turn aborted | ❌ | ✓ | ❌ | Interrupted turns |
| File snapshot | ✓ | ✓ (`ghost_snapshot`) | ❌ | File state capture |
| Session summary | ✓ | ❌ | ✓ | Conversation summary |
| Queue operations | ✓ | ❌ | ❌ | Background task tracking |
| MCP tool calls | ❌ | ✓ | ❌ | MCP server tools |
| Web search | ❌ | ✓ | ❌ | Web search events |
| Patch apply | ❌ | ✓ | ❌ | File patch events |
| Context compacted | ❌ | ✓ | ❌ | Context truncation |

---

## Summary: Common Fields

Fields present in **all three CLIs** (candidates for unified schema core):

### Always Present
- `session_id`
- `message_id`
- `timestamp`
- `message_type` (user/assistant/tool_call/tool_result)
- `content`
- `input_tokens`
- `output_tokens`
- `cached_tokens`
- `tool_call.id`
- `tool_call.name`
- `tool_call.input`
- `tool_result.call_id`
- `tool_result.output`
- `tool_result.status`

### Present in 2 of 3
- `cwd` (Claude, Codex)
- `cli_version` (Claude, Codex)
- `git_branch` (Claude, Codex)
- `thinking_tokens` (Codex, Gemini)
- `total_tokens` (Codex, Gemini)
- `thinking_text` (Claude, Gemini)
- `error` message type (Codex, Gemini)
- `info` message type (Codex, Gemini)

---

## Recommendations for Unified Schema

### Core (NormalizedMessage)
Keep focused on universal conversation fields:
- Identity: `id`, `session_id`, `source`, `timestamp`
- Content: `content`, `msg_type`
- Tokens: `input`, `output`, `cached`, `thinking`
- Tools: `tool_call`, `tool_result`
- Model: `model`, `thinking`

### Session Metadata (SessionMeta)
Separate structure for session-level context:
- Common: `session_id`, `start_time`, `cwd`, `cli_version`, `git_branch`
- CLI-specific: `cli_meta` field with typed variants

### CLI-Specific Details
Keep in source schemas, accessible via:
- `raw_data` field on NormalizedMessage
- Direct access to v1.py typed schemas
