Traces and replay
A trace is the full record of what a session actually did: every turn, every step, the prompt that went to the model, the completion that came back, and each tool call with its arguments and result. One append-only NDJSON file per session, on your disk, readable in any text editor.
Traces answer the questions logs cannot. Why did the agent pick that tool? What exactly was in the prompt when it went wrong? And — via replay — has anything changed underneath a session since it ran?
Tracing is on by default
The interactive entry points all record. run, tui, and serve boot the runtime with traceDefault: true, so a fresh install traces every session you drive by hand.
Two things do not trace by default:
- The
taskcommand.task run,task tickandtask createboot withtraceDefault: false, so tasks they run are not recorded unless you opt in explicitly. Tasks the background scheduler runs insidetuiorserveare traced like any other turn, because they run in that traced runtime. - The sidecar. Embedded hosts stay silent unless configured.
The config key is tri-state, which is what makes that split work:
{ "tracing": { "trace": { "enabled": null, "maxBytesPerSession": 10485760 } }}tracing.trace.enabled | Meaning |
|---|---|
null (default) | Defer to the entry point. run / tui / serve trace; the task command and the sidecar do not. |
true | Always trace, whatever the entry point. This is how you record task runs started from the task command. |
false | Never trace, including interactive sessions. |
An explicit true or false always wins over the entry-point default.
maxBytesPerSession (default 10 MiB, 10485760 bytes) caps one session’s file. When a write would cross it, the oldest events are dropped: the file is rewritten to about half the cap, keeping the opening session_started line, and a trace_truncated marker records how many events and bytes were lost. Recording then continues. An event too large to fit on its own is skipped.
To change the cap, set the key directly:
atomic-agent config set tracing.trace.maxBytesPerSession 52428800Where traces live
<stateDir>/traces/<sessionId>.ndjsonWith the default state directory that is ~/.atomic-agent/traces/. Override the root with ATOMIC_AGENT_STATE_DIR.
Commands
atomic-agent trace list [--limit N]atomic-agent trace show <sessionId> [--step N] [--raw]atomic-agent trace export <sessionId> [--format ndjson|json]atomic-agent trace replay <sessionId> [--step N]Summarise the most recent trace files in the traces directory.
atomic-agent trace list --limit 20Pretty-print one session as a human-readable chronology.
atomic-agent trace show s-1234atomic-agent trace show s-1234 --step 2atomic-agent trace show s-1234 --raw--step N isolates a single step. --raw prints the unformatted lines instead of the formatted chronology. A session id with no trace file on disk exits 1.
Dump the raw trace for processing elsewhere.
atomic-agent trace export s-1234 # ndjson (default)atomic-agent trace export s-1234 --format json > session.json--format accepts ndjson (the default) or json. Anything else exits 1. NDJSON streams the file as-is, one event per line — good for jq and grep. JSON wraps the events into a single array, easier to load whole.
Rebuild the stable prompt prefix with the current tools, skills, and capabilities, then compare its hash against what the trace recorded.
atomic-agent trace replay s-1234atomic-agent trace replay s-1234 --step 2--step N limits the comparison to one step. Output lists each turn and step with its recorded hash, the current hash, and whether they diverge:
turn step drift recordedHash currentHashReplay and prompt drift
replay does not re-run the agent. It answers a narrower and more useful question: would this session get the same prompt prefix today?
The stable prefix is the byte-identical head of every prompt — persona, tool catalog, skill index, capabilities. Replay reconstructs it from the current runtime and compares hashes with the ones the trace recorded. A mismatch means something underneath the session changed: a tool was added or removed, a skill was installed or disabled, or capabilities shifted.
The exit code is the part to script against:
| Exit code | Meaning |
|---|---|
0 | No drift. The current runtime reproduces the recorded prefix. |
2 | Drift detected on at least one step. |
1 | The command failed — no such session, unreadable file, bad arguments. |
atomic-agent trace replay s-1234 || echo "prefix changed since this session ran"Reading a trace directly
The file is NDJSON, so the usual tools work:
# Every tool call in a sessionjq -c 'select(.type == "tool_invocation")' ~/.atomic-agent/traces/s-1234.ndjson
# Follow a live sessiontail -f ~/.atomic-agent/traces/s-1234.ndjsonTurning tracing off
If you would rather record nothing, set the key to false:
atomic-agent config set tracing.trace.enabled falsewhich leaves this in config.json:
{ "tracing": { "trace": { "enabled": false } }}That covers every entry point, interactive ones included. Existing files stay on disk — delete <stateDir>/traces/ yourself if you want them gone.