History stays local. ai-hist export writes a selected slice of it as NDJSON
built from versioned Rust evidence records, for your own programs to consume.
Create an explicit selection file:
{
"all_sources": false,
"sources": ["claude", "codex"],
"sessions": [],
"kinds": ["history", "session_event", "tool_call", "file_edit", "presence"],
"excluded_sessions": []
}Sources and individual session identities form a union. Select every source
with all_sources: true; evidence kinds must still be explicit. A session in
excluded_sessions is left out, and so is a relationship that names it at
either end. Each record is one stored row: payload holds every column as
SQLite stores it except revision (reported beside it) and
session_events.raw_facts_version (the local parser generation), record_id
is the SHA-256 of the change feed's key for the
row, and revision is the row's change-feed revision, so an export and the
feed name a record the same way. origin_id is the store's change-feed epoch.
ai-hist export --selection selection.json > history.ndjson
ai-hist export --selection selection.json --out history.ndjsonStdout contains one complete JSON record per line, with errors on stderr. File output is replaced only after a complete export. A successful pipe means export completed; it does not mean the downstream consumer accepted the history.
import { exportHistory } from 'ai-hist';
for await (const record of exportHistory(selection, { dbPath })) {
await consume(record);
}The iterator reads one consistent snapshot: the store exactly as it stood when
the export began, whatever is written, rewritten or deleted while it runs. It
closes the snapshot when finished or stopped. For paging, use
beginHistoryExport, readHistoryExportPage, and closeHistoryExport. The
cursor of the page just read serves that page again until the next page is
read, and a snapshot lives in the process that began it until it is closed or
expires (one hour by default).
Abandoned snapshots expire.
A page holds 100 records and at most 1 MiB. Pass publishedLimits — a
delivery receipt, or the body of GET /v1/delivery/limits — and a positive
integer maxRecords becomes the record cap (never above 10,000). The byte
cap stays 1 MiB: maxRequestBytes is the server's decoded HTTP body, not
the page's JSON. exportLimitsFromPublished is that same choice without
opening a snapshot. An explicit limits value is used as given.