Stately
PackagesAgent

Statechart policy examples

Three offline examples put consequential decisions in visible machine states. Each exports its machine and a runner accepting RunAgentOptions. The consensus-review runner takes patch instead of input, because the host, not the caller, decides whether a patch is trusted. Replace the scripted generateText executor with an SDK implementation; override booking actors through native XState actors bindings.

ExampleMachine policyVerification
consensus-reviewThree concurrent reviewers, two approvals required, failures abstain, external patches always reach a human, human fallbackConcurrent starts, reordered completions, malformed votes, an adversarial external patch, JSON restoration, native XState host parity
booking-compensationHuman approval before reservations; confirmed hotel unavailability compensates the flight; uncertain outcomes require reconciliationNo reservation before approval, compensation order, compensation failure, uncertain outcome, success/cancel paths
deadline-escalationMatching request identity and host timestamp determine whether approval or expiry is acceptedDeadline equality, stale identities, early expiry, late approval, JSON restoration

Run without provider credentials:

pnpm tsx examples/consensus-review/index.ts
pnpm tsx examples/booking-compensation/index.ts
pnpm tsx examples/deadline-escalation/index.ts

The first CLI accepts three scripted votes. The second restores an approval checkpoint and demonstrates compensation using simulated bookings. The third restores a checkpoint and delivers a simulated scheduler expiry. Tests also run the alternate outcomes.

Reviewer quorum

Anthropic's voting pattern motivates independent reviews aggregated by policy. This example uses a two-of-three approval threshold, not unanimity: a security dissent can be outweighed. Change the machine policy if a particular reviewer must veto. Model votes do not prove a patch is safe.

Trust is a machine rule, not a prompt instruction. The patch text is embedded in all three reviewer prompts, so a patch the host did not author is untrusted input. Input carries source: "trusted" | "external" with no default. Host code decides it, never the caller who supplied the patch: the example runner marks only its built-in patch "trusted" and any patch passed in "external", and it does not let a caller set source. The counting choice state routes any "external" patch to humanReview regardless of the tally, so model votes can only auto-accept trusted input, and the interaction label tells the human why they were asked. A native XState host parses no input schema and supplies source itself.

Each reviewer is a named parallel region invoking the same typed request. No host Promise.all hides the topology. All regions reach a final state even when their request fails; the parent then counts votes. Human review persists both successful votes and abstentions.

The tests execute the same artifact through both runAgent and createActor(provideExecutors(...)). This establishes those host modes' behavior for this example; it is not certification of every model SDK.

Booking compensation

Microsoft's compensating-transaction pattern motivates explicit recovery after partially completed work. The model proposes an itinerary; deterministic states control approval, reservations, and compensation.

stateDiagram-v2
  [*] --> planning
  planning --> approval: itinerary
  approval --> flight: APPROVE
  approval --> cancelled: CANCEL
  flight --> hotel: reserved
  flight --> manualRecovery: uncertain
  hotel --> hotelResult: outcome
  hotel --> manualRecovery: uncertain
  hotelResult --> booked: reserved
  hotelResult --> compensating: unavailable
  compensating --> compensated: acknowledged
  compensating --> manualRecovery: uncertain

All reservation actors are simulated. A real host supplies idempotent implementations keyed by bookingId and operation name. A confirmed unavailable hotel returns { status: "unavailable" }; an exception means the outcome is uncertain and leads to manualRecovery. Flight and cancellation exceptions also require reconciliation. The result preserves bookingId and known reservation references.

Compensation records an attempted business reversal; it does not roll back time. Snapshots cannot guarantee exactly-once external effects. A host must reconcile timeouts and crashes against the provider before retrying or compensating. The test checkpoint is before reservations, not in the middle of a partially committed provider request.

Approval deadlines

LangGraph interrupts provide a useful comparison for persisted human waits. This example adds a scheduler-owned deadline with a machine-owned acceptance rule.

Persist the awaitingApproval checkpoint before scheduling expiry. Store an absolute deadline, use an authenticated host clock for observedAt, and correlate every delivery with requestId. Never trust a client to supply the timestamp. Approval requires observedAt < deadline; expiry requires observedAt >= deadline.

Serialize deliveries per request, or use storage compare-and-swap with conflict handling. Two independent resumes of one snapshot can otherwise produce conflicting outcomes. Retry scheduler deliveries that arrive before the waiting checkpoint exists. Scheduling, authentication, durable storage, and delivery retries belong to the host.

This example is marked manual in the demo catalog because generic chat cannot supply that clock/scheduler contract. Its CLI is fully offline. It exports an ordinary XState machine that a dedicated host can visualize and execute.

Visualization and validation

Every example exposes named states, typed inputs/events, and schema-validated model output. The tests require lintAgentMachine to report no structural errors. These checks establish specific paths and structure; they are not exhaustive safety proofs. Payload-sensitive guards, provider side effects, and concurrent storage require their own verification.

On this page