TypeScript SDK

The ai-hist package: discover, hydrate, search, and read sessions, tool calls, file edits, and subagent trees from TypeScript.

npm install ai-hist

The SDK is the same engine the CLI and MCP server use: one Rust core loaded as a prebuilt Node-API addon. Every API is async.

Discover, list, read

import {
  discoverSessions,
  hydrateSession,
  listSessionCatalogPage,
  getSession,
  getSessionEventsPage,
  sessionEvents,
  search,
} from 'ai-hist';

await discoverSessions({ scope: 'local', limit: 100 });

const page = await listSessionCatalogPage({ scope: 'local', limit: 20 });
const first = page.sessions[0];

if (first) {
  const { source, sessionId } = first;
  await hydrateSession({ source, sessionId, scope: 'local' });

  // Session ids collide across providers, so every read names the source too.
  const prompts = await getSession(sessionId, { source });
  const events = await getSessionEventsPage(sessionId, { source, limit: 200 });

  for await (const event of sessionEvents(sessionId, { source })) {
    // walks every page without holding the whole transcript in memory
  }
}

const matches = await search('authentication', { scope: 'all' });
CallWork it does
listSessionCatalog / listSessionCatalogPageCache only.
discoverSessionsShallow discovery: refreshes catalog metadata.
hydrateSessionFull indexing of one cataloged session. Returns hydrated, updated, unchanged, or capability_limited.
syncFull ingestion of every selected source.

Reads on a missing database return empty results; they never trigger provider I/O.

Tool calls and file edits

Each comes as a page, an iterator, and a collecting convenience, and takes a source and a session id:

import { getSessionToolCallsPage, sessionToolCalls, sessionFileEdits } from 'ai-hist';

const tools = await getSessionToolCallsPage('claude', sessionId, { limit: 200 });

for await (const call of sessionToolCalls('claude', sessionId)) consume(call);
for await (const edit of sessionFileEdits('claude', sessionId)) consume(edit);

A source outside the known set throws InvalidArgumentError rather than reading as an empty session. Use CATALOG_SOURCES and isCatalogSource() to validate input without copying the provider list.

Subagent trees

import { getSessionRelationships, getSessionTree, sessionEventsIncludingDescendants } from 'ai-hist';

const { asParent, asChild } = await getSessionRelationships({ source: 'codex', sessionId: rootId });
const tree = await getSessionTree({ source: 'codex', sessionId: rootId, maxDepth: 8 });

for await (const event of sessionEventsIncludingDescendants({ source: 'codex', sessionId: rootId })) {
  // event.sessionId is the session that produced it, never rewritten to the root
}

See Sessions for observed versus unlinked relationships.

Scopes

Collection and acquisition calls, including hydrateSession, take scope: 'local' | 'remote' | 'all', defaulting to 'local'. Hydrating a remote-only session needs scope: 'remote' or 'all' and a registered plugin. Results echo the requested scope, discovery reports locationsRun (what actually executed), and each session row lists the locations it was observed in. See Remote sources to register plugins.

Handoffs

createHandoff() and resumeHandoff() are the SDK form of the MCP handoff tools. See Handoffs.

From Rust

Rust programs depend on the ai-hist crate and use SessionStore::open, sync, sessions, session, and changes_since — no raw database connection. See the embedder guide.