Where Claude Code and Codex keep their usage logs, and what a token count is worth
Claude Code and Codex keep local session records that include usage counters. It is not hidden and it is not encrypted; it is a directory of newline-delimited JSON in your home folder that the agent needs for resuming sessions. Everything a cost dashboard knows about local usage starts there, so it is worth knowing what is in those files, what is not, and how to turn a token count into a dollar figure that means something.
Claude Code: ~/.claude/projects/
Claude Code keeps one subdirectory per working directory it has been started in, named after the path with the slashes replaced, and inside it one .jsonl file per session, named by the session's UUID. Every line is one record: a user message, an assistant message, a tool result, a summary. The records that matter for cost are the assistant ones, and each of those carries the model that answered and the usage block the API returned for that call:
{
"type": "assistant",
"sessionId": "5b1c…",
"cwd": "/Users/alice/work/acme-api",
"timestamp": "2026-08-27T09:14:02.511Z",
"message": {
"model": "claude-sonnet-5",
"usage": {
"input_tokens": 412,
"cache_creation_input_tokens": 18230,
"cache_read_input_tokens": 96114,
"output_tokens": 655
},
"content": [ …the reply, and any tool calls… ]
}
}
Three things to notice. The usage is per API call, not per session, so summing the turns of a session counts the context window again on every turn. That is correct: it is what the vendor charges for. The cwd is a full path, which is how a tool can attribute tokens to a repository and also how it can leak a username and a client's name if it is careless. And the content is right there beside the numbers, which is why a collector's promise to not read it has to be a property of its code rather than its intentions.
Claude Code deletes transcripts older than a retention window (cleanupPeriodDays in its settings, 30 days by default). A usage tool that only runs when someone remembers will find that the second half of last quarter is gone. Anything that reads these files for cost has to run on a schedule, and a dashboard cannot backfill a month the agent already cleaned up.
Codex: ~/.codex/sessions/
OpenAI's Codex CLI writes a rollout file per session, filed by date: ~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<id>.jsonl. The shape is the same idea, one JSON record per line, with the session's metadata first (including the working directory) and then the conversation. Token counts arrive as their own event type rather than on each message, and they are recorded as running totals for the session, so the naive sum of every count event overstates the session several times over. A correct reader takes the last total per session, or diffs consecutive events, and prices the difference. Inside a live session, /status shows the session's token usage and /usage the account's limits; the rollout file is what remains once the session ends.
Models are named in the session metadata and on the turn. Codex's cache accounting is coarser than Claude Code's, which is one reason a Codex estimate and a Claude Code estimate for the same afternoon should not be compared to the cent.
GitHub Copilot: ~/.copilot/session-state/ and the VS Code user-data directory
Copilot CLI writes one event log per session at ~/.copilot/session-state/<session-id>/events.jsonl, and its durable token counters arrive on the session.shutdown event as a cumulative per-model snapshot, the same running-total trap as Codex with one more twist: Copilot's input count includes its cache reads and writes, so those are subtracted before pricing. Copilot Chat in VS Code keeps each session under the editor's user-data directory, as a JSON snapshot in older builds and a JSONL mutation log in current ones, with whole-turn totals per model when the editor saved them and only the last model call when it did not. The Copilot log guide walks both formats field by field, and the tracking guide says what a team can do with the counts.
Other agents
Gemini CLI, OpenCode, Amp and the rest all do a version of the same thing, each in its own directory with its own field names. Parsing them is a moving target; the projects that do it well (Kibble's collector uses the open-source tokscale core for exactly this, behind an adapter so the parser can be swapped) treat every agent's format as something that will change with the next release, and measure their output against the raw transcripts rather than trusting the parser.
What a token is worth
Price token categories separately. The following ratios illustrate standard Anthropic pricing for models such as Sonnet 5; they are not universal across models or providers. Check the model and cache-write duration against the vendor rate table.
| Field | What it is | Relative rate |
|---|---|---|
| input_tokens | Fresh prompt tokens the model read for the first time | 1× |
| output_tokens | What the model wrote, including any reasoning tokens | ≈5× input |
| cache_creation_input_tokens | Prompt tokens written into the five-minute prompt cache | 1.25× input |
| cache_read_input_tokens | Prompt tokens served from the cache | 0.1× input |
A coding agent re-sends the whole conversation on every turn, so after the first few turns nearly all of its input is a cache read. In the record above, cache reads are 83% of the tokens and about a quarter of the cost; the 18,230 tokens written to the cache cost more than twice as much as the 96,114 read from it. Two consequences:
- A "tokens used" number is not a cost number. Two engineers with the same token total can differ by 5× in spend depending on how much of it was cached reads versus output. Price the four fields separately, then add.
- Do not count reasoning tokens twice. Both Anthropic and OpenAI already include them in output. A parser that adds a
reasoning_tokensfield on top inflates every reasoning-heavy model.
Kibble's collector prices each field at the vendor's current list rate, keeps the result as an integer number of micro-dollars (a float would drift over a month of additions), and calls the result what it is: an estimate. What the vendor actually charged is a different number, from a different source, and the two are never added together.
What to leave on the machine
Here are examples of Kibble's usage fields and the content boundary. The privacy policy is the authoritative field list, including capability names and billing-plan metadata. The server rejects unexpected fields.
| Sent | Never sent |
|---|---|
| The day, the agent, the model | Prompts, replies, tool results |
| Input, output, cache read, cache write totals, message count | File contents, diffs |
| Opaque session ids, for de-duplication and size percentiles | Tool arguments |
The repository's name (acme-api, or owner/repo from the git remote) | The path, the username in it, the hostname, any hardware id |
The repo name is the one place a path is touched, so it gets its own function: take the checkout's directory name or the remote's owner/repo, fold worktrees into their main repo, and throw away everything above it. A monorepo package resolves to the monorepo. /Users/alice/clients/acme-bank/api leaves the machine as api, or as acme/api if that is what the remote says, and Alice's name and her client list stay where they were.
The numbers are in the same file as the words. A collector's privacy is the part of its code that reads one and not the other.
The collector that reads these files is open source and about a screen long per adapter.
Read the source- Codex usage dashboard for teams: how to see Codex usage on every machine
- How to track GitHub Copilot usage across a team
- Where GitHub Copilot keeps its usage logs, and how to count them
- How much does Claude Code cost per engineer per month?
- Kibble vs an LLM gateway vs a local usage tool, in one table
- How to see Claude Code cost per engineer, without reading anyone's prompts
- Estimated vs billed: why a coding-agent cost dashboard must never add the two