Agent Transcript Sync
Provon can project persisted Claude Code, Codex, Pi, and generic JSON/JSONL sessions into normalized OpenTelemetry spans. Use this path when a coding agent already writes a local transcript and adding live SDK instrumentation is unnecessary or impossible.
local transcript -> detect and parse -> sanitize -> OTel spans -> POST /v1/tracesTranscript sync reads files from the local machine. The server never scans developer workstations.
Prerequisites#
The CLI authenticated, or a project API key with
telemetry:ingest:provon auth token set --api-key "$PROVON_API_KEY"The agent's transcript directory exists locally; see the source table below.
For read-back after upload, the same key needs
telemetry:read.
For real-time reporting while the agent is running, use
provon ingest hooks install instead of sync. In this build live upload works for
Claude Code, Codex, and Cursor; the OpenCode, dsh, and Pi plugins do not upload traces yet. Pi sessions persist
to ~/.pi/agent/sessions/, so backfill them with sync:
provon ingest sync ~/.pi/agent/sessions --onceSync is for backfill; hooks are for live capture. See Install hooks for the per-tool status.
Supported sources#
| Source | Typical directory | Format signatures |
|---|---|---|
| Claude Code | ~/.claude/projects/ |
uuid / parentUuid / sessionId, or user/assistant/summary records with a message |
| Codex | ~/.codex/sessions/ |
session_meta, turn_context, event_msg, response_item, or Codex payload types |
| Pi | ~/.pi/agent/sessions/ |
model_change, thinking_level_change, session, or message records with a role |
| Generic | Any .json or .jsonl transcript path |
Records with a role and content; parsed through the generic projection |
Detection uses file content and known format signatures, not only the directory name. A parent directory can
therefore contain more than one supported source. The scanner walks the directory recursively, skips
node_modules and .git, and accepts .jsonl and .json files.
What the projection preserves#
The adapter projects a persisted transcript into normalized OTel spans while preserving:
- session and conversation identity (stable trace/span IDs derived from source and session);
- user, assistant, and reasoning events;
- tool calls and tool results;
- model and token usage when present (including reasoning and cache tokens where the source records them);
- timestamps, working directory, and agent version;
- warnings for malformed or incomplete source records.
To inspect a local transcript without uploading, run the built-in diagnostic Rules against it (see below).
Run sync#
Build the CLI from the repository, or use the installed package:
# From the repository
pnpm --filter @provon/cli build
node cli/dist/bin.js ingest sync ~/.claude/projects --once
# Installed package
provon ingest sync ~/.claude/projects --onceprovon ingest sync uploads to https://otel.provon.dev/v1/traces by default. For local development, set
PROVON_OTEL_URL="http://127.0.0.1:3000/v1".
Command options#
| Option | Default | Purpose |
|---|---|---|
--once |
off (watch mode) | Run one backfill pass and exit |
--dry-run |
off | Parse and summarize changes without uploading or changing state |
--print-sample |
off | Include a small OTLP payload sample in the output |
--include-tool-output <mode> |
truncated |
Tool-output policy; see below |
--poll-interval <ms> |
30000 |
Watch-mode rescan interval |
--settle-ms <ms> |
1000 |
Watch-mode debounce before reading a recently written file |
--max-payload-bytes <n> |
8388608 (8 MiB) |
Maximum bytes per OTLP upload batch |
Preview before upload#
Use a dry run to verify detection, parsing, warnings, and projected payload size without uploading:
provon ingest sync ~/.codex/sessions --once --dry-run --print-sampleThe JSON result reports detected sources; scanned, changed, skipped, and uploaded file counts; trace and conversation IDs; event and payload counts; parse warnings and per-file failures; and the local state-file path. Inspect the sample for private content before the first real upload.
Tool output policy#
Tool output often contains the strongest diagnostic evidence and the most sensitive data. Select a mode explicitly:
| Mode | Behavior | String budget |
|---|---|---|
truncated |
Default. Retain tool results with bounded strings | 32,000 characters per string |
none |
Remove tool-result text and structured result values | 1,000 characters; result bodies nulled |
full |
Retain larger tool results, still subject to sanitization limits | 256,000 characters per string |
provon ingest sync ~/.pi/agent/sessions --once --include-tool-output truncatednone reduces evidence for Rules that depend on failed command output, negative tool results, or verification.
full increases payload size and privacy risk.
Sanitization#
Before projection, the adapters:
- replace values under secret-bearing keys matching
api_key,authorization/auth,bearer,cookie,credential,password,private_key,refresh_token,secret, andtoken; - redact known secret value patterns found in strings:
sk-…,sk-ant-…, GitHubgh[opsu]_…, AWSAKIA…, andBearer …tokens; - bound strings (32,000 characters default), array items (100), object keys (100), and nesting depth (8).
This is best-effort sanitization, not a data-loss-prevention boundary. Review source content and use
--include-tool-output none when the transcript can contain data that must never leave the workstation.
Capture semantics and idempotency#
Live writes: a JSONL file modified within the settle window is read up to its last newline, so a partial write never produces a malformed record.
Change detection: a local state file stores per-file
contentHash, size,mtimeMs,uploadedAtMs, andtraceId. Unchanged files are skipped; only changed files are reprojected.State location:
$PROVON_SYNC_STATE_DIR, else$XDG_STATE_HOME/provon/sync, else~/.local/state/provon/sync. The state key is derived from the source path, API URL, project, and profile.Stable identity: trace IDs are derived from source plus session id; span IDs from the event parts. Retries and re-runs reuse the same IDs instead of inventing a new execution identity.
Continuous mode: omit
--onceto watch for changes:provon ingest sync ~/.claude/projectsTune the watcher when the source writes large or slow transcript updates:
provon ingest sync ~/.claude/projects \ --poll-interval 60000 \ --settle-ms 2000 \ --max-payload-bytes 8388608
Diagnose without upload#
Run the built-in diagnostic Rules directly against a transcript file or directory:
provon diagnose ~/.claude/projects/example/session.jsonl
provon diagnose ~/.codex/sessions --mode rule-signalsThis path does not require an API key and persists no Findings. When semantic review is required, the calling agent uses its current model and returns a hash-bound structured response. See Trace diagnosis with CLI.
Verify after sync#
- Open Traces and search for the reported trace ID.
- Confirm the root span identifies the coding-agent session.
- Inspect message, tool-call, and tool-result ordering.
- Open Sessions under the Traces navigation and confirm the reported conversation ID groups the traces.
- Review sync warnings before relying on the trace for diagnosis.
Limitations#
- Sync projects persisted files only; sessions never written to disk cannot be recovered.
- Live hook support varies by tool; OpenCode and dsh have no backfill path in this build.
- Best-effort sanitization is not a compliance boundary; sensitive environments should use
nonemode and source-side controls.