Skip to content

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.

View as Markdown Open the plain-text version of this page.
text
local transcript -> detect and parse -> sanitize -> OTel spans -> POST /v1/traces

Transcript 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:

    bash
    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:

bash
provon ingest sync ~/.pi/agent/sessions --once

Sync 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:

bash
# 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 --once

provon 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:

bash
provon ingest sync ~/.codex/sessions --once --dry-run --print-sample

The 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
bash
provon ingest sync ~/.pi/agent/sessions --once --include-tool-output truncated

none 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, and token;
  • redact known secret value patterns found in strings: sk-…, sk-ant-…, GitHub gh[opsu]_…, AWS AKIA…, and Bearer … 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, and traceId. 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 --once to watch for changes:

    bash
    provon ingest sync ~/.claude/projects

    Tune the watcher when the source writes large or slow transcript updates:

    bash
    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:

bash
provon diagnose ~/.claude/projects/example/session.jsonl
provon diagnose ~/.codex/sessions --mode rule-signals

This 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#

  1. Open Traces and search for the reported trace ID.
  2. Confirm the root span identifies the coding-agent session.
  3. Inspect message, tool-call, and tool-result ordering.
  4. Open Sessions under the Traces navigation and confirm the reported conversation ID groups the traces.
  5. 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 none mode and source-side controls.