AI Agent Session Audit Trails: Cost, Tokens, and Model Attribution
Atomic session attestations summarize which changes an AI coding session recorded, which model metadata was reported, and—when the integration supplies usage—its tokens and cost. When Atomic receives a supported session-end event, it attempts to create this audit node for sessions that recorded at least one change, and the attestation travels with its covered changes on push.
Content addressing makes an attestation tamper-evident under its hash; it does not authenticate the model provider or the origin of hook-reported metadata. Treat agent and model fields as captured attribution unless a separate signed artifact establishes a stronger claim.
What Is an Attestation?
When a supported agent session ends, Atomic attempts to create an attestation covering the changes that session actually recorded. The attestation aggregates data from the provenance entries embedded in each covered change:
Session: agent-ses_3781fc7a6ffet5c6r1ILy1BEbv
│
├── Turn 1: Change ABCD23EF (provenance: claude-sonnet-4-5, 3.2k tokens, $0.04)
├── Turn 2: Change DEFG45HJ (provenance: claude-sonnet-4-5, 5.1k tokens, $0.06)
└── Turn 3: Change JKLM67NP (provenance: claude-sonnet-4-5, 4.1k tokens, $0.05)
│
▼
Attestation XMJZ3IPF
├── Agent: OpenCode (anthropic)
├── Session: agent-ses_3781fc...
├── Models: claude-sonnet-4-5 (12.4k tokens, $0.15)
├── Code: +116 lines, -8 lines
├── Wall time: 3m 42s
└── Changes covered: ABCD23EF, DEFG45HJ, JKLM67NP
Viewing Attestations
Inspect One Change's Embedded Provenance
atomic change <change-hash>
The default change view renders the first embedded provenance entry as === Attestation ===, with that change's provider, model, tool, suggestion type, available tokens and cost, session identifier, and turn metadata. It also includes === Change Ledger === with the observed goals, tools, edits, decisions, and verification associated with the change.
Despite its display heading, this inline block is embedded provenance attached to one change. atomic agent attest lists and inspects separate session-level attestation artifacts that can aggregate several covered changes.
List All Session Attestations
$ atomic agent attest
XMJZ3IPF OpenCode · claude-sonnet-4-5 · 12.4k tokens · 3m 42s · 3 changes
R3KQP7YN Claude Code · claude-sonnet-4-5 · 8.1k tokens · 1m 15s · 1 change
──────────────────────────────────────────
Total: $0.27 · 4 changes covered · 20.5k tokens
Inspect a Specific Session Attestation
$ atomic agent attest --hash XMJZ3IPF
Attestation XMJZ3IPF
Agent: OpenCode
Session: agent-ses_3781fc7a6ffet5c6r1ILy1BEbv
Changes: 3 changes
Wall time: 3m 42s
Cost: $0.15
Tokens: 12.4k
Code: +116 -8
Model Breakdown:
claude-sonnet-4-5: 3.2k in / 9.2k out · $0.15
Changes Covered (3):
ABCD23EF
DEFG45HJ
JKLM67NP
Coverage:
dev ████████████░░░░░░░░ 3/5 (60%)
Filter by View
$ atomic agent attest --view dev
Verbose Output
$ atomic agent attest --verbose
Shows per-model token breakdown and per-change details for every attestation.
How Attestations Are Created
Automatic Creation at Session End
When the TurnOrchestrator receives a session-end event and the session recorded at least one change:
- Read the session's recorded changes — use the change hashes recorded by this session, excluding inherited parent-view history
- Check for existing attestations — find which changes are already covered by prior attestations from the same session (for resumed sessions)
- Determine new changes — filter to changes not yet attested
- Load each change — read provenance entries (model, tokens, cost) and file operations (lines added/removed)
- Aggregate data:
- Per-model token and cost totals
- Total lines added and removed from the CRDT semantic layer
- Wall duration from session timestamps
- Build and save — create the attestation with all aggregated data
Data Sources
The attestation pulls data from two places:
| Data | Source | How It Gets There |
|---|---|---|
| Model name | change.provenance[].model | Set by build_turn_provenance() at record time |
| Token counts | change.provenance[].tokens | Set by build_turn_provenance() at record time |
| Cost | change.provenance[].cost | Set by build_turn_provenance() at record time |
| Lines added/removed | change.file_ops[].line_ops[] | Generated by the CRDT semantic layer during recording |
| Wall duration | session.started_at / session.ended_at | Tracked by the TurnOrchestrator |
| Agent attribution | session.agent_name / session.agent_vendor | Captured from hook events (OpenCode sends provider/model) |
Fallback Behavior
If no provenance usage data is found in the changes but the session payload includes a model name, the attestation creates a minimal model entry. Token and cost fields can remain unavailable when the integration does not report them.
Resumed Sessions
When an agent session is resumed (e.g., claude --resume or continuing an OpenCode session), the attestation system handles it correctly:
- Finds existing attestations for this session ID
- Determines which changes are new (not yet covered)
- Creates a new attestation covering only the new changes
- Chains to the previous attestation via the
previous_attestationfield
$ atomic agent attest --hash R3KQP7YN
Attestation R3KQP7YN
Agent: Claude Code
Previous: XMJZ3IPF # ← chains to the prior attestation
Notes: Resumed session (1 new change, 4 total in session)
...
Automatic resumed-session generation avoids covering the same change twice within that session chain, while the previous_attestation link preserves the session's segmented history.
On-Demand Generation
The server API also supports generating attestations on demand from the provenance data in changes:
POST /tenant/:id/portfolio/:id/project/:id/attestations/generate
This reads the provenance entries from every change on a view, aggregates them, and creates an attestation. This is useful when:
- Changes were pushed before the attestation was created locally
- You want to regenerate an attestation with updated data
- The local attestation was lost or corrupted
Storage and Transport
On Disk
Attestations are stored in the same two-level directory structure as changes:
.atomic/changes/{change-hash[0:2]}/{full-change-hash}.change
.atomic/changes/{attestation-hash[0:2]}/{full-attestation-hash}.attest
Content Addressing
Attestations are content-addressed:
data = postcard::serialize(attestation)
hash = blake3(MAGIC_BYTES + data)
path = .atomic/changes/{hash[0:2]}/{hash}.attest
Push
When you push changes, Atomic automatically uploads attestations that cover the pushed changes:
$ atomic push origin
✓ Pushed 3 changes
✓ XMJZ3IPF attestation ($0.15, 3 covered)
An attestation is only uploaded when all of its covered changes have been pushed. This ensures the server never has an attestation referencing changes it doesn't have.
Data Model
Attestation
| Field | Type | Description |
|---|---|---|
version | u8 | Schema version for forward compatibility |
timestamp | i64 | Unix epoch seconds when created |
agent | AttestAgent | Hook-reported agent attribution (name, display name, vendor) |
session_id | String | Session this attestation covers |
cost_usd | f64 | Total cost across all models |
duration_api_ms | u64 | API processing time |
duration_wall_ms | u64 | Wall clock duration of the session |
code_changes | CodeChangeStats | Lines added and removed |
models | Vec<ModelUsage> | Per-model token and cost breakdown |
changes_covered | Vec<Hash> | Hashes of changes this attestation covers |
previous_attestation | Option<Hash> | For resumed sessions — chains to prior attestation |
notes | Option<String> | Human-readable context |
ModelUsage
| Field | Type | Description |
|---|---|---|
model | String | Model identifier (e.g., claude-sonnet-4-5) |
input_tokens | u64 | Prompt/input tokens |
output_tokens | u64 | Completion/output tokens |
cache_read_tokens | u64 | Tokens read from cache |
cache_write_tokens | u64 | Tokens written to cache |
cost_usd | f64 | Cost for this model's usage |
CodeChangeStats
| Field | Type | Description |
|---|---|---|
lines_added | u64 | Total lines added across all covered changes |
lines_removed | u64 | Total lines removed across all covered changes |
Web UI
The Atomic web UI renders attestations on the Attestations tab of each project. The display includes:
- Summary card — Agent name, cost, tokens, duration, change count, lines changed
- Models Used — Per-model breakdown with token counts and cost
- Provenance Graphs — Interactive visualization of the session's decision DAG (from the provenance graphs associated with covered changes)
- Timeline — Temporal view of the session's activity
See Also
- How to See Why an AI Agent Changed Your Code — How observed agent activity is captured as a causal DAG
- How Atomic Records What Your AI Coding Agent Did — How the full agent lifecycle works
atomic agent attestcommand reference — CLI documentation