Comparing data across tools
Goal: Compare the data available in the JSON/transcripts of each tool supported by ai-footprint.
Update: This document is updated every time a new tool is added.
Legend
| Symbol | Meaning |
|---|---|
| ✅ | Available in this tool |
| ❌ | Not available |
| ⚠️ | Partially available (different structure) |
Comparison table
| Field | Claude Code (JSONL) | Opencode (JSON) | Pi (JSONL) | Usage in ai-footprint |
|---|---|---|---|---|
| Identifiers | ||||
sessionId |
✅ | ✅ session_id |
✅ header type:"session", id |
Join key with the sessions table |
uuid / id |
✅ msg_id |
✅ message.id |
✅ message.id (entry type:"message") |
Unique key per message |
| Model | ||||
model (string) |
✅ "claude-opus-4-8" |
⚠️ {providerID, modelID} |
✅ message.model |
Normalized to provider + model in InferenceEvent |
provider |
❌ (inferred from model) |
✅ model.providerID |
✅ message.provider |
Allows the EcoLogits lookup |
| Tokens | ||||
input_tokens |
✅ | ✅ tokens.input |
✅ message.usage.input |
Impact calculation |
output_tokens |
✅ | ✅ tokens.output |
✅ message.usage.output |
Impact calculation |
cache_creation_input_tokens |
✅ | ⚠️ tokens.cache.write |
✅ message.usage.cacheWrite |
Impact calculation (cache) |
cache_read_input_tokens |
✅ | ⚠️ tokens.cache.read |
✅ message.usage.cacheRead |
Impact calculation (cache) |
reasoning_tokens |
❌ | ✅ tokens.reasoning |
❌ | To be validated with EcoLogits |
| Time | ||||
timestamp (ISO 8601) |
✅ | ⚠️ Unix timestamp ms | ✅ native ISO 8601 | Converted to ISO 8601 UTC |
active_seconds (delta) |
✅ Computed | ✅ time.completed - time.created |
✅ Computed (delta between consecutive timestamps) | Active time measurement |
| Cost | ||||
cost (USD) |
❌ (not read by the collector) | ✅ cost (DB + exports) |
⚠️ message.usage.cost.total (present but not stored) |
Not stored — no cost field in InferenceEvent |
latency (ms) |
❌ (not read by the collector) | ❌ | ❌ | Not currently used |
| Session metadata | ||||
title |
❌ | ✅ session.title |
❌ | Not stored (note: to be reconsidered) |
directory |
✅ cwd |
✅ session.directory |
✅ session.cwd |
Used for project (basename) |
slug |
❌ | ✅ session.slug |
❌ | Not stored (note: to be reconsidered) |
path |
❌ | ✅ session.path |
❌ | Not stored (note: to be reconsidered) |
| Status | ||||
role (user/assistant) |
✅ | ✅ | ✅ message.role |
Filtering (only assistant messages count) |
error |
❌ | ✅ error.name, error.data |
❌ | Not stored — no error field in InferenceEvent |
archived |
❌ | ✅ time_archived |
❌ | Not currently used |
| Collection | ||||
| Storage format | JSONL (line by line) | SQLite (local DB) | JSONL (line by line) | Tool-specific collectors |
| Trigger | hooks.Stop (end of session) |
session.idle (plugin) |
session_shutdown (extension) |
Automatic installation |
| Backfilling | Direct JSONL read | Direct SQLite read | Direct JSONL read | Tool-specific backfilling scripts |
Details per tool
Claude Code
Available sources:
- Session transcripts:
~/.claude/projects/**/*.jsonl— the only source actually read byClaudeCodeCollector(glob + parsing of all**/*.jsonlfiles).
Structure of an event (transcript):
{
"type": "assistant",
"message": {
"model": "claude-opus-4-8",
"usage": {
"input_tokens": 8427,
"output_tokens": 287,
"cache_creation_input_tokens": 7052,
"cache_read_input_tokens": 8020
}
},
"sessionId": "sess-A",
"uuid": "u1",
"cwd": "/Users/me/DEV/projA",
"timestamp": "2026-06-27T10:08:45.619Z"
}
Collector: ClaudeCodeCollector (direct JSONL read)
Opencode
Available sources:
- Local DB:
~/.local/share/opencode/opencode.db(SQLite) - Plugin exports:
~/.ai-footprint/crush-exports/<sessionId>.json - SSE SDK:
event.subscribe()(real time)
Structure of a session (session table):
{
"id": "ses_120fa4dc7ffeSg6YofUeAPBX1P",
"title": "New session - 2026-06-19T08:35:53.016Z",
"directory": "/Users/renaudheluin/.agents",
"model": "{\"id\":\"Qwen3.6-35B-A3B-4bit\",\"providerID\":\"myprovider\"}",
"tokens_input": 145920,
"tokens_output": 11950,
"tokens_reasoning": 0,
"tokens_cache_read": 0,
"tokens_cache_write": 0,
"time_created": 1781858206763,
"time_updated": 1781858206763
}
Structure of a message (message table):
{
"id": "msg_edf05b24e001gD1yQMa8Rqwfpa",
"session_id": "ses_120fa4dc7ffeSg6YofUeAPBX1P",
"data": "{\"role\":\"assistant\",\"time\":{\"created\":1781858153054,\"completed\":1781858153428},\"model\":{\"providerID\":\"myprovider\",\"modelID\":\"Qwen3.6-35B-A3B-4bit\"},\"tokens\":{\"input\":0,\"output\":0,\"reasoning\":0,\"cache\":{\"read\":0,\"write\":0}},\"cost\":0}"
}
Structure of a plugin export:
{
"info": {
"id": "sess-abc123",
"slug": "mon-projet",
"directory": "/home/user/project",
"model": { "id": "claude-sonnet-4-20250514", "providerID": "anthropic" },
"tokens": {
"input": 8427,
"output": 287,
"reasoning": 0,
"cache": { "read": 8020, "write": 7052 }
},
"time": { "created": 1719741600000, "updated": 1719742500000 }
},
"messages": [
{
"info": {
"id": "msg-1",
"role": "assistant",
"time": { "created": 1719741700000, "completed": 1719741800000 },
"model": {
"id": "claude-sonnet-4-20250514",
"providerID": "anthropic"
},
"tokens": {
"input": 8427,
"output": 287,
"reasoning": 0,
"cache": { "read": 8020, "write": 7052 }
},
"cost": 0.00123
}
}
]
}
Collector: CrushCollector (DB backfilling + plugin export reading)
Pi
Available sources:
- Session transcripts:
~/.pi/agent/sessions/--<cwd>--/<timestamp>_<uuid>.jsonl
Structure of a session file (one type:"session" header entry, then
type:"message" entries):
{"type":"session","id":"sess-A","timestamp":"2026-06-27T10:00:00.000Z","cwd":"/Users/me/DEV/projA"}
{"type":"message","id":"u1","timestamp":"2026-06-27T10:08:45.619Z","message":{"role":"assistant","provider":"anthropic","model":"claude-opus-4-8","usage":{"input":8427,"output":287,"cacheRead":8020,"cacheWrite":7052,"totalTokens":15786,"cost":{"total":0}}}}
Collector: PiCollector (direct JSONL read, root = directory or single
file)
Notes for future tools
When a new tool is added, update:
- This document (comparison table)
- The corresponding collector in
ai_footprint/collectors/ - The tests in
tests/test_<tool>_collector.py
Required fields to map into InferenceEvent:
provider(string)model(string)input_tokens(int)output_tokens(int)cache_creation_tokens(int)cache_read_tokens(int)timestamp(ISO 8601 UTC)project(string, basename of the directory)session_id(string)msg_id(string)
Optional fields already mapped in InferenceEvent:
active_seconds(float)client(string, identifies the tool)
Fields not yet mapped (present in at least one tool, absent from
InferenceEvent — to be added if a product need arises):
reasoning_tokens(int)cost(float, USD)error(string)
Document created on 2026-06-30, updated on 2026-07-13. To be updated with each new tool added.