TypeScript MIT

agent

Create state-machine-powered LLM agents using XState

S

statelyai

Dernière activité 28 sept. 2026
statelyai/agent

467

étoiles

21

forks

7

issues ouvertes

agentsaillmstate-machinestatechartworkflow

Ce README est souvent en anglais.

Stately Agent

Make invalid agent actions impossible.

Agent logic as state machines: deterministic, inspectable, resumable, runs anywhere. The machine owns control flow; the model only ever picks a legal event. Testing, inspection, and visualization fall out for free.

Stately Agent adds model requests and decisions to XState:

  • The machine defines what the agent can do.
  • Your application chooses the model, runs the requests, and stores the state.
  • The model proposes an event; the machine decides whether it is allowed and what happens next.

Stately Agent 2 is in alpha. APIs may change before the stable release.

Documentation · Examples · XState

Three starting points

  • Author a new agent. Build a machine from states, decisions, and typed requests; run it locally with runToQuiescence, test it with no API key, then use it in any framework or runtime with zero machine changes. See the Quickstart and Use in any stack.
  • Retrofit an existing agent. Turn a while loop into a machine: your SDK calls, tools, and retry code become the executors; the machine replaces only the control flow. See Migrating from a hand-rolled loop.
  • Copy a known pattern. ReAct, reflection, plan-and-execute, RAG, supervisor, and more, each a single runnable file you lift in 60 seconds. See Agent patterns.

Install

pnpm add @statelyai/agent@alpha xstate@6.0.0-alpha.64 zod

XState is pinned while its experimental durable restoration API evolves.

For the optional Vercel AI SDK executor:

pnpm add ai@^7 @ai-sdk/openai@^4

For the optional raw OpenAI SDK executor (@statelyai/agent/openai):

pnpm add openai

Classification, grading, relevance, and yes/no checks are judgments: call the AI SDK's experimental_decide with @ai-sdk/typesafe-ai (pnpm add ai @ai-sdk/typesafe-ai) from an ordinary actor; see Judgments with the AI SDK.

Requirements:

  • Node 22.18 or newer, and XState v6 alpha.46 or newer.
  • The package is ESM-first. CommonJS builds are published too, so require() works.
  • Provider packages must match your ai major: @ai-sdk/openai@^4 pairs with ai@^7. A bare @ai-sdk/openai resolves to @latest, which can mismatch the ai peer.

Quick start

This agent reviews refund requests. The model may propose an automatic refund, but the state machine owns the $100 limit.

import { createAgentRuntime, runToQuiescence, setupAgent } from "@statelyai/agent";
import { z } from "zod";

const agentSetup = setupAgent({
  context: z.object({
    request: z.string(),
    amount: z.number(),
  }),
  input: z.object({
    request: z.string(),
    amount: z.number(),
  }),
  output: z.object({
    outcome: z.enum(["refunded", "review"]),
  }),
  events: {
    AUTO_REFUND: {},
    REVIEW: z.object({ reason: z.string() }),
  },
});

const refundMachine = agentSetup.createMachine({
  context: ({ input }) => input,
  initial: "deciding",
  states: {
    deciding: {
      invoke: {
        src: "agent.decide",
        input: ({ context }) => ({
          model: "fast",
          system: "Choose AUTO_REFUND for eligible requests. Otherwise choose REVIEW.",
          prompt: `${context.request}\nAmount: $${context.amount}`,
          allowedEvents: ["AUTO_REFUND", "REVIEW"],
        }),
      },
      on: {
        AUTO_REFUND: ({ context }) => (context.amount <= 100 ? { target: "refunded" } : undefined),
        REVIEW: { target: "review" },
      },
    },
    refunded: {
      type: "final",
      output: () => ({ outcome: "refunded" }),
    },
    review: {
      type: "final",
      output: () => ({ outcome: "review" }),
    },
  },
});

const result = await runToQuiescence(
  createAgentRuntime(refundMachine, {
    // An executor is a plain function, so this run needs no API key and no
    // provider package.
    executors: { decide: async () => ({ event: { type: "AUTO_REFUND" } }) },
  }),
  {
    input: {
      request: "I was charged twice for the same order.",
      amount: 75,
    },
  },
);

if (result.status === "done") {
  console.log(result.output);
}

When the machine reaches refunded, the result is:

{ outcome: 'refunded' }

The model chooses between the events allowed in deciding. The AUTO_REFUND transition only works when the amount is at most $100. If the model chooses it for a larger amount, the guard rejects the choice and the decision is tried again.

To call a real model, leave the machine unchanged and swap the executors:

import { openai } from "@ai-sdk/openai";
import { createAiSdkExecutors } from "@statelyai/agent/ai-sdk";

const liveResult = await runToQuiescence(
  createAgentRuntime(refundMachine, {
    executors: createAiSdkExecutors({ models: { fast: openai("gpt-5.4-mini") } }),
  }),
  { input: { request: "I was charged twice for the same order.", amount: 75 } },
);

For a machine with several requests, the executor routes on request.name, or MockLanguageModelV3 from ai/test stands in for the provider behind createAiSdkExecutors. See Evals.

Passing the same models map to setupAgent({ models }) types the machine's model refs. Executors are always explicit, and core does not import the AI SDK. See Hosts and executors.

Architecture

flowchart LR
  M["Agent machine<br/>states · guards · requests"] -->|request| R["runToQuiescence"]
  R -->|executor call| E["Host executors<br/>generateText · streamText · decide"]
  E -->|API call| L["Model"]
  L -->|result| E
  E -->|result| R
  R -->|event or output| M
Loading

The machine never talks to a model directly, so swapping createAiSdkExecutors for your own functions changes nothing about the agent.

The example above has one model decision and two final outcomes. Real machines add approval states, retries, parallel work, child agents, and long-running waits without changing how control flow is represented.

The core concepts (machines owning control flow, bounded model decisions, typed requests, host-run executors, and native XState snapshots) are in the documentation overview.

Examples

See all examples.

Projets similaires

State machines, statecharts, and actors for complex logic

TypeScriptactor-modelbackground-jobsfinite-state-machine
Sstatelyai
30,2 k étoiles1,4 k

Langflow is a powerful tool for building and deploying AI-powered agents and workflows.

Pythonagentschatgptgenerative-ai
Llangflow-ai
155,4 k étoiles10,2 k

Platform for stateful agents: AI with advanced memory that can learn and self-improve over time.

aiai-agentsllm
Lletta-ai
25 k étoiles2,6 k