Persistence
Persistence lets a session survive process restarts and gives product surfaces a stable session ID.
A resumed session can repopulate task lists, execution history, artifacts, and delivery summaries without replaying the completed run.
A3S Code persists three related but different things:
File Session Store
Go selects the built-in file store with FileSessionStoreDir; custom
SessionStore trait implementations remain a Rust embedding capability.
Atomic Snapshot Generations
session.save() gathers the current persisted state into one
SessionSnapshotV1 and calls SessionStore::save_snapshot once. The envelope
contains:
schema_versionandSessionData- tool artifacts
- trace events and run records
- verification reports
- delegated subagent task snapshots
The file store writes one complete JSON envelope to a synced temporary file and
atomically replaces <session-id>.json. Readers therefore observe the previous
generation or the next one, not a new conversation paired with old run or trace
fragments. The memory store publishes the same aggregate under one lock. Both
report SessionStoreCapabilities { atomic_session_snapshots: true }.
Historical files remain readable. A bare SessionData file is combined with
the legacy artifact/trace/run/verification/subagent fragment locations during
load, then restored through the v1 in-memory shape. Once a new aggregate is
saved, the single envelope is authoritative. A document that already looks like
an aggregate but has a malformed or unsupported schema is rejected rather than
reinterpreted as legacy data.
Custom stores must implement save_snapshot explicitly. Its default returns an
error; it does not fan an aggregate out into independent writes or silently
acknowledge a no-op. The default load_snapshot exists only as best-effort
legacy assembly, and capabilities() lets a host distinguish that behavior
from an atomic backend.
Resume
resumeSession restores a saved session snapshot. It is not the same as
resumeRun: use resumeSession when the user is continuing a saved
conversation, and use resumeRun(runId) only when a run checkpoint exists for
an interrupted run. Resume validates the snapshot schema before restoring any
history or runtime evidence.
Memory And Sessions
Session persistence stores conversation and replay evidence. Memory stores reusable task facts. Use both when you want resumable workflows that also learn from repeated tasks.
Loop Checkpoints and Run Resumption
When a SessionStore is configured, the agent loop persists a LoopCheckpoint
after each completed tool round. The boundary policy is strict: checkpoints are
taken only between tool rounds, never mid-tool. If a process dies while a
tool is executing, that round's work is lost on resume and the LLM
re-deliberates from the previous checkpoint — re-running a non-idempotent tool
(write, bash) on the wrong side of the boundary is worse than re-asking the LLM.
session.resumeRun(runId) (Node) / session.resume_run(run_id) (Python) /
session.ResumeRun(ctx, runID) (Go) — core
AgentSession::resume_run(checkpoint_run_id) — loads the latest checkpoint
stored under that run ID and replays the loop from the last boundary. Because
the checkpoint lives in the shared store, resume can happen on any node that
shares it. Cumulative accounting continues rather than restarting at zero:
total_usage and tool_calls_count carry forward from the checkpoint. A new run
ID is allocated for the resumed work; the old-to-new relationship is host
metadata, not interpreted by the framework.
Completed runs are not resumed through resumeRun; their final state is
available through runs(), runEvents(runId), artifacts, verification reports,
and session snapshots.
Go also exposes workflow checkpoint orchestration through
ParallelResumable(ctx, specs, workflowID).
resume_run rejects when the session has no sessionStore configured (or when
no checkpoint exists for the given ID). SessionStore gains
save_loop_checkpoint / load_loop_checkpoint / delete_loop_checkpoint; the
file store's writes are crash-atomic. LoopCheckpoint::ensure_loadable() is
called right after deserialization and rejects checkpoints from a future,
incompatible schema version, so neither resume_run nor the live-run sink acts
on an unreadable checkpoint.
See CHANGELOG [3.3.0] — "Loop checkpoints + run resumption" — and [3.4.0]
"LoopCheckpoint::ensure_loadable()".
Workflow Checkpoints
WorkflowCheckpoint is the step-boundary analogue of the tool-round
LoopCheckpoint, one level up: it journals completed orchestration steps so an
interrupted workflow resumes from the longest completed prefix. Its fields are
schema_version, workflow_id, steps, and checkpoint_ms, with the schema
pinned by the WORKFLOW_CHECKPOINT_SCHEMA_VERSION constant. A resuming run skips
the recorded steps and re-dispatches only the rest.
SessionStore gains save_workflow_checkpoint / load_workflow_checkpoint /
delete_workflow_checkpoint (default no-ops; the file store writes
crash-atomically). Loads from a future, incompatible schema version are rejected
via WorkflowCheckpoint::ensure_loadable().
This pairs with the orchestration grammar — see Orchestration and Multi-Machine.
See CHANGELOG [3.4.0] — "WorkflowCheckpoint".
Resuming on a Different Node
Both checkpoint types are serializable. Combined with a shared SessionStore
and a pluggable executor, this lets a host resume an interrupted run or workflow
on a different node from the one that started it — the framework owns the
serializable contract, the host owns placement and transport.
Operational Notes
Keep session stores out of public commits when they may contain prompts, tool output, or private file paths.