open-agent-stream (oas) helps developers gather, retain, replay, inspect, and export their own coding-agent session data across operating systems and one or more machines without depending on vendor-native schemas or vendor-controlled infrastructure.
It captures source-native artifacts, appends them to an immutable local ledger, normalizes them into a portable canonical event stream, and optionally routes those events to one or more sinks. The durable source of truth stays user-controlled and local-first rather than vendor-hosted.
The project optimizes for:
- user-owned session history
- source volatility
- replayability
- inspectability
- exportability
- privacy
- portability
- community extensibility
It explicitly does not optimize for tightly coupled product semantics, vendor-native canonical schemas, or vendor-hosted control planes.
- gather supported coding-agent session artifacts from local machines
- retain raw and canonical history under storage the developer controls
- replay retained history when sinks, schemas, or downstream tooling change
- inspect sessions locally with stable CLI workflows
- export deterministic snapshots for review, analytics, and downstream tooling
- converge data from one machine or many without making a vendor backend the center of the architecture
- developers using coding agents across one machine or many who want to keep session history under their own control
- tool builders who need portable local session traces for review, analytics, compliance, or automation
- OSS contributors who want to expand source coverage, sink coverage, and cross-platform operability
OAS is still early enough that outside contributors can materially expand what it supports.
High-value contribution lanes:
- add or harden source adapters for more local artifact families
- improve cross-platform discovery rules, root/path conventions, and Windows or WSL ergonomics
- contribute fixtures and conformance coverage for new artifact families and replay or export edge cases
- add external sink examples or upstream generally useful sinks
- improve install, packaging, and distribution ergonomics across macOS, Linux, and Windows
- help shape schema, compatibility, and extension boundaries as more agents and destinations are supported
Today the stock oas CLI supports built-in source adapters and out-of-process
external sinks, but it does not yet support drop-in third-party source
adapters. If you want a new source in the stock CLI, the practical path today
is to upstream it here or maintain a custom CLI overlay. See
docs/integrations/README.md and
CONTRIBUTING.md.
This repository is intentionally spec-first. The initial implementation includes:
- versioned normative specs under
/spec - fixtures plus executable conformance-style tests
- a Go reference collector with:
- source adapters for Codex-style and Claude-style local artifacts
- a SQLite-backed raw ledger and state store
- a canonical normalizer
- neutral sinks for
jsonl,sqlite,stdout,http,command, ands3, plus awebhookcompatibility alias tohttp - CLI commands for
run,daemon,replay,export,summary,inspect,analytics,doctor, andvalidate
- product-specific sink semantics in the core repo
- a required vendor-hosted backend or managed OAS control plane
- LLM-powered summarization in the ingestion path
- vendor-defined canonical schemas
- a distributed ingestion system in v0
/docs: project intent, architecture, governance, and roadmap/spec: normative versioned specs and schemas/fixtures: captured examples and expected canonical outputs/conformance: contract tests over fixtures and runtime behavior/cmd/oas: CLI entrypoint/pkg: stable public contracts/internal: runtime implementation details/sources: built-in adapters/sinks: built-in neutral sinks
For early adopters today, the canonical install path is:
go install github.com/open-agent-stream/open-agent-stream/cmd/oas@latest
oas versionIf you are evaluating a local checkout instead:
go install ./cmd/oas
oas versionFor the current distribution reality and PATH troubleshooting, see
docs/adoption/install.md.
If you want to run the fixture-backed demo below, use a local checkout of this
repo so the starter config can point at ./fixtures/....
The demo flow below uses repository fixtures, so run these commands from the repo root. It walks through the core lifecycle: gather, retain, export, and inspect.
oas config init -output ./oas.jsonThis creates a starter config with current defaults, fixture-backed Codex and
Claude sources for a local demo, and a stdout sink. Treat it as a safe demo
config, not a serious multi-machine starting point.
For real use:
- keep the live config outside the repo checkout, usually under
~/.config/open-agent-stream/or/etc/open-agent-stream/ - replace the fixture roots with real local session roots before you run OAS
- use a private ops repo or an existing infrastructure repo for committed machine configs and shared sink templates
If you want fuller shape references, see
examples/config.example.json and the examples
index in examples/README.md.
oas config print -config ./oas.json
oas config validate -config ./oas.jsonoas config printshows a readable effective-config summary by default, including resolved state, ledger, daemon, and source-root paths.- add
-jsonif you want structured output for automation. oas config validatechecks both the config and the repo's bundled fixtures when you run it from anopen-agent-streamcheckout. If you're using an installed binary elsewhere, either pass-root /path/to/open-agent-streamor skip fixture validation.
oas run -config ./oas.jsonWith the default starter config, this ingests the fixture sources in
./fixtures/sources/..., appends them to the local ledger, normalizes them,
and writes canonical events to stdout.
For serious local use, avoid pointing OAS at top-level agent home directories
such as ~/.codex or ~/.claude; those trees usually contain unrelated JSON.
Prefer session subtrees such as:
- Codex:
~/.codex/sessions, optionally~/.codex/archived_sessions - Claude:
~/.claude/projects
mkdir -p ./exports
oas export -config ./oas.json -output ./exports/events.jsonl
wc -l ./exports/events.jsonlAfter the fixture-backed run above, the export should contain six canonical events.
For a built-in reviewer-oriented session summary:
oas summary -input ./exports/events.jsonl -sort recent -limit 20
oas summary -input ./exports/events.jsonl -failedTo drill into one interesting session from that summary:
oas inspect -input ./exports/events.jsonl -session <session_key>
oas inspect -input ./exports/events.jsonl -session <session_key> -command-status attention
oas inspect -input ./exports/events.jsonl -session <session_key> -command-limit 0For a quick event-level skim:
jq -r '[.timestamp, .session_key, .kind] | @tsv' ./exports/events.jsonlFor a reviewer-oriented session summary:
jq -s 'group_by(.session_key) | map({session_key: .[0].session_key, events: length, kinds: (map(.kind) | unique)})' ./exports/events.jsonlWhat to look for:
session_keygroups events that belong to the same captured sessionkindshows the interaction shape (session.started,message.user,command.finished,tool.failed, ...)exportgives reviewers a deterministic JSONL snapshot without re-delivering to sinkssummaryturns exported JSONL into a stable per-session review table with project paths plus optional recent/biggest/failed triage knobs, without requiringjqinspectturns onesession_keyfrom that summary into a session-level view with project path, duration, collapsed command summaries, failure signals, tool failures, anATTENTION EVENTSsection with short failure excerpts for non-zero command exits and tool failures when output is available, plus backfilled command context when the raw failure event omitted the command text, and a compact timeline;-command-status attentionisolates failed or incomplete command rows, and-command-limit 0restores the full command list when needed
oas analytics build -config ./oas.json
oas analytics query -config ./oas.json
oas analytics query -config ./oas.json -preset failures
oas analytics export -config ./oas.json -output ./exports/analytics
oas analytics status -config ./oas.jsonoas analytics builds a local DuckDB cache on demand from retained ledger
history. It is not part of the ingest hot path. The stable portable contract is
the exported Parquet snapshot plus manifest.json; the local DuckDB layout is
an implementation detail except for the public query views.
oas daemon start -config ./oas.json
oas daemon status -config ./oas.json
oas daemon status -config ./oas.json -json
oas daemon stop -config ./oas.jsonoas daemon status now surfaces current storage usage, configured limits, the
most recent storage-guard event it can recover from the daemon log, and a more
structured JSON view of runtime settings plus resolved status, lock, and
control paths for automation.
Daemon lifecycle is now daemon-owned rather than PID-probe-owned:
<prefix>.jsonis the live status record for the current daemon instance<prefix>.lockis the exclusivity primitive that prevents duplicate starts<prefix>.control.d/holds stop requests the daemon acknowledges on its next control poll
The JSON status output keeps running as a compatibility alias for live, and
also exposes explicit live, ready, heartbeat freshness, lifecycle state,
and the current instance_id. start waits for the matching instance to
become ready before returning, and restart will fail rather than spawning a
duplicate if the previous owner does not release the lock.
Store daemon state on a local filesystem. Network-mounted state directories are not supported for daemon lifecycle control because lock semantics are not portable there.
Remote delivery is optional. You can stop at local retention plus export,
summary, and inspect if you only need user-controlled collection and review.
Built-in remote sinks use two config blocks:
settings: sink-specific destination fields such as URL, headers, bucket, key templates, argv, and secret references for credentialsdelivery: timing and retry policy such as batch size, batch age, fixed release windows, retry backoff, and poison-batch thresholds
Common built-in choices:
http: send neutral OAS batches to a proprietary API or generic HTTP collectorcommand: stage a sealed payload file and invoke your own transport such asrsync,scp, or a wrapper scripts3: upload sealed batch objects to a bucket with a deterministic object key
For serious multi-machine use, do not keep the live destination config inside
the public OAS repo checkout. Keep committed configs in a private ops repo and
render or copy the live machine config into ~/.config/open-agent-stream/.
See docs/adoption/config-management.md,
docs/adoption/remote-destinations.md,
and docs/adoption/multi-machine.md.
A minimal http sink looks like:
{
"id": "remote-http",
"type": "http",
"settings": {
"url": "https://collector.example.invalid/ingest",
"method": "POST",
"format": "oas_batch_json",
"bearer_token_ref": "env://OAS_REMOTE_TOKEN"
},
"delivery": {
"max_batch_events": 100,
"max_batch_age": "5s",
"retry_initial_backoff": "2s",
"retry_max_backoff": "1m",
"poison_after_failures": 5
}
}For a full guide with http, command, and s3 examples plus a safe local
validation flow, see
docs/adoption/remote-destinations.md.
If you are validating a local checkout in daemon mode, prefer a real built
binary rather than go run so oas daemon start re-execs the exact build you
tested:
go build -o ./bin/oas ./cmd/oas
./bin/oas config print -config ./oas.json
./bin/oas daemon start -config ./oas.json
./bin/oas daemon status -config ./oas.json -json
./bin/oas doctor -config ./oas.json
./bin/oas daemon stop -config ./oas.jsonoas doctor -config ./oas.json
oas doctor -config ./oas.json -jsonoas doctor prints a readable table by default and supports -json for
automation.
oas replay -config ./oas.json
oas replay -config ./oas.json -dry-runNotes:
go install github.com/open-agent-stream/open-agent-stream/cmd/oas@latestis the primary install path today.- The demo starter config points at repository fixtures, so run the Quickstart from the repo root or change the generated source roots to your real local artifacts.
oas validateis repo-checkout-aware because it also validates bundled fixtures; for installed-binary use outside the repo, pass-root /path/to/open-agent-streamor skip that step.- Default persistent storage should live in a durable app state directory. If you omit
state_path/ledger_path, OAS defaults toXDG_STATE_HOME/open-agent-streamor~/.local/state/open-agent-stream. oas <command> --helpis intended to be the primary discovery surface for common and advanced usage.sqliteis replay-safe by default because it converges byevent_id.jsonlis an append-only delivery sink, so replay will duplicate lines if you explicitly include it.s3is also append-only by default; use deterministic keys and expect replay to stay disabled unless you explicitly opt in.http,command,external, andwebhookare side-effecting delivery sinks and are skipped by default during replay.exportis the deterministic way to produce a JSONL snapshot from the ledger for review or downstream tooling.max_storage_byteslets the daemon enforce a storage budget for its managed files. When usage exceeds the budget, OAS prunes safely delivered ledger rows, compacts the SQLite stores, and exits loudly if it still cannot get back under the configured limit.- Before handing OAS to an early adopter, read
docs/adoption/early-adopters.mdfor current guardrails and evaluation expectations. - For remote sink setup and validation, read
docs/adoption/remote-destinations.md. - For multi-machine rollout and service management, read
docs/adoption/multi-machine.md.
If you want to build a third-party adapter or sink, start with:
Today the public contracts are ready for authoring against /pkg, and the
stock CLI supports built-in sinks plus out-of-process external sinks. In
practice, that means:
- upstream an adapter or built-in sink here if you want it available in the
stock
oasCLI for everyone, - ship an external sink executable if you want a proprietary or unbundled destination without forking the runtime, or
- maintain a custom CLI overlay when you need a custom source adapter or a deeper runtime change than the published sink/runtime contracts cover
See docs/integrations/README.md and
rfcs/0002-external-plugin-runtime.md
for the current boundary.
- Start with
docs/README.md. - Read the architecture docs before the runtime packages.
- Treat
/specas the normative source of truth. - Use
/fixturesand/conformanceto understand expected behavior. - Read
/internalonly after the contracts make sense.
- Code: Apache-2.0
- Specs, docs, RFCs, and fixtures: CC BY 4.0