Skip to main content
Every adapter reports the LLM tokens a session spends, in one shape for every framework. No code change is needed: each integration reads usage from the framework’s model responses, traces or transcripts.

Where usage appears

  • Each stop event carries the session’s running total in data.usage.
  • The session summary (get_summary(), or report() on the zero-config modules) carries the final total in usage, and exporters receive it in on_session_end.
Both follow SessionUsage in schemas/exporter/common.json. When no source reported anything, the field is absent rather than zero.

What the numbers mean

Frameworks disagree on whether input tokens include cached tokens. Anthropic’s API leaves them out; OpenAI, Gemini, LangChain and CrewAI count them in. Agentegrity uses the inclusive definition and converts the others, so cache and reasoning counts are always parts of input and output, never added on top. The cache, reasoning and request fields are absent when no source reported them, so a missing number never shows up as zero. Anthropic does not break out thinking tokens per request, so a Claude session has no reasoning_tokens even when the model reasoned; those tokens are inside output_tokens. Totals are kept per model in by_model, so a session that switches models, or runs subagents on a smaller one, shows each separately. A SessionUsage for a session with two model calls on two models:
The schema allows additional fields on SessionUsage and on each per-model entry, and later releases may add some. Consumers should ignore fields they do not know.

Provenance

sources lists where the counts came from: complete is false when:
  • The transcript starts after a compaction that dropped earlier history.
  • A request never got its final usage line. Claude Code sometimes omits it for subagent requests.
  • The counts were reconstructed from running totals. Codex builds without token_usage_record lines report usage only as running totals.
  • On AI SDK 5, generate spans drop the cache-read count, so those calls are marked incomplete.

Deduplication

Every entry is keyed by the model call it counts: a request id, a response id, a run id or a trace id. Recording a key again replaces the earlier entry instead of adding to it. That is how sources that repeat themselves are counted once:
  • Claude Code transcripts write one line per content block, each with the request’s usage. The last line of a request wins.
  • Running totals are reported again after every call, so each new report replaces the previous one under the same key.
  • A LangChain handler attached twice reports the same run_id twice.
When a more exact source arrives, the less exact one is dropped. For example, passing Claude Agent SDK messages to observe() replaces the transcript estimate with the SDK’s own per-model totals. A custom adapter records usage with record_usage(); a missing model falls back to the profile’s model_id:
In TypeScript the equivalent is DefaultAdapter.recordUsage() in @agentegrity/client, which shares the Python ledger’s test vectors.

Hook daemon restarts

For Claude Code and Codex, each hook daemon lifetime is its own exporter session. The runtime saves how far each transcript was read in <hook dir>/<host>/<session>.usage.json (mode 0600), so a restarted daemon reports only the tokens spent after the previous one stopped, and adding up a conversation’s sessions gives its total. The file holds paths, read positions, a hash of each file’s first line, and the id of the last call counted. It holds no token counts and no transcript content. A transcript replaced while no daemon ran (for example, a cloud session resuming from a transcript rebuilt after compaction) is detected by its first line, since a replacement can reuse the inode. Lines up to the last call already counted are skipped; a replacement that does not contain that call is counted whole.

Sources per integration

Each integration converts its provider’s cache and reasoning conventions to the inclusive definition above. The per-integration normalization rules are in spec/token-usage.md.

What is kept

Transcript readers keep only usage counts, request ids and model names. Nothing else from a transcript is stored or exported. Usage reaches a backend only through an exporter you attach; see Exporters.
AGENTEGRITY_MODEL_ID sets the model on a coding-agent profile. It is the configured model, so a mid-session model switch is not reflected in that field; per-call model names in by_model come from the transcript.