@statelyai/agent 2.0.0-alpha.11 → 2.0.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/ai-sdk.cjs +4 -5
  2. package/dist/ai-sdk.d.cts +7 -4
  3. package/dist/ai-sdk.d.mts +7 -4
  4. package/dist/ai-sdk.mjs +1 -2
  5. package/dist/{events-JiVPYrct.mjs → decision-BezSD_YC.mjs} +327 -20
  6. package/dist/{events-CRQj3VtP.cjs → decision-dWGhBh0P.cjs} +401 -28
  7. package/dist/errors-BQRk9eiZ.d.cts +19 -0
  8. package/dist/errors-C9rxnWbX.d.mts +19 -0
  9. package/dist/errors-CeSXQx0v.mjs +23 -0
  10. package/dist/errors-DUBBzRLP.cjs +28 -0
  11. package/dist/event-log-store-CNT_7F0V.cjs +452 -0
  12. package/dist/event-log-store-CriMgX1D.d.mts +144 -0
  13. package/dist/event-log-store-D7pWtIhb.mjs +411 -0
  14. package/dist/event-log-store-Ruq18mGp.d.cts +144 -0
  15. package/dist/index.cjs +1050 -705
  16. package/dist/index.d.cts +538 -565
  17. package/dist/index.d.mts +538 -565
  18. package/dist/index.mjs +950 -644
  19. package/dist/machines.cjs +752 -0
  20. package/dist/machines.d.cts +372 -0
  21. package/dist/machines.d.mts +372 -0
  22. package/dist/machines.mjs +741 -0
  23. package/dist/otel.cjs +268 -0
  24. package/dist/otel.d.cts +67 -0
  25. package/dist/otel.d.mts +67 -0
  26. package/dist/otel.mjs +267 -0
  27. package/dist/run-agent-C3mFDGTf.d.mts +1111 -0
  28. package/dist/run-agent-DnvtcnTZ.d.cts +1111 -0
  29. package/dist/setup-agent-DAZZSjDS.mjs +1711 -0
  30. package/dist/setup-agent-DP95MFrI.cjs +1836 -0
  31. package/dist/sqlite.cjs +135 -0
  32. package/dist/sqlite.d.cts +57 -0
  33. package/dist/sqlite.d.mts +57 -0
  34. package/dist/sqlite.mjs +133 -0
  35. package/dist/{text-logic-CaKqgX4Y.d.mts → text-logic-BDxwQNsD.d.cts} +155 -72
  36. package/dist/{text-logic-Ckhr2kKC.d.cts → text-logic-TkKPw8Aq.d.mts} +155 -72
  37. package/dist/{types-qm00QF91.d.mts → types-QbEfCVny.d.cts} +1 -1
  38. package/dist/{types-C9QiMjre.d.cts → types-_FXoFBGO.d.mts} +1 -1
  39. package/package.json +47 -39
  40. package/readme.md +49 -12
  41. package/schemas/agent-workflow.json +40 -21
  42. package/skills/generate-machine/SKILL.md +267 -0
  43. package/dist/adapter.cjs +0 -15
  44. package/dist/adapter.d.cts +0 -4
  45. package/dist/adapter.d.mts +0 -4
  46. package/dist/adapter.mjs +0 -2
  47. package/dist/decision-C3k4ve51.mjs +0 -227
  48. package/dist/decision-D8wJrM8W.cjs +0 -286
  49. package/dist/openai-compat.cjs +0 -309
  50. package/dist/openai-compat.d.cts +0 -59
  51. package/dist/openai-compat.d.mts +0 -59
  52. package/dist/openai-compat.mjs +0 -308
  53. package/dist/steps-BALp1eZo.d.mts +0 -198
  54. package/dist/steps-CVe54GPP.cjs +0 -420
  55. package/dist/steps-CkyyyuHd.mjs +0 -379
  56. package/dist/steps-MjnQI4aB.d.cts +0 -198
  57. package/dist/steps.cjs +0 -12
  58. package/dist/steps.d.cts +0 -3
  59. package/dist/steps.d.mts +0 -3
  60. package/dist/steps.mjs +0 -3
  61. package/dist/utils-BYqT_Dyv.d.cts +0 -108
  62. package/dist/utils-Do5wIJrh.d.mts +0 -108
  63. package/dist/zod.cjs +0 -31
  64. package/dist/zod.d.cts +0 -30
  65. package/dist/zod.d.mts +0 -30
  66. package/dist/zod.mjs +0 -30
package/readme.md CHANGED
@@ -1,20 +1,22 @@
1
1
  # Stately Agent
2
2
 
3
- **The logic layer for AI agents.**
3
+ **Make invalid agent actions impossible.**
4
4
 
5
- Build agents as state machines to control exactly what the agent can do. The machine owns control flow; the model only ever picks a legal event. Testing, inspection, and visualization fall out for free.
5
+ 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.
6
6
 
7
- Stately Agent adds model requests and decisions to XState. The state machine defines what the agent can do. Your application chooses the model, runs the requests, and stores the state.
7
+ Stately Agent adds model requests and decisions to XState:
8
8
 
9
- Any agent workflow or loop can be modeled as a state machine. Model calls and tools run as effects inside it. The model proposes an event. The machine decides whether it is allowed and what happens next. See [how this compares to LangGraph and hand-rolling a loop](docs/comparison.md).
9
+ - The machine defines what the agent can do.
10
+ - Your application chooses the model, runs the requests, and stores the state.
11
+ - The model proposes an event; the machine decides whether it is allowed and what happens next.
10
12
 
11
13
  Stately Agent 2 is in alpha. APIs may change before the stable release.
12
14
 
13
15
  [Documentation](https://stately.ai/docs/agents) · [Examples](examples/README.md) · [XState](https://github.com/statelyai/xstate)
14
16
 
15
- ## Three ways to start
17
+ ## Three starting points
16
18
 
17
- - **Author a new agent.** Build a machine from states, decisions, and typed requests; run it locally with `runAgent`, test it with no API key, then eject to any framework or runtime with zero machine changes. See the [Quickstart](docs/quickstart.md) and [Eject to your stack](docs/eject.md).
19
+ - **Author a new agent.** Build a machine from states, decisions, and typed requests; run it locally with `runAgent`, test it with no API key, then use it in any framework or runtime with zero machine changes. See the [Quickstart](docs/quickstart.md) and [Use in any stack](docs/any-stack.md).
18
20
  - **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 loop](docs/from-a-loop.md).
19
21
  - **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](docs/patterns.md).
20
22
 
@@ -26,7 +28,11 @@ Stately Agent 2 is in alpha. APIs may change before the stable release.
26
28
  pnpm add @statelyai/agent@alpha xstate@alpha zod ai@^6 @ai-sdk/openai@^3
27
29
  ```
28
30
 
29
- Node 22.18 or newer is required. The package is ESM-only; the library targets XState v6 alpha and stays compatible with XState v5. Provider packages must match your `ai` major: `@ai-sdk/openai@^3` pairs with `ai@^6` (a bare `@ai-sdk/openai` resolves to `@latest`, which can mismatch the `ai` peer).
31
+ Requirements:
32
+
33
+ - Node 22.18 or newer, and XState v6 alpha.25 or newer.
34
+ - The package is ESM-first. CommonJS builds are published too, so `require()` works.
35
+ - Provider packages must match your `ai` major: `@ai-sdk/openai@^3` pairs with `ai@^6`. A bare `@ai-sdk/openai` resolves to `@latest`, which can mismatch the `ai` peer.
30
36
 
31
37
  ## Quick start
32
38
 
@@ -114,11 +120,34 @@ When the machine reaches `refunded`, the result is:
114
120
 
115
121
  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.
116
122
 
117
- ## The state machine
123
+ `createScriptedExecutors` plays back canned answers through the same executor contract, so the machine above runs end to end before a model is involved. **No API key needed:**
124
+
125
+ ```ts
126
+ import { createScriptedExecutors } from "@statelyai/agent";
127
+
128
+ const result = await runAgent(refundMachine, {
129
+ input: { request: "I was charged twice for the same order.", amount: 75 },
130
+ executors: createScriptedExecutors({ decisions: [{ type: "AUTO_REFUND" }] }),
131
+ });
132
+ ```
133
+
134
+ Swap in `createAiSdkExecutors({ models })` when you want a real model. The scripted set is what your tests keep using.
135
+
136
+ ## Architecture
137
+
138
+ ```mermaid
139
+ flowchart LR
140
+ M["Agent machine<br/>states · guards · requests"] -->|request| R["runAgent"]
141
+ R -->|executor call| E["Host executors<br/>generateText · streamText · decide"]
142
+ E -->|API call| L["Model"]
143
+ L -->|result| E
144
+ E -->|result| R
145
+ R -->|event or output| M
146
+ ```
118
147
 
119
- <!-- Add the state machine illustration here. -->
148
+ The machine never talks to a model directly, so swapping `createAiSdkExecutors` for `createScriptedExecutors` (or your own functions) changes nothing about the agent.
120
149
 
121
- The example has one model decision and two final outcomes. Real machines can add approval states, retries, parallel work, child agents, and long-running waits without changing how the control flow is represented.
150
+ 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.
122
151
 
123
152
  ## Core concepts
124
153
 
@@ -129,6 +158,7 @@ The example has one model decision and two final outcomes. Real machines can add
129
158
  - **Requests are typed.** Inputs, outputs, context, and events use Standard Schema. Zod works out of the box.
130
159
  - **Your code runs the model.** `runAgent` accepts executor functions. The example uses the Vercel AI SDK adapter, but the machine does not depend on a provider.
131
160
  - **Snapshots can be stored.** An agent can stop for human input, save its XState snapshot, and resume later in another process.
161
+ - **Runs export verified replay entries.** Every `runAgent` result carries a JSON-safe `AgentLogEntry[]` with event identity, timestamp, machine version, and state/effect hashes; pass it to `replay` or `verifyReplay` without repeating model or tool calls.
132
162
  - **Machines can be checked without model calls.** Lint their structure, simulate scripted decisions, and explore paths without an API key.
133
163
  - **Agents are XState machines.** Guards, actors, parallel states, inspection, testing, and visualization work as usual.
134
164
 
@@ -151,7 +181,14 @@ See [all examples](examples/README.md).
151
181
  - [Decisions](docs/decisions.md)
152
182
  - [Human in the loop](docs/human-in-the-loop.md)
153
183
  - [Testing and verification](docs/verify.md)
154
- - [Running on different hosts](docs/hosts.md)
155
- - [Eject to your stack](docs/eject.md)
184
+ - [Evals](docs/evals.md)
185
+ - [Generating machines with an LLM](docs/generate-machines.md)
186
+ - [Hosts and executors](docs/hosts.md)
187
+ - [Models and providers](docs/models-and-providers.md)
188
+ - [Use in any stack](docs/any-stack.md)
189
+ - [The event log](docs/event-log.md)
190
+ - [Observability](docs/observability.md)
191
+ - [Usage and budgets](docs/usage-and-budgets.md)
156
192
  - [Agent patterns](docs/patterns.md)
157
193
  - [Migrating from a loop](docs/from-a-loop.md)
194
+ - [LangGraph vs agent machines](docs/langgraph-comparison.md)
@@ -45,6 +45,17 @@
45
45
  },
46
46
  "default": {}
47
47
  },
48
+ "actors": {
49
+ "description": "Placeholder actor sources declared by key. JSON wires no execution; provide the logic via machine.provide({ actors }) after setupAgent.fromConfig(...).",
50
+ "type": "object",
51
+ "propertyNames": {
52
+ "$ref": "#/$defs/Identifier"
53
+ },
54
+ "additionalProperties": {
55
+ "$ref": "#/$defs/Actor"
56
+ },
57
+ "default": {}
58
+ },
48
59
  "initial": {
49
60
  "description": "Initial child state key.",
50
61
  "type": "string"
@@ -233,6 +244,7 @@
233
244
  }
234
245
  },
235
246
  "toolChoice": {
247
+ "description": "Passed to the provider as-is; unlike other request fields it is NOT template-evaluated, so {{ }} expressions are not allowed here.",
236
248
  "anyOf": [
237
249
  {
238
250
  "type": "string",
@@ -250,10 +262,13 @@
250
262
  }
251
263
  },
252
264
  "additionalProperties": false
253
- },
254
- { "$ref": "#/$defs/ExpressionString" }
265
+ }
255
266
  ]
256
267
  },
268
+ "reasoning": {
269
+ "description": "Opt into the structured-output envelope's `reasoning` field.",
270
+ "type": "boolean"
271
+ },
257
272
  "temperature": {
258
273
  "$ref": "#/$defs/ExpressionValue"
259
274
  },
@@ -301,7 +316,8 @@
301
316
  "$ref": "#/$defs/ExpressionValue"
302
317
  }
303
318
  },
304
- "Tool": {
319
+ "Actor": {
320
+ "description": "A placeholder actor source: schemas describe it for authors/editors; the implementation is provided by the host.",
305
321
  "type": "object",
306
322
  "properties": {
307
323
  "description": {
@@ -314,6 +330,21 @@
314
330
  "$ref": "#/$defs/JsonSchema"
315
331
  }
316
332
  },
333
+ "additionalProperties": false
334
+ },
335
+ "Tool": {
336
+ "type": "object",
337
+ "properties": {
338
+ "description": {
339
+ "type": "string"
340
+ },
341
+ "inputSchema": {
342
+ "$ref": "#/$defs/JsonSchema"
343
+ },
344
+ "outputSchema": {
345
+ "$ref": "#/$defs/JsonSchema"
346
+ }
347
+ },
317
348
  "additionalProperties": {
318
349
  "$ref": "#/$defs/JsonValue"
319
350
  }
@@ -395,7 +426,8 @@
395
426
  "$ref": "#/$defs/ExpressionValue"
396
427
  },
397
428
  "meta": {
398
- "$ref": "#/$defs/ExpressionObject"
429
+ "description": "Static state metadata, passed through verbatim. Not template-evaluated.",
430
+ "$ref": "#/$defs/JsonObject"
399
431
  }
400
432
  },
401
433
  "additionalProperties": false
@@ -420,9 +452,6 @@
420
452
  },
421
453
  "onError": {
422
454
  "$ref": "#/$defs/TransitionOrArray"
423
- },
424
- "meta": {
425
- "$ref": "#/$defs/ExpressionObject"
426
455
  }
427
456
  },
428
457
  "additionalProperties": false
@@ -453,21 +482,10 @@
453
482
  ]
454
483
  },
455
484
  "guard": {
485
+ "description": "Either a whole-string {{ }} expression evaluated as truthy/falsy, or a named guard reference resolved against the `guards` passed to setupAgent.fromConfig(config, { guards }). Object guards ({ type, params }) are rejected by the lowering.",
456
486
  "anyOf": [
457
487
  { "$ref": "#/$defs/ExpressionString" },
458
- {
459
- "type": "object",
460
- "required": ["type"],
461
- "properties": {
462
- "type": {
463
- "type": "string"
464
- },
465
- "params": {
466
- "$ref": "#/$defs/ExpressionValue"
467
- }
468
- },
469
- "additionalProperties": false
470
- }
488
+ { "$ref": "#/$defs/Identifier" }
471
489
  ]
472
490
  },
473
491
  "assign": {
@@ -484,7 +502,8 @@
484
502
  "type": "boolean"
485
503
  },
486
504
  "meta": {
487
- "$ref": "#/$defs/ExpressionObject"
505
+ "description": "Static transition metadata, passed through verbatim. Not template-evaluated.",
506
+ "$ref": "#/$defs/JsonObject"
488
507
  }
489
508
  },
490
509
  "additionalProperties": false
@@ -0,0 +1,267 @@
1
+ ---
2
+ name: generate-machine
3
+ description: Author a @statelyai/agent workflow as JSON and check it before it runs. Use when asked to generate or author an agent machine, create a workflow config, write an AgentWorkflowConfig, or produce an LLM-authored state machine for an agent — and when a generated config fails Ajv validation or agent lint and needs repair.
4
+ ---
5
+
6
+ # Generate an agent machine
7
+
8
+ An agent machine is data. You author a JSON `AgentWorkflowConfig`, then run it through gates that all work with no API key: Ajv → `fromConfig` → lint → simulate. Do not hand back a config that has not passed all four.
9
+
10
+ ```
11
+ author → validate (Ajv 2020) → lower (fromConfig) → lint (assertAgentMachine) → simulate → hand back
12
+ ```
13
+
14
+ ## 1. Read the schema
15
+
16
+ The config shape is fixed by a shipped JSON Schema. Read it first; it is the contract.
17
+
18
+ - Consuming the package: `node_modules/@statelyai/agent/agent-workflow.json` (importable as `@statelyai/agent/agent-workflow.json`)
19
+ - In the `statelyai/agent` repo: `schemas/agent-workflow.json`
20
+
21
+ ## 2. Author the config
22
+
23
+ Rules the runtime enforces. Each one maps to a build error or a lint diagnostic:
24
+
25
+ - Every value is a JSON literal or a whole-string `"{{ }}"` expression reading a dot path on `input`, `context`, or `event`. No JavaScript.
26
+ - Guard expressions are truthy-only: no comparisons, operators, or negation.
27
+ - A model call is either a named `requests` entry invoked with `src: "<requestName>"`, or an inline invoke with `src: "agent.decide"` plus an `allowedEvents` list.
28
+ - A state invoking `agent.decide` MUST handle every allowed event in its `on`.
29
+ - An `agent.decide` invoke has no `onDone` (a decision produces no output). Use `onError` for the retries-exhausted path.
30
+ - A request invoke reads its result via `onDone.assign` from `"{{ event.output.<field> }}"`.
31
+ - Every path must reach a `"type": "final"` state, and each final state needs an `output` when `schemas.output` is declared.
32
+ - Do not invent guard or action names. Only `"{{ }}"` guards, `assign`, and `emit` exist unless the host tells you which named guards/actions it implements.
33
+ - Model refs are strings (`"openai/gpt-5.4-mini"`); the host resolves them.
34
+
35
+ Reference config — decision, text request, idle human step, one final state:
36
+
37
+ ```json
38
+ {
39
+ "id": "support-ticket",
40
+ "schemas": {
41
+ "input": {
42
+ "type": "object",
43
+ "properties": { "ticket": { "type": "string" } },
44
+ "required": ["ticket"]
45
+ },
46
+ "context": {
47
+ "type": "object",
48
+ "properties": {
49
+ "ticket": { "type": "string" },
50
+ "reply": { "type": "string" },
51
+ "resolution": { "type": "string" }
52
+ },
53
+ "required": ["ticket"]
54
+ },
55
+ "events": {
56
+ "ESCALATE": {
57
+ "type": "object",
58
+ "properties": { "reason": { "type": "string" } },
59
+ "required": ["reason"]
60
+ },
61
+ "REPLY": { "type": "object", "properties": {} },
62
+ "APPROVE": { "type": "object", "properties": {} },
63
+ "REJECT": { "type": "object", "properties": {} }
64
+ },
65
+ "output": {
66
+ "type": "object",
67
+ "properties": { "resolution": { "type": "string" }, "reply": { "type": "string" } },
68
+ "required": ["resolution"]
69
+ }
70
+ },
71
+ "context": { "ticket": "{{ input.ticket }}" },
72
+ "requests": {
73
+ "draftReply": {
74
+ "model": "openai/gpt-5.4-mini",
75
+ "system": "Draft a short, courteous support reply.",
76
+ "prompt": "{{ context.ticket }}",
77
+ "input": {
78
+ "type": "object",
79
+ "properties": { "ticket": { "type": "string" } },
80
+ "required": ["ticket"]
81
+ },
82
+ "output": {
83
+ "type": "object",
84
+ "properties": { "reply": { "type": "string" } },
85
+ "required": ["reply"]
86
+ }
87
+ }
88
+ },
89
+ "initial": "triaging",
90
+ "states": {
91
+ "triaging": {
92
+ "invoke": {
93
+ "id": "triageDecision",
94
+ "src": "agent.decide",
95
+ "input": {
96
+ "model": "openai/gpt-5.4-mini",
97
+ "system": "Decide whether this ticket needs escalation or a drafted reply.",
98
+ "prompt": "{{ context.ticket }}",
99
+ "allowedEvents": ["ESCALATE", "REPLY"],
100
+ "maxRetries": 2
101
+ },
102
+ "onError": { "target": "resolved", "assign": { "resolution": "escalated" } }
103
+ },
104
+ "on": {
105
+ "ESCALATE": { "target": "resolved", "assign": { "resolution": "escalated" } },
106
+ "REPLY": { "target": "drafting" }
107
+ }
108
+ },
109
+ "drafting": {
110
+ "invoke": {
111
+ "id": "draft",
112
+ "src": "draftReply",
113
+ "input": { "ticket": "{{ context.ticket }}" },
114
+ "onDone": {
115
+ "target": "awaitingApproval",
116
+ "assign": { "reply": "{{ event.output.reply }}" }
117
+ }
118
+ }
119
+ },
120
+ "awaitingApproval": {
121
+ "description": "Idle: nothing to do until a human approves or rejects the draft.",
122
+ "on": {
123
+ "APPROVE": { "target": "resolved", "assign": { "resolution": "replied" } },
124
+ "REJECT": { "target": "resolved", "assign": { "resolution": "escalated" } }
125
+ }
126
+ },
127
+ "resolved": {
128
+ "type": "final",
129
+ "output": { "resolution": "{{ context.resolution }}", "reply": "{{ context.reply }}" }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ ## 3. Validate with Ajv 2020
136
+
137
+ The workflow schema is draft 2020-12, so it needs Ajv's 2020 build. Run this before anything else touches the config.
138
+
139
+ ```ts
140
+ import Ajv2020 from "ajv/dist/2020.js";
141
+ import workflowSchema from "@statelyai/agent/agent-workflow.json";
142
+ import type { AgentWorkflowConfig } from "@statelyai/agent";
143
+
144
+ const validateWorkflow = new Ajv2020({ strict: false }).compile(workflowSchema);
145
+
146
+ function validateGeneratedConfig(candidate: unknown): AgentWorkflowConfig {
147
+ if (validateWorkflow(candidate)) return candidate as AgentWorkflowConfig;
148
+ throw new Error(
149
+ (validateWorkflow.errors ?? [])
150
+ .map((error) => `${error.instancePath || "(root)"} ${error.message}`)
151
+ .join("\n"),
152
+ );
153
+ }
154
+ ```
155
+
156
+ Ajv errors carry `instancePath`, so a repair prompt can name the exact bad field.
157
+
158
+ ## 4. Lower with `fromConfig`
159
+
160
+ `fromConfig` needs a `compileSchema` that turns the JSON Schemas _inside_ the config into Standard Schema validators. Two engines, two jobs: the 2020 build above checks the config document; this one compiles the per-field schemas. The library bundles no JSON Schema engine, by design.
161
+
162
+ ```ts
163
+ import Ajv from "ajv";
164
+ import { setupAgent, type SchemaCompiler, type StandardSchemaV1 } from "@statelyai/agent";
165
+
166
+ const ajv = new Ajv({ strict: false });
167
+
168
+ export const ajvCompileSchema: SchemaCompiler = (jsonSchema, name): StandardSchemaV1 => {
169
+ const validate = ajv.compile(jsonSchema);
170
+ return {
171
+ "~standard": {
172
+ version: 1,
173
+ vendor: "ajv",
174
+ validate: (value) =>
175
+ validate(value)
176
+ ? { value }
177
+ : {
178
+ issues: (validate.errors ?? []).map((e) => ({
179
+ message: `${name}${e.instancePath} ${e.message}`,
180
+ })),
181
+ },
182
+ // Expose the source JSON Schema so lint's serializability checks can read the shape.
183
+ jsonSchema: { input: () => jsonSchema },
184
+ },
185
+ };
186
+ };
187
+
188
+ const { machine, schemas } = setupAgent.fromConfig(config, { compileSchema: ajvCompileSchema });
189
+ ```
190
+
191
+ Lowering is itself a gate: it throws on an unresolved named guard/action and on an `onDone` attached to an `agent.decide` invoke.
192
+
193
+ ## 5. Lint
194
+
195
+ ```ts
196
+ import { assertAgentMachine, lintAgentMachine } from "@statelyai/agent";
197
+
198
+ const diagnostics = lintAgentMachine(machine);
199
+ assertAgentMachine(machine); // throws AgentLintError on error-severity findings
200
+ ```
201
+
202
+ Every check applies to config-built machines, reachability included — the lowering keeps the config's transition targets, so `unreachable-state` and `missing-final` read the real graph. Do not disable checks.
203
+
204
+ The one that bites most often is `decide-without-events`: an `allowedEvents` list with no matching `on` transitions produces a decision the machine can never deliver.
205
+
206
+ ## 6. Simulate a dry run
207
+
208
+ Lint is structural. A dry run proves a path actually settles. No API key needed.
209
+
210
+ ```ts
211
+ import { simulateAgent } from "@statelyai/agent";
212
+
213
+ const dryRun = await simulateAgent(machine, {
214
+ input: { ticket: "" },
215
+ script: {
216
+ decisions: { "agent.decide": [{ type: "REPLY" }] },
217
+ text: { draftReply: [{ reply: "" }] },
218
+ },
219
+ });
220
+ // dryRun.status: 'done' | 'idle' | 'exhausted'
221
+ ```
222
+
223
+ Derive the script from the config rather than guessing: take the first entry of each decision's `allowedEvents`, and stub each request's output from its declared `output` schema (`""` for string, `0` for number, `[]` for array, recurse on `properties`).
224
+
225
+ - `'exhausted'` → the machine loops. Reject it.
226
+ - `'idle'` → it stopped at a human step. Expected when the config has one.
227
+ - To cover every branch instead of one path, use `explorePaths` / `canReach`.
228
+
229
+ ## 7. Repair loop
230
+
231
+ Every gate throws with a message naming the offending field, state, or path. Feed that message back verbatim and regenerate:
232
+
233
+ ```ts
234
+ for (let attempt = 0; attempt < 3; attempt++) {
235
+ try {
236
+ return buildAndVet(await author({ system, prompt }));
237
+ } catch (error) {
238
+ prompt = `${originalTask}\n\nYour previous config was rejected:\n${(error as Error).message}\nReturn a corrected config.`;
239
+ }
240
+ }
241
+ ```
242
+
243
+ Keep the cap at ~3. A config that fails three schema-shaped repairs is usually asking for something the data form cannot express — author it in TypeScript with `setupAgent` instead.
244
+
245
+ ## 8. Hand back
246
+
247
+ Deliver the config JSON plus how to run it:
248
+
249
+ ```ts
250
+ import { runAgent } from "@statelyai/agent";
251
+ import { createAiSdkExecutors } from "@statelyai/agent/ai-sdk";
252
+
253
+ const result = await runAgent(machine, {
254
+ input: { ticket: "Export downloads a 0-byte CSV on Safari." },
255
+ executors: createAiSdkExecutors({ resolveModel }),
256
+ });
257
+ ```
258
+
259
+ Say which gates passed, list any warning-severity lint diagnostics, and report the dry-run status. Model refs are strings, so the host supplies `resolveModel`.
260
+
261
+ ## Known limits
262
+
263
+ - **Named guards and actions are host-resolved.** A config carries no functions. `guard: "isReady"` only works if the host passes an `isReady` implementation to `fromConfig`; an unresolved name is a build-time throw. Either list the host's names in the prompt or forbid named guards/actions entirely.
264
+ - **Expressions are dot-path templates only.** `"{{ context.a.b }}"` — no comparisons, no arithmetic, no method calls, no partial interpolation inside a larger string.
265
+ - **Ajv validity is not semantic validity.** A config can validate and still invoke a request that does not exist or route to a state that solves nothing. That is what lint and simulate are for.
266
+ - **A dry run covers one path.** Use `explorePaths` when the branch structure matters.
267
+ - **Model refs and tool names are unchecked strings.** They resolve at run time, in the host.
package/dist/adapter.cjs DELETED
@@ -1,15 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_events = require("./events-CRQj3VtP.cjs");
3
- exports.bindRequestExecutor = require_events.bindRequestExecutor;
4
- exports.buildEnvelopeSchema = require_events.buildEnvelopeSchema;
5
- exports.getAgentOutputMode = require_events.getAgentOutputMode;
6
- exports.getJsonSchema = require_events.getJsonSchema;
7
- exports.getJsonSchemaSync = require_events.getJsonSchemaSync;
8
- exports.getMachineStructuralHash = require_events.getMachineStructuralHash;
9
- exports.isStandardSchema = require_events.isStandardSchema;
10
- exports.isStructuredOutputSchema = require_events.isStructuredOutputSchema;
11
- exports.matchesEventPattern = require_events.matchesEventPattern;
12
- exports.parseModelRef = require_events.parseModelRef;
13
- exports.parseOutput = require_events.parseOutput;
14
- exports.parseStructuredEnvelope = require_events.parseStructuredEnvelope;
15
- exports.validateSchemaSync = require_events.validateSchemaSync;
@@ -1,4 +0,0 @@
1
- import { C as StandardSchemaV1 } from "./types-C9QiMjre.cjs";
2
- import { D as parseOutput, E as parseModelRef, J as matchesEventPattern, O as parseStructuredEnvelope, S as buildEnvelopeSchema, T as isStructuredOutputSchema, a as AgentRequestExecutorInfo, h as TextLogic, i as AgentRequestExecutor, l as AgentTextRequest, m as StructuredOutputEnvelope, o as AgentRequestExecutorResult, r as AgentOutputMode, w as getAgentOutputMode, x as bindRequestExecutor } from "./text-logic-Ckhr2kKC.cjs";
3
- import { a as getMachineStructuralHash, f as validateSchemaSync, i as getJsonSchemaSync, r as getJsonSchema, s as isStandardSchema } from "./utils-BYqT_Dyv.cjs";
4
- export { type AgentOutputMode, type AgentRequestExecutor, type AgentRequestExecutorInfo, type AgentRequestExecutorResult, type AgentTextRequest, type StandardSchemaV1, type StructuredOutputEnvelope, type TextLogic, bindRequestExecutor, buildEnvelopeSchema, getAgentOutputMode, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, isStandardSchema, isStructuredOutputSchema, matchesEventPattern, parseModelRef, parseOutput, parseStructuredEnvelope, validateSchemaSync };
@@ -1,4 +0,0 @@
1
- import { C as StandardSchemaV1 } from "./types-qm00QF91.mjs";
2
- import { D as parseOutput, E as parseModelRef, J as matchesEventPattern, O as parseStructuredEnvelope, S as buildEnvelopeSchema, T as isStructuredOutputSchema, a as AgentRequestExecutorInfo, h as TextLogic, i as AgentRequestExecutor, l as AgentTextRequest, m as StructuredOutputEnvelope, o as AgentRequestExecutorResult, r as AgentOutputMode, w as getAgentOutputMode, x as bindRequestExecutor } from "./text-logic-CaKqgX4Y.mjs";
3
- import { a as getMachineStructuralHash, f as validateSchemaSync, i as getJsonSchemaSync, r as getJsonSchema, s as isStandardSchema } from "./utils-Do5wIJrh.mjs";
4
- export { type AgentOutputMode, type AgentRequestExecutor, type AgentRequestExecutorInfo, type AgentRequestExecutorResult, type AgentTextRequest, type StandardSchemaV1, type StructuredOutputEnvelope, type TextLogic, bindRequestExecutor, buildEnvelopeSchema, getAgentOutputMode, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, isStandardSchema, isStructuredOutputSchema, matchesEventPattern, parseModelRef, parseOutput, parseStructuredEnvelope, validateSchemaSync };
package/dist/adapter.mjs DELETED
@@ -1,2 +0,0 @@
1
- import { F as getMachineStructuralHash, H as validateSchemaSync, L as isStandardSchema, N as getJsonSchema, P as getJsonSchemaSync, b as parseOutput, d as buildEnvelopeSchema, g as isStructuredOutputSchema, h as getAgentOutputMode, r as matchesEventPattern, u as bindRequestExecutor, x as parseStructuredEnvelope, y as parseModelRef } from "./events-JiVPYrct.mjs";
2
- export { bindRequestExecutor, buildEnvelopeSchema, getAgentOutputMode, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, isStandardSchema, isStructuredOutputSchema, matchesEventPattern, parseModelRef, parseOutput, parseStructuredEnvelope, validateSchemaSync };