These examples use one XState machine artifact across different model SDKs and hosts.
Sections group examples by what the machine proves, not by which provider they happen to call.
- email-drafter: typed requests, a Jev completeness judgment, revision, approval, and sending
- plain-xstate: an ordinary XState machine that knows nothing about the agent library
- port-walkthrough: the
generateTextloop from Migrating from a hand-rolled loop, ported with the same result type and ending on its tests - retrofit: the same agent as a
while(true)loop and as a machine, refactored step by step, ending with Jev triage - retrofit-parity: a
generateTextloop with two tools and one approval, recorded withcreateRecorder, replayed against its machine port withcompareRecordings, and compared withcompareRunsand one reviewed difference - strangler-fig: an async workflow wrapped unchanged with
wrapWorkflow, then its classify retry carved into states, withcheckParityagainst the original at each step and acanReachproof the wrap could not give - harness-governor: a machine beside a tool loop it does not run; each tool call is an event, a result is quarantined until classified, and
canReachproves web search is unavailable once the session has seen confidential data - json-agent: the whole workflow authored as a
.jsonfile and lowered to the same machine - triage: Jev classifies category and sentiment and escalates when unsure, with one retry when the draft fails
self-improving-refunds is the worked, experimental example of Environment and policy: a refund desk whose rules are fixed guards, and a JSON playbook the agent improves between batches.
- Environment only: verification, booking-compensation, human-in-the-loop
- Policy with a judgment inside: triage, joke
- Both: self-improving-refunds, code-review-loop, tic-tac-toe
Two more examples improve a policy the same way:
- code-review-loop: the review gates are the environment and the triage policy is the part that improves. It runs on Claude Code in headless mode.
- tic-tac-toe: the game knows nothing about AI, the agent plays O at its boundary, and each promoted strategy must be proven never to lose.
Each entry names the construct that makes the behavior structural rather than a convention in a prompt.
- twenty-questions: a guard makes the final turn a
GUESS— the model cannot spend it on a question; Jev reads the human's yes/no replies - guardrails: input and output checks as separate states, each a Jev yes/no judgment; an unsupported answer is flagged, not returned
- joke: a Jev score rates each joke on a five-level rubric, and the model itself decides whether to loop again, through
agent.decideunder a guard - reflection-writer: a revision budget in context, compared against a constant in a guard
- code-assistant: a retry budget around code that really runs and really fails
- corrective-rag: Jev grades each retrieved document with one probability, and a grade-then-branch choice state picks answering or a rewritten fallback lookup
- plan-and-execute: a step budget that can be exhausted — the run ends in
failedinstead of answering from a half-executed plan - river-crossing: the machine is ground truth and solves itself —
REQUEST_PLANtraverses it withxstate/graphand hands back the shortest legal route, and guards reject any crossing the model proposes off it - route-replanning: a plan is a prediction — the machine plans the whole route, drives it a leg at a time without recomputing, and replans from where it stands when a road turns out to be shut
- todo-nl: one free-text command becomes a bounded sequence of typed events via an explicit decide loop
- context-compaction: an explicit
compactingstate, entered when the window overflows - chat-with-pdf: one question per state entry, a refresh guard instead of "after 3-4 questions", and a Jev verdict on each answer
- game-agent: rock-paper-scissors where the event log saved in context is the agent's only memory
- game-loop-agent: an invoked agent that receives pushed events and can act only on its own turn; Jev reads the human's free-text round reply
- generate-and-repair: three candidates fanned out as concurrent invokes, judged by the host's own parser, with a repair round the machine caps explicitly
- adaptive-rag: a Jev router picks the index and Jev grades the documents and the answer, then choice states check
MAX_REWRITESandMAX_REGENERATIONSon both the rewrite loop and the regenerate loop, where LangGraph relies onrecursion_limit - agentic-rag: the model picks
RETRIEVEorANSWERthroughagent.decide, Jev grades each passage, and a guard rejectsRETRIEVEonceMAX_RETRIEVALSis spent, so an answer forced by the budget lands infailed - reflexion: the critique drives real searches and each revision must cite what came back; citations to passages the run never retrieved are dropped, and a revision budget caps the loop
- tree-of-thoughts: a hand-written scorer rechecks every proposed Game of 24 step and rejects any that uses a number not on the table; a beam capped at
BEAM_SIZEandMAX_DEPTHends infailedwhen no line reaches 24 - self-discover: Jev judges each reasoning module with a yes/no question; a choice state keeps the top
MAX_SELECTED_MODULESaboveMODULE_THRESHOLDand fails with a notice naming the threshold when none clears it, a check the LangGraph chain leaves to trust - prompt-chaining: the punchline gate is a choice state over a pure check, and a failed check regenerates at most twice before landing in
failedinstead of ending silently - data-enrichment: a choice state, not the model, decides the record is complete; the reviewer is one Jev yes/no question per field, its feedback is built from the fields no passage backs, and gap searches and rejections share one
MAX_LOOPSbudget that ends infailedwith the partial record - tnt-llm: one summary child spawned per document, then a batch index in context drives generate, update per batch, and review through choice states, with a category cap
- tool-retrieval: a Jev rerank (one probability per registry tool) picks the tools, and a guard lets
agent.decidecall only those, with tool-call and reselection budgets forcing an answer - model-fallback: a validator actor plus a choice state send a rejected cheap-model tool call to a stronger model exactly once (
MAX_FALLBACKS), thenfailed - project-planner: the model only proposes tasks; the machine checks the dependency graph (with a repair budget) and computes the critical-path schedule, Jev rates the risk, and a choice state replans against the deadline until
MAX_REPLANS
Every pause here is a first-class idle state with typed meta.interaction, so
it survives a snapshot round-trip instead of living in a closure.
- human-in-the-loop: the base shape — an idle review state, a
REJECT-with-feedback redraft loop bounded by a counter, and a real JSON round-trip - review-tool-calls: who owns the tool loop — the machine gates each proposed call behind
APPROVE/EDIT/REJECT, or the AI SDK runs the whole loop undermaxStepsand the machine only appends the messages it returns - customer-support: one Jev call routes safe questions past the gate and sensitive actions into it, selecting the confirmation code and new flight from candidates found in code rather than generating them
- answer-reuse: Jev checks the help article the search already found before any agent call, reusing it at
yesand confidence0.8or higher, otherwise investigating exactly once, rechecking, and waiting for a person when the answer still fails - sql-agent: the model plans the query, a human approves, and only then does the engine run it
- long-running-onboarding: pauses measured in days, with a bounded resend loop and an escalation path off every wait
- machine-as-tool: the whole machine behind one tool call, where the handle is the persisted snapshot and the policy check is a Jev judgment backed by a hard limit in code, so a wrong verdict only costs a human review
- info-gathering: a choice state checks the four requirement slots itself (the model only extracts), forces one confirmation turn, and ends in
failedafterMAX_TURNSanswers - long-term-memory: the memory store is machine input and output, recall is a Jev relevance judgment per memory and a capped save is its own state, and a second run started from the first run's output remembers
- feynman-tutor: an idle explain-back state per checkpoint; Jev scores the explanation against a rubric, and a choice state re-teaches below
PASS_SCOREwhile a per-checkpointMAX_RETEACHESbudget lasts, then records the checkpoint failed and moves on
- hierarchical-teams: a coordinator over two child machines, each with its own budget
- swarm-handoff: the model chooses
HANDOFFunder a guard, and the active agent survives a snapshot round-trip - deep-research: dynamic fan-out: a researcher spawned per query, results reduced as they land, and a Jev sufficiency judgment deciding whether to run another round
- parallel-streams: two regions streaming at once, each chunk tagged with the request it came from
- just-one: isolated parallel regions whose inputs are fixed before any sibling settles; the duplicate-cancelling rule is a pure function, not an instruction
- chameleon: hidden information enforced by request input shaping — the chameleon's request provably never carries the secret word
- agent-supervisor: a flat supervisor routes through
agent.decideunder guards: each worker is capped at two reports,FINISHis illegal until one lands, and a turn budget ends infailed - map-reduce: LangGraph's
Sendfan-out as one spawnedwriteJokechild per subject, reduced as they land, with a capped width and a Jevchoicejudge whose labels are the landed jokes; a choice state still rejects a label that names no joke - llm-compiler: each task's dependencies are the
$Nreferences in its args; a choice state rejects references to later or missing tasks before anything runs, each wave of ready tasks is spawned together, and a task's inputs are filled in only after the tasks it references finish - storm-writer: one interview child machine spawned per editor with a turn cap, transcripts reduced as they land; editor, turn and section overflow is dropped and counted against exported constants
- multi-agent-debate: turn order and round count are machine edges, with a
MAX_ROUNDSceiling in the input schema; no speaker can end the debate or speak out of turn, and a Jev judge picks the winner
- crash-recovery: events-only recovery by folding the log; no snapshot is persisted
- snapshot-migration: a run paused before a deploy and resumed after it, through XState's native
version/migratecontract - portable-xstate-loop: the durable transition/effect loop written by hand, with no Agent runner
- file-snapshot-store: the same machine two ways — persisted to a JSON file between processes, and kept alive in one long-lived
createActorthat persists nothing
These are not machine showcases. They hold the machine constant and vary
what surrounds it, to show the control flow is portable. Most need a provider
key or a specific runtime, so they set manual: true and are not exported from
index.ts.
- ai-sdk-host: Vercel AI SDK tool calls mapped to legal machine events for a narrated combat turn
- ai-sdk-ui-stream: the AI SDK v7 UI message stream protocol — a text lane per request plus live machine state, to an unmodified
useChat - tanstack-ai-stream: the same run as AG-UI server-sent events
- anthropic-sdk-host, openai-sdk-host: the executor contract against the raw provider APIs, with no AI SDK in between
- langchain-host, mastra-host: coexistence with a framework — the framework makes the text-model calls, Jev the judgments, the machine owns legality
- cloudflare-agent-host: a Durable Object whose SQLite event log is the source of truth
- cloudflare-workers-ai-host: a provider with no native tool calling, so the legal events are serialized into the prompt
- next-host: controlled mode across a stateless Next route handler
- mcp-workflow: a coding agent drives a bug-fix machine through MCP tools; review events are tagged
actor: "human", so only a person sends them
- verification:
canReachproves a violation state unreachable across every branch, without one model call - braintrust-evals: evals over typed output, transition trajectories, named request calls, and usage
- ai-sdk-evaluator-optimizer: the evaluator-optimizer loop as explicit states, with a Jev score and aspect checks gating the exit
- chatbot-simulation-eval: a simulated customer and the bot under test alternate under a
MAX_EXCHANGESchoice guard, the bot's request input provably omits the persona, and a Jev judge (a policy yes/no plus a quality score) runs inside the machine - essay-grader: each pass is a Jev score over a five-level rubric; three choice states over exported score thresholds stop grading at the first weak pass, and the result names the stage it stopped after
- rank-candidates: Jev decides, code sorts — a Jev score per candidate against a brief and a yes/no per shortlisted pair, while the threshold, sort, per-bucket quota and top-k cut-off are plain code, and every cut carries a reason
- code-review-loop: generate, then a self-check and an independent review in parallel, then checks, then ship, retry, or ask a person. The loop is a fixed machine, and
canReachproves it never ships without passing checks and approval. The triage policy is JSON improved from event-log traces against hidden tests, and it runs on Claude Code in headless mode - tic-tac-toe: a plain XState game where
decideNextpicks O's move. A JSON policy over low-level board features climbs from "first empty cell" to a policycanReachproves never loses to any opponent, gated byverifyAgentConfigand an objective that counts losing lines first - self-improving-refunds (experimental): the agent rewrites its own playbook, a JSON machine gated by
verifyAgentConfigand a held-out replay before promotion, while the desk's business rules stay guards it cannot touch and anillegallyIssuedsentinel stays provably unreachable
- consensus-review: two-of-three reviewer approval (each vote a Jev approve/reject/abstain choice), abstentions, and human escalation
- booking-compensation: approval before effects, compensation, and uncertain-outcome reconciliation
- deadline-escalation: scheduler-driven approval deadlines and stale-event rejection;
manual: truebecause it needs a two-phase CLI and a trusted host clock on top of the provider key
All three run against a real model with OPENAI_API_KEY=... pnpm tsx examples/<name>/index.ts; their tests script the model by request name through the AI SDK's mock model. See Statechart policy examples for sources, tests, and host contracts.
Every example follows these. A new example that breaks one is probably wrong.
- Interaction metadata uses the exported
interactionMetaSchemaas the machine'smetaschema, and is read back withgetInteraction(snapshot)— never a hand-rolled copy of either. - Branching is a
type: "choice"state with guards, not anifinside anonDonehandler or a helper function. A branch has to be in the machine to be inspectable. - Every loop is bounded by a counter in context compared against an exported constant in a guard.
- Exhausting a budget lands in a
failedfinal state. Degrading todonewith an empty or partial output is a bug, not graceful handling. - Every
invokehas anonError. Theinvoke-without-on-errorlint code must stay clean. - Tests mock the model with
examples/mock-model.ts: the AI SDK'sMockLanguageModelV3behind the realcreateAiSdkExecutorsadapter, with answers keyed by request name (thesetupAgent({ requests })key). The helper is repo-internal and not published. A hand-written mock executor routes onrequest.nametoo. Never on a prompt substring, a call index, a positional array, orrequest.model— those pass while silently exercising the wrong request. - Tests mock the judge with
examples/mock-judge.ts: an object that implements the AI SDK's decision-model spec, passed wherever an example takes its judge model, with answers keyed by question id (the key in thequestionsmap). It runsexperimental_decide's real validation over scripted answers. Repo-internal and not published, likemock-model.ts. - Context is replay-stable: no
Date.now(),Math.random(), orrandomUUID()in context, and no module-level mutable state. Pass stores and executors in, or use a factory function. - Derived strings are computed in
outputor the host, not stored in context. Boolean mirrors of final states and sentinel values ("",-1) are replaced by states, or byoutputderived fromsnapshot.matches. - Event names are facts or commands from the human or host. Choices the
model makes go through
agent.decideand are filtered by guards. - Sibling imports are allowed — an example may import another example's
machine — but the only shared test helpers are the
examples/mock-model.tslanguage-model double and theexamples/mock-judge.tsdecision-model double. Each example's test stands on its own.