FAQ
Common questions about what agentsessions is, what it is not, and how it behaves. See
Concepts for the model and Architecture for the
implementation.
What is agentsessions in one sentence?
Section titled “What is agentsessions in one sentence?”A neutral, open contract and working reference implementation for durable agent sessions: create, run, suspend, resume, fork, and replay an agent session, with a bring-your-own-harness SPI on top and a pluggable compute backend underneath.
How is this different from LangGraph, Temporal, or Durable Task?
Section titled “How is this different from LangGraph, Temporal, or Durable Task?”It is a different layer, not a competitor. Those are workflow or durable-execution frameworks you build
an application inside. agentsessions is the neutral session contract that sits between a producer (what
starts and drives sessions) and a compute backend (what runs them). It defines the wire form and the
determinism guarantees, and it composes with durable engines: a Durable Task or Dapr backend can sit
underneath as an event-log or state store. You do not rewrite your agent to adopt it; you implement a
thin harness adapter.
Do I need Kubernetes?
Section titled “Do I need Kubernetes?”No. The default backend (runtime/local) is filesystem-only and runs entirely on your machine, which is
what the quickstart uses. Kubernetes and agent-substrate are one compute backend among
several (pod, Kata, Cloud Hypervisor, agent-substrate). Compute is pluggable through the Runtime SPI,
and the core knows about none of these specifically.
Why replay instead of always snapshotting memory?
Section titled “Why replay instead of always snapshotting memory?”Because replay runs anywhere, including a plain pod with no snapshot support, and costs nothing special.
A session’s truth is its durable log, so a killed session is reconstructed byte-for-byte by replaying
that log, invoking the model zero times. Memory snapshot is the opt-in tier for harnesses whose state
genuinely cannot be replayed (a live REPL, a browser, a warm process). Those declare
REQUIRES_MEMORY_SNAPSHOT and are scheduled only on memory-capable compute. You pay for the heavier
capability only when you actually need it.
What is a fence, and why does it exist?
Section titled “What is a fence, and why does it exist?”A fence is a monotonic token bound to a live incarnation (a running sandbox). The event log is the fence authority: a new incarnation mints a higher fence, and the log rejects any append carrying a lower fence. This is how a session with automatic failover still has exactly one writer. If a supposedly-dead incarnation wakes up and tries to write (a zombie), its stale fence is refused. Fencing plus compare-and- swap on append is what makes “single writer” enforceable rather than a hope.
Can Exec, Suspend, and Resume overlap?
Section titled “Can Exec, Suspend, and Resume overlap?”Across all Placers in one Registry, overlapping calls for one session return gRPC ABORTED without
changing compute or the journal. Suspend will not interrupt a running turn. Retry after the active call
finishes, using the current cursor for Exec. Other sessions remain independent. A standalone Placer
has its own guard; separate Registries or hosts are not fenced by this local guard.
Is the provenance chain Go-specific?
Section titled “Is the provenance chain Go-specific?”No. The content_hash is RFC 8785 JCS (JSON Canonicalization Scheme) computed over the proto3-JSON form
of each event, not over any language’s serializer. A roughly 30-line Python verifier
(hack/verify_chain.py) reproduces the exact Go hash and audits a full journal, and hack/verify_chain.sh
demonstrates it end to end, including rejecting a tampered record. Any implementation or auditor can
verify the chain.
What model providers are supported?
Section titled “What model providers are supported?”It is model-agnostic. The harness selects a model by id and the host mediates the call; the core has no
provider-specific code. Opaque provider reasoning parts are carried in one neutral ReasoningPart type
and replayed verbatim, which was validated across Anthropic, OpenAI, and Gemini in the reasoning-
continuity work. Because the harness routes model calls through the sink and not a provider SDK, swapping
providers does not change the replay path.
Can I bring my existing agent framework?
Section titled “Can I bring my existing agent framework?”Yes. Implement the api.Harness SPI (two methods, Describe and Run) as a thin adapter over your
framework. A Microsoft Agent Framework agent, a GitHub Copilot agent, a LangChain loop, or a custom agent
all plug in the same way. See Writing a harness.
What happens if the harness crashes in the middle of a turn?
Section titled “What happens if the harness crashes in the middle of a turn?”Use Resume to re-drive an interrupted execution. For an existing current approval-bearing execution,
no-input Exec also continues that recorded invocation, ignoring request config/harness/cursor
overrides. Other inputless Exec calls keep ordinary new-turn behavior. Host-executed tools carry an
idempotency key; a durable executor must deduplicate on session UID plus that key and retrieve the
original receipt after a crash. The journal alone does not make external effects exactly once.
What is fork actually for?
Section titled “What is fork actually for?”Fork makes a session a branch point. Four patterns the implementation supports today:
- Parallel exploration from identical state.
Fork(count: N)takes one checkpoint and clones N children from it, so a fleet of agents branches from exactly the same base instead of N separate re-derivations that can drift. Run several strategies against one task, keep the best, discard the rest. - Reuse a warmed base session. A session that has already installed dependencies, cloned a repo, or built an index can act as the base for many later jobs, paying that setup once rather than per job. Fork skips re-deriving the state; it does not make copying it free, and where that beats a cold boot depends on setup cost versus snapshot size, which is not yet measured.
- Retry from before a bad turn. For a
STATELESS_REPLAYharness--at Nmay name any historical seq, so a session that went wrong at turn 12 can branch at turn 11 and take a different path without discarding the work before it. - Auditable A/B. Each branch is its own hash-chained log carrying a
FORKmarker that names its parent and seq. Two branches differing only in a prompt or a model stay independently verifiable, so a result traces back to the exact branch that produced it.
The cost is real: no runtime forks copy-on-write today, so an N-way fan-out is N restores rather than N references to one image.
What are fork semantics?
Section titled “What are fork semantics?”fork --at N creates a child session that inherits the parent’s log up to seq N, records a FORK
lifecycle marker, and then evolves independently. Because each session is a hash-chained log, a fork is
a branch in a verifiable hash tree.
What happens to compute depends on the harness’s declared resumability:
STATELESS_REPLAY— the child gets a fresh incarnation and the host replays the inherited prefix into it. The parent is untouched, and--at Nmay name any historical seq.REQUIRES_MEMORY_SNAPSHOT— the child’s RAM has to come from somewhere, so the parent is first checkpointed: it is suspended, its worker is freed, and aSUSPENDevent is appended to its chain. Each child is then cloned from that snapshot. The parent is recoverable withresume, but it is not untouched. A memory snapshot captures RAM as of now, so forking at a historical seq is refused.
A child inherits the parent’s project, harness and model, and records parent_uid and fork_seq so
its lineage survives beyond the fork response. It does not inherit the parent’s name: pass
--names (ForkRequest.child_names) to label children, or leave them unnamed and render them by
uid. See concepts for why the server does not invent one.
No runtime forks copy-on-write today. On substrate each child is a full snapshot restore, so an N-way fan-out costs N restores rather than one shared image — see substrate conformance.
How do tool calls work, and can I gate sensitive ones?
Section titled “How do tool calls work, and can I gate sensitive ones?”Each tool declares a mediation tier. IN_HARNESS_REPORTED (the default) runs the tool in the sandbox and
reports the result for audit. CONTROLLER_MEDIATED has the host execute the tool so it can apply authz,
policy, and audit centrally in a custom executor. REQUIRES_APPROVAL parks a stateless-replay execution
for an explicit caller decision. RPC/SDK Approve commits only that decision; call Resume separately.
CLI approve and deny do both, and say “decision committed” if recovery fails. Get/List and final paused
Exec frames expose the pending tuple at their captured cursor. A decision clears that reference, but
new input stays blocked until its receipt commits. Identity is not authorization; there is no policy
hook, timeout or expiry. Snapshots and unresolved requests are retained indefinitely, and Cancel and
DeleteSession remain UNIMPLEMENTED. Tool calls align with the MCP shape (name plus structured
arguments), but the stock daemon has no executor and the bundled model adapter does not map tools;
this is not a complete MCP/provider integration. See
approval recovery.
How do I list sessions, and can I trust the compute state I get back?
Section titled “How do I list sessions, and can I trust the compute state I get back?”ListSessions reads a metadata row written at create time, not the event log, which is why a session
with no turns and no compute still appears and why a listing is complete after a process restart. It
filters on an exact project and pages with a cursor over (create time, uid), so a session created
while you are walking pages neither skips nor duplicates a row.
last_seq is read straight from the log and is exact. compute_state is not a live probe: it records
what the log implies about compute. Any ordinary event moves a session to LIVE, since something had
to be running to produce it; SUSPEND and FORK land it COLD. So LIVE goes stale if the process
behind it later died, and a session that was created but never run stays NONE. Treat it as recorded
intent. Making it exact needs the backend to enumerate its own incarnations, which the Runtime SPI
does not yet expose.
Is it production-ready?
Section titled “Is it production-ready?”It is a working reference implementation (Go 1.26), not yet a productized service. The Sessions API, the
single-writer event-sourced controller, the durable hash-chained log, bring-your-own-harness over
Harness.Connect, the Runtime SPI with local and substrate backends, the replay-conformance suite, and
real-substrate conformance across both capability tiers all run today. Session-level suspend and resume
use the Placer for both resumability tiers. See the
Substrate verification boundary for live-test coverage
and limitations. There is no authentication, authorization, or transport security, and no managed
control plane, enterprise identity and provenance, or confidential and GPU snapshots. See the Status
section of the root README.
Where is the wire format?
Section titled “Where is the wire format?”In api/*.proto (Sessions, Harness, Runtime, and the shared Event). The hand-written Go SPI in
api/*.go is what hosts and backends code against. One neutral wire form, one Go SPI.
How do I run it on real agent-substrate?
Section titled “How do I run it on real agent-substrate?”See Running on agent-substrate. It runs in CI across both capability tiers
(stateless-replay and memory-snapshot continuity, both on a micro-VM), against a real ate-system in
kind, with the core importing zero substrate code. On a host without KVM, both tiers can run on gVisor
instead.