Convert AI-agent session transcripts (Claude Code, Anthropic Messages API, OpenAI Responses, OpenAI Agents SDK) into vCons with the
agent_sessionextension, embedding a Verifiable Agent Conversations (VAC) record inanalysis[].
Spec targets:
- vCon core:
draft-ietf-vcon-vcon-core-02(syntax"0.4.0") - Agent session:
draft-howe-vcon-agent-session-00 - VAC record:
draft-birkholz-verifiable-agent-conversations
For each AI-agent session, it produces a single vCon that carries:
- Parties — the user (party 0) and each agent (party 1+) with
meta.agent_session(model_id, provider, recording_agent, cwd, vcs_branch, vcs_commit, parent_agent_id for sub-agents). - Dialog turns — user prompts and assistant replies as ordinary
dialog[]entries. - Internal trace — tool calls, tool results, reasoning, and system events embedded as a JSON-encoded VAC
verifiable-agent-recordinanalysis[]withtype: "agent_trace"andschemapointing at the VAC datatracker URL. - File-edit provenance —
attachments[]entries withpurpose: "agent_file_change"whenever the agent's tool calls touched a file (Claude Code:Write,Edit,MultiEdit,NotebookEdit, and file-touchingBashcommands). - Environment metadata —
purpose: "agent_environment"per agent. - Optional lawful basis —
lawful_basisextension attachment for synthetic / test fixtures and recorded consent.
| Platform | Mode | Notes |
|---|---|---|
| Claude Code | file (~/.claude/projects/**/*.jsonl) |
Native parser; sub-agents via Task tool |
| Anthropic Messages API | request/response logs | via vcon-mcp-adapters bridge |
| OpenAI Responses API | request/response logs | via vcon-mcp-adapters bridge |
| OpenAI Agents SDK | live TracingProcessor |
enqueue-only callback; daemon consumes |
| OpenTelemetry | OTLP/JSON spans | GenAI semantic conventions; no vendor SDK, no extra deps |
Anthropic/OpenAI platforms require pip install vcon-mcp-adapters alongside this adapter.
uv pip install -e .
# Optional extras:
uv pip install -e ".[cbor,signing,dev]"
# For Anthropic / OpenAI source bridges:
uv pip install vcon-mcp-adaptersvac-adapter convert \
~/.claude/projects/-Users-x-proj/2026-05-22-abc.jsonl \
--platform claude_code \
--out /tmp/session.vcon.jsonPoint it at anything that already emits the OpenTelemetry GenAI semantic conventions — an OTLP/JSON export body, or a bare JSON array of spans:
vac-adapter convert trace.json --platform otel --sign-key private.pem --out session.vcon.jsonSpans map by gen_ai.operation.name: chat / text_completion /
generate_content / invoke_agent become dialog[] turns (from
gen_ai.input.messages and gen_ai.output.messages, or the legacy
gen_ai.user.message / gen_ai.choice span events); execute_tool becomes a
tool_call + tool_result pair inside the VAC record; everything else under
the trace becomes a VAC event. Model, provider, and agent identity come from
gen_ai.provider.name / gen_ai.request.model / gen_ai.agent.*; host and OS
from resource attributes. A tool span with no model attributes inherits its
parent span's agent.
--sign-key JWS-signs the finished vCon (RSA private key, PEM) so the output
is a verifiable artifact, not just a JSON blob.
Options:
--granularity {session,per_tool_call}— singleagent_traceper session (default) vs one per tool call (for selective redaction).--vac-encoding {json,cbor}— CBOR mode emits a base64url-encoded CBOR record withmediatype: application/cbor.--critical-agent-session— also addagent_sessionto vConcritical[].--no-lawful-basis— skip the lawful_basis attachment.
cp config.example.yaml config.yaml
# edit config.yaml: source.platform, source.claude_code.projects_dir, webhook.url, ...
vac-adapter daemonDaemon mode tails the configured source path, builds a vCon per detected session, and POSTs to the configured webhook with HMAC-SHA256 body signing, exponential backoff, and a dead-letter queue on full failure.
/healthz and Prometheus /metrics are exposed on server.host:server.port.
The adapter inherits the 14 spec-compliance smoke tests from vcon-adapter-template plus 14 additional agent_session / VAC assertions:
vcon: "0.4.0"agent_sessioninextensions[]- Every agent party carries
meta.agent_session.{model_id, provider, recording_agent} analysis.type == "agent_trace"with requiredvendor,schema, valid JSONbodyparsable as VAC- Deterministic VAC entry IDs (UUIDv5 from session namespace)
agent_file_changeattachments includepurpose,party,dialog,content_hash- Per-tool-call granularity emits one trace per
tool_call - CBOR mode round-trips canonically
Run the suite:
pytest- Reasoning fidelity — when ingesting via
vcon-mcp-adaptersthe upstream Claude Code parser records onlythinking_block_count; our native Claude Code parser preserves full thinking text. - Edit
content_hashis post-hoc lossy unless--git-blame-mode(planned) reads the file at the recorded commit viagit show. - Sub-agent detection for Claude Code relies on the Task tool naming; structural changes upstream will silently degrade to single-agent emission.
vcon-adapter-template— scaffold this adapter was forked fromvcon-mcp-adapters— sibling repo providing Anthropic / OpenAI / Agents SDK parsersdraft-kuehlewind-audit-architecture-00— wider agent auditing architecture this adapter participates in
MIT