Skip to main content

OpenAIDR

PyPI Python Licence CI

Session collection for AI coding agents.

OpenAIDR reads the session state AI coding agents already write to disk and normalises it into one session model with stable span identity.

Status: beta, on PyPI. Two agent kinds read today (claude-code, devin). Start with Install and run, then What OpenAIDR reads, and what it emits if you want to know what leaves your machine before you run it. (Nothing does.)

Today it reads two kinds: Claude Code and Devin (devin, which covers Devin CLI, Devin Desktop's local agent and editors driving it over ACP: one agent, one session store). Codex and Cursor are the intended next kinds and are not read yet — a session from either is absent from the output entirely, not collected and marked. The parsing dependency supports more formats than this package instantiates, which is a distinction worth holding onto; what each additional kind would cost is measured in Agent kind coverage.

It answers what an agent did, never what that means. No scoring, no identity resolution, no findings, no upload path. Interpreting a session is a consumer's job; OpenAIDR takes no position on what a consumer concludes.

What OpenAIDR reads, and what it emits

OpenAIDR reads files the agent already wrote — Claude Code's JSONL transcripts and its per-server MCP connection logs — opened read-only. No process hooks, no proxy, no injected agent, no listener, and nothing is written back.

There is no upload path at all. OpenAIDR reads, normalises and returns a model. It opens no network connection and ships nothing anywhere. What a consumer does with the model is the consumer's decision and the consumer's boundary to state.

Transcripts contain a person's own words, the arguments they passed to tools, and the paths they work in. The session model separates that from the structural account of what happened, and the separation is enforced in code, not by convention — LOCAL_ONLY names every field that serves correlation and local rendering only.

Neither CLI surface prints the conversation:

Text (--detail) --format json
Session id, agent kind, start, turn count shown emitted
Tool name, MCP server, status, result size, duration, truncation shown emitted
Span identity — emitted
Model, source, working directory — emitted
MCP transport, endpoint, advertised name/version, failure category aggregate counts emitted per connection
Turn text, initial prompt, context items never never
Tool arguments, tool results never never
Machine, user never never
MCP failure detail (the server's own words) never never
Tool error text shown withheld

--format json is the shape most likely to be redirected to a file, piped into another program, or pasted somewhere, so it is the narrower of the two: it carries the structural account and drops even the error text the local text surface shows. There are tests asserting that a prompt and an sk--shaped token present in a transcript reach neither surface.

One honest caveat: the boundary described above is the boundary of the CLI. The in-memory Session model does carry the LOCAL_ONLY fields, because a consumer embedding OpenAIDR as a library may legitimately need them on the machine that produced them. If you embed it, Session.public_descriptor() is the deliberately narrow shape intended to travel outward, and drawing your own boundary is your call to state.

What OpenAIDR adds on top of adr-sensor

Session parsing itself comes from Uber's ADR project (adr-sensor, Apache-2.0) — it owns the on-disk formats, and it is OpenAIDR's only runtime dependency. It is confined behind an adapter: upstream types convert at the reader boundary and never appear elsewhere. Any one agent kind can instead be backed by an OpenAIDR-owned reader on the same contract — per kind, not all-or-nothing.

What the adapter adds is not a thin wrapper. Upstream's unified event model was measured against this one rather than assumed (the full table):

adr-sensor supplies OpenAIDR adds
Discovery A whole-tree walk keyed on the sessionId field A per-file walk. Every record in a subagent transcript carries the parent's sessionId, so upstream's own walk collapses a parent and its subagents into one identity. OpenAIDR keeps the file path, which is the only thing distinguishing them.
Span identity No call identifier below the session An identity for one tool call that survives re-reads of a growing session, derived only from what cannot change as it grows
Outcomes success or unknown A six-status model — ok, error, rejected, pending, interrupted, unknown — with five emitted from Claude Code's own records
Timing No timestamp below the session Per-call wall clock, from the transcript's record of both ends of a call
MCP Nothing Transport, the server's advertised identity, connection outcome and per-call outcome, read from the per-server connection logs — the only place any of it is written down
Tool names The raw name A split into (server, tool) per kind, never one global pattern: applied to the wrong kind a general pattern does not fail to find a server, it finds the wrong one
Agent kinds An on-disk self-name A mapping to an agent kind, where an unmapped kind is reported as unmapped rather than guessed
Failures A per-file error printed to stdout and swallowed That narration captured and turned into a reported ReaderFailure, so an unreadable file is a stated gap rather than a silent absence
Privacy boundary None LOCAL_ONLY, above

The pin is exact (adr-sensor==1.0.0, not >=): the adapter reads upstream internals as well as its public entry point, and a test asserts the exact error string upstream prints, so a version bump is a deliberate re-check rather than silent drift.

Install and run

uv tool install openaidr

That installs an openaidr command:

$ openaidr --version
openaidr 0.1.0

openaidr sessions prints what each agent on this machine actually did — sessions, turns, tool calls, and how each one ended. Per-call timings come from the transcript's own record of both ends of a call, not from the parsing dependency's event schema, which carries no timestamp below the session.

For Claude Code it also reads the per-server MCP connection logs, which is the only place the transport, the server's advertised identity, and the outcome of an MCP call are written down. That enrichment is best-effort — the path is an undocumented cache — and every way it can fall short is reported rather than passed off as an absence.

$ uv run openaidr sessions --since 2d --detail
claude-code:96a1d0d4-d106-4cd1-9031-1e174593f8d0  [claude-code]  2026-08-29T05:11:38+00:00  86 turns
                   142c  Read
                    79c  Bash
    rejected        41c  github/create_issue

--format json emits the same account machine-readably, --agent-kind selects one kind, and --since bounds the window. Nothing is resolved to a component and nothing is judged: what a consumer concludes is the consumer's concern.

Design

docs/specs/session-collection.md — the session model, span identity, per-agent-kind normalisation, and the collection interface. Decisions in docs/adrs/, and a doc index in docs/.

Status

Beta. Session collection works for claude-code: sessions, turns, tool calls, and span identity, read by an OpenAIDR-owned reader that calls adr-sensor's Claude Code parser one file at a time and normalises everything upstream's shared event schema cannot express. The model has six outcome statuses; this reader emits five of them (unknown, rejected, pending, ok, error) from Claude Code's own transcripts and, for MCP calls, its per-server connection logs — interrupted is modelled but not emitted, since neither source records it.

Not yet built, in the order they matter:

  • Two further agent kinds, Codex and Cursor. What each would cost is measured in Agent kind coverage rather than assumed — including why Cursor cannot be added dependency-only without breaking span identity.

For long-lived consumers, IncrementalCollector accepts an agent kind and one changed transcript path. It cold-reads that file once, then projects only newly appended, newline-terminated records while returning its current full session. IncrementalCollector.locations() says which directories hold each kind's session files, sub-agents included, so a consumer can watch every kind without knowing its layout (ADR-0023). The cursor is in memory only; process restart deliberately falls back to a cold read. See ADR-0011.

Why this reader walks the tree and calls adr-sensor's parser per file, rather than going through upstream's own whole-tree walk, is recorded in the spec.

Contributing

Reading an agent's session log and normalising it is plumbing, and every new agent format is coverage a contributor can add without touching anything that interprets the result. That is the contribution this project is shaped around, and CONTRIBUTING.md walks the reader contract end to end — the four wiring points, then the traps that cost us an ADR each.

Security

For a vulnerability in OpenAIDR's own code, see SECURITY.md — report privately via a GitHub security advisory rather than a public issue.

Licence

Apache-2.0. See LICENSE.

OpenAIDR depends on adr-sensor from Uber's ADR project, Copyright Uber Technologies, Inc., also Apache-2.0, consumed unmodified from PyPI with no source vendored here. Attribution is in NOTICE, which ships inside both the wheel and the sdist.

Metadata

Release files for openaidr 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openaidr 0.5.2
File Size Uploaded
openaidr-0.5.2.tar.gz 300.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for openaidr 0.5.2
File Interpreter ABI Platform
openaidr-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 399.7 kB

Release files / openaidr-0.5.2.tar.gz

Download URL openaidr-0.5.2.tar.gz
Size 300.2 kB
Tags Source
SHA-256 checksum
How to use checksums
54dfeea6691f3514af97c6a8ba6dab41d632246b9efc7fa939f7ebc8cfe48b67
BLAKE2b-256 checksum
How to use checksums
aa43c68abea5669ac6f6c526ba903da3b7efcde8edf6e940ddbf532296ea2ea1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / openaidr-0.5.2-py3-none-any.whl

Download URL openaidr-0.5.2-py3-none-any.whl
Size 99.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4d27a8aa5ff80684879a4100fd3512c1bb01a2c30edb0950bc0a163e8fde379b
BLAKE2b-256 checksum
How to use checksums
131a255f52a0948bd6f13a6a785c35bb5d435a61fedb16e0cef0a9947cb175f7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

0.7.0

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

This release

0.5.2 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page