xstate-statemachine vs @statelyai/agent

An honest comparison of xstate-statemachine's [agents] extra and @statelyai/agent: hierarchy, guards as policy, durable timeouts, human-in-the-loop, persistence, observability, and when to choose which.

@statelyai/agent is Stately’s TypeScript library for agents built on XState and the Vercel AI SDK. Same chart model, different runtime.

This table is generated from docs/_data/comparisons.json, and corrections are welcome as PRs against that file. Every row carries a source note. It was checked against @statelyai/agent 1.1.6 (npm latest, published 2024-09-28; a 2.0 line on XState 6 is in alpha, 2.0.0-alpha.28 on 2026-10-08), 2026-10-10; see @statelyai/agent for the current state. A test (tests/test_battle_291_a.py) fails the nightly comparisons CI job when this check is more than 180 days old, or when the installed release’s major version differs from the one checked, so the table cannot silently go stale (a pull request is never blocked by a competitor’s release).

Feature table

Capability xstate-statemachine @statelyai/agent Source
Hierarchy (nested states) Yes: compound states at any depth, XState v5 semantics Yes (XState v5, TypeScript) @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Parallel regions Yes: type: parallel, done.state join Yes (XState v5) @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Guards as policy Named guards in the chart; run_tool re-checks per-state tool allow-lists (X0.13) XState guards; tool policy is app code @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
after timeouts Yes: durable deadlines for model, tool and human waits, persisted and woken by DueTimerScanner XState after (in-process) @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Human-in-the-loop as durable state awaiting_human is a persisted state with an escalation deadline; approval names exact call ids Model it as a state; persistence is yours @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Persistence / replay Snapshots in SQLite / Redis / SQLAlchemy (e.g. Postgres) stores, versioned layout, transition-log replay() XState snapshots; storage is yours @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Visual editor Opens in the Stately editor (the chart is XState JSON; tool policy in meta, plus one engine key, actionErrorPolicy) Stately editor @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Typed context / events Pydantic context + event schemas ([pydantic]), strict tool-argument schemas TypeScript types + Zod @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Observability JSONL trace with gen_ai.* fields, OTel spans (on_span="otel"), no content by default, secret-key redaction XState inspection @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Multi-agent Reference supervisor / pipeline / debate charts; spawn_agent with per-child and global budgets Multiple agents as actors @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples
Incremental adoption One LangGraph node (statechart_node), a graph as a service (langgraph_service), a pydantic-ai Tool Drop into any XState app @statelyai/agent 1.1.6 (npm): deps xstate ^5.18.2, ai ^3.4.6 (Vercel AI SDK); zod is a dev dependency used in its examples

When to choose @statelyai/agent

  • Your stack is TypeScript / Node and you want first-party Stately tooling.
  • You already run XState in the browser or on Node and want the agent in the same process.

When to choose xstate-statemachine

  • Your stack is Python: the library reads XState v5 JSON and follows XState v5 statechart semantics (event names for invoke/after/done.state keep the v4 spelling), so a chart designed in Stately runs here.
  • You want Python-side persistence (SQLite / Redis / Postgres stores), durable timers via DueTimerScanner, and FastAPI / Starlette / Litestar integration.
  • You want the batteries in this extra: budgets as guards, per-state tool allow-lists re-checked in run_tool, FakeModel, JSONL traces, LangGraph and pydantic-ai interop.

Ours, in 20 lines

This is a support agent. The refund tool is legal only in the tool states and needs a human to approve it, and the whole run is capped at 5 turns and 10 cents. It runs offline with FakeModel:

import asyncio
from xstate_statemachine.contrib.agents import (
    FakeModel,
    load_chart,
    run_agent,
    tool,
    tool_registry,
)


def lookup(order_id: int) -> str:
    """Look up an order."""
    return f"order {order_id}: shipped"


def refund(order_id: int) -> str:
    """Refund an order."""
    return "refunded"


chart = load_chart()  # TOOL_LOOP, plain XState JSON
for s in ("awaiting_model", "awaiting_tool"):
    chart["states"][s]["meta"]["tools"] = [
        "lookup",
        "refund",
    ]  # policy lives in the chart
tools = tool_registry(
    tool(lookup, timeout_s=5), tool(refund, timeout_s=5, side_effect=True)
)
model = FakeModel(
    [
        {
            "tool": "lookup",
            "args": {
                "order_id": 42,
            },
        },
        {
            "tool": "refund",
            "args": {
                "order_id": 42,
            },
        },
    ]
)

res = asyncio.run(
    run_agent(
        chart,
        model=model,
        tools=tools,
        prompt="refund 42",
        budgets={
            "max_turns": 5,
            "max_usd": 0.10,
        },
    )
)
assert (
    res.waiting and res.final_state == "toolLoop.awaiting_human"
)  # durable: persist, resume later

See LLM agents for the full reference, including the LangGraph interop and pydantic-ai adapters.

New to the library? Start with the integrations journey: pick your path, then a fifteen-minute tutorial.