Docs menu

Claude Code and Codex

Claude Code and the Codex CLI write a transcript of every session to your own disk. The open engine’s importer turns them into runs — every session a run, every model response an llm step, every tool call a tool step, every sub-agent an agent span — with no SDK, exporter or proxy. The engine’s recorders guide documents what becomes what.

1. Keys

Each developer gets their own keys on the project their sessions should land in, so the organization’s Coding agents page can tell developers apart:

  • an ingest key for the importer, labelled with the developer’s name;
  • a read key for the hook pack, if you use it: it asks the pre-call check and reads earlier runs.

2. Import

The importer speaks plain HTTP only and refuses an https:// URL, so it cannot reach https://api.trazadb.com directly. The engine’s traza forward bridges the two: it listens on your machine and passes each request to the gateway over TLS. Install it with the engine’s Python package (Python 3.9 or newer) and leave it running in a terminal of its own:

pip install "git+https://github.com/toshish/traza.git#subdirectory=sdk/python"
traza forward --listen 127.0.0.1:8089 --to https://api.trazadb.com

Keep --listen on 127.0.0.1: your key travels through the forwarder in each request, as it would to the gateway.

Install traza-server from an engine release (the importer is part of the server binary), then, beside the forwarder:

traza-server import claude-code --since 30d --url http://127.0.0.1:8089 --token "$TRAZA_INGEST_KEY"
traza-server import codex --since 30d --url http://127.0.0.1:8089 --token "$TRAZA_INGEST_KEY"

It reads ~/.claude/projects and ~/.codex/sessions (--path for others); --since limits it to sessions with a file modified that recently, and --project-filter to sessions whose path or working directory contains a string. --dry-run converts and counts without sending. Importing again is safe: ids come from the transcripts themselves, so a span sent twice is stored once.

--url must be plain HTTP: the forwarder’s address, never the gateway’s https:// one. The Coding agents page in the console gives these commands with your developer key filled in.

Before reading anything, the importer asks the gateway who its key is (GET /v1/whoami) and stops if the key cannot write.

3. Follow live sessions

traza-server import claude-code --watch --url http://127.0.0.1:8089 --token "$TRAZA_INGEST_KEY"

With traza forward still running beside it, --watch polls every second (--interval-ms) for appended lines and new files, so a session you are running is a live run within about a second, in Mission Control with its foresight. Progress is kept in ~/.traza/import-state.json per destination, so a restart sends nothing twice; when the gateway cannot be reached the watch backs off and tries again. The key is never written to the state file.

4. Steer them

The engine’s Claude Code hook pack asks the pre-call check before each tool call: a stop or a block nudge denies the call with the reason shown to Claude, and advice is added as context. It never answers “allow”, so your own permission prompts still ask. From a checkout of the open engine, in your project:

python3 examples/claude-code/install.py --url https://api.trazadb.com
export TRAZA_TOKEN=tzk_…   # the read key, in the environment Claude Code runs in

The hook pack speaks HTTPS itself. Its session id is the importer’s run id, so nudges and fuses apply to the session you are watching. Give the sessions a policy (match: {"service": "claude-code"}) with a full key; see Guard.

What the sessions answer

Everything Traza reads from runs: the Fate Map of your sessions (/v1/fate/map?agent=claude-code), the X-Ray and Cache Autopsy of any call, the detector library (/v1/detectors/findings: test tampering, edit thrash, unverified finishes and the rest), and Agent Blame — which session, step, prompt and model introduced a line:

curl -sG https://api.trazadb.com/v1/blame -H "Authorization: Bearer $TRAZA_READ_KEY" \
  --data-urlencode 'path=src/cart.rs' \
  --data-urlencode 'line=self.items.iter().map(|item| item.price).sum()'

The organization’s Coding agents page brings every developer together: spend by developer, repository (the session’s working directory), model and sub-agent, from every session. Cache efficiency and the dollars lost to cache breaks come from autopsies of the ten most expensive sessions in the window, summed into one figure. The page also lists detector findings, and estimates the AI share of a codebase by sampling lines through blame, labelled as an estimate.

Costs

The importer records tokens and invents no cost. A session’s cost is derived from your project’s cell’s pricing table: the list prices Anthropic, OpenAI and Google publish for their models, per million input, output, cache-read and cache-write tokens, at the standard tier. In Claude Code and Codex sessions, cache reads and writes are priced at their own rates, counted the way each provider counts them.

A model the table does not name, such as a model released after the table was last updated or a cloud partner’s model id like Bedrock’s anthropic.claude-…, is counted as unpriced rather than guessed: the Coding agents page shows how many calls each figure leaves out. Derived cost is an estimate, not your bill. It leaves out batch discounts, long-prompt price tiers, Anthropic’s higher rate for one-hour cache writes, and negotiated prices.

Limits

Both transcript formats are undocumented: a record the importer does not understand is counted under its type and skipped, never an error. Transcripts carry no system prompt or tool definitions. Blame cannot see lines written by shell commands. The full list is in the recorders guide.