| title | Where state lives |
|---|---|
| description | The two durable artifacts an agent run produces, what each one holds, and how to restore from them. |
Alpha:
@statelyai/agent2.0 is in alpha. APIs can change between releases; pin an exact version. Feedback: github.com/statelyai/agent.
This page describes what an agent run stores durably, where each artifact lives, and how to restore from it.
An agent run produces two artifacts worth writing to disk, plus run state that exists only while the process does.
- The event log is an ordered array of every external input the machine received. It is the only artifact a run can be fully reconstructed from.
- The persisted snapshot is the machine's serialized state at one quiescent point. It lets a resume skip replaying the log from index 0.
- In-memory run state is the live actor, in-flight effects, and stream chunks. It is derived and disposable.
runAgentstops its actor on every settle path, so no durable artifact depends on it.
The log is the source of truth. A snapshot is a cache over the log and can be discarded at any time.
The table covers the two durable artifacts plus the run state that is not durable. Messages and stores are not artifacts and are described after it.
| Artifact | What it holds | When written | Survives | Restore with | Full guide |
|---|---|---|---|---|---|
Event log entries (AgentLogEntry[]) |
One envelope per external input: the @agent.init event, effect completions and errors with outputs inline, externally sent events, timer firings, plus identity, timestamp, machine version, and verification hashes |
Per accepted external input, via result.events, onEvent, or a step-path append |
Process death, redeploy, machine-version change, forking | replay(machine, entries), or runAgent(machine, { events }) with no snapshot |
The event log |
| Persisted snapshot | The machine's serialized state at a quiescent point: state value, context, and restored pending invokes | At a settle point (result.snapshot) or from an actor's getPersistedSnapshot() |
Process death, redeploy, days of waiting | runAgent(machine, { snapshot, event }) |
Human in the loop |
| In-flight effect state | Which model calls are owed right now | Not written directly. It is implied by the log's recorded completions | Process death, through the log only. A mid-flight snapshot cannot carry it | replay, which re-derives owed effects including still-owed dynamic spawns |
The step path |
Messages and any other accumulated data live in machine context, so they ride inside both artifacts. There is nothing separate to store or restore. See Messages.
Stores are where the two artifacts land. See Stores below.
Two rules follow from the table:
- Snapshot only at quiescent points. A mid-flight snapshot cannot carry in-flight effect state, but the log can. Compact at idle, then resume from the snapshot plus the entries appended since.
- Keep context JSON-serializable. Both artifacts round-trip through
JSON.stringify. Keep sessions, database clients, and sockets in closures, and store only their ids.
Two store protocols are defined as interfaces, so a userland store interoperates with the library.
AgentEventLogStoreis append-only with optimistic concurrency on log length. It hasappend({ threadId, expectedIndex, entries }),read,length, andfork. A stale writer fails withAgentEventLogConflictError. See the store contract.AgentSnapshotStorehasload(id)andsave(id, snapshot). It is a key-to-JSON upsert.
Shipped implementations:
| Store | Import | Notes |
|---|---|---|
| In-memory event log | createInMemoryEventLogStore() from @statelyai/agent |
The reference implementation and conformance baseline |
| SQLite event log | createSqliteEventLogStore({ database }) from @statelyai/agent/sqlite |
Node's built-in node:sqlite, no dependencies, Node >= 22.18 |
| SQLite snapshots | createSqliteSnapshotStore({ database }) from @statelyai/agent/sqlite |
Shares one DatabaseSync handle with the event log store |
Both SQLite stores take a file path, ':memory:', or an existing handle. They create their tables on demand. Postgres and Redis adapters are on the roadmap and are not shipped. To use another database, write a store against the protocol and check it with assertEventLogStoreConformance.
Each recipe is written up in full on its owning page.
- Crash recovery: resume from the log alone. Recorded model calls are replayed instead of re-executed. See log-only resume and append before continue.
- Resume with an event: run to idle, store the snapshot, then load it later and deliver the human's event. See persist and resume across processes.
- Time travel:
replay(machine, entries.slice(0, n))rebuilds the state as of any point in the log without executing anything. See crash recovery and resume. - Fork and branch:
store.fork({ threadId, newThreadId, upToIndex })copies the prefix[0, upToIndex)onto a new thread.diffEventLogsreports what diverged. See fork and diff. - Verify a log:
replay(machine, entries, { verify: 'strict' })requires verification hashes on every entry and fails at the first mismatch. See strict replay verification.
Runnable versions: crash-recovery and time-travel for the log recipes, file-snapshot-store for a store written against AgentSnapshotStore, session-actor for one live actor across turns on a single log, and snapshot-migration for resuming a paused run after the machine was redeployed.
- The event log: the log envelope, the JSON wire contract, and the SQLite stores.
- Human in the loop: idle states, which are the compaction points.
- The step path: owning the loop and persisting between model calls.
- Choosing a run mode: which artifacts each mode produces by default.