@statelyai/agent 2.0.0-alpha.17 → 2.0.0-alpha.19

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 (38) hide show
  1. package/dist/ai-sdk.cjs +4 -2
  2. package/dist/ai-sdk.d.cts +1 -1
  3. package/dist/ai-sdk.d.mts +1 -1
  4. package/dist/ai-sdk.mjs +3 -2
  5. package/dist/{decision-C11xuud2.mjs → decision-DsIkEuHz.mjs} +48 -28
  6. package/dist/{decision-DnQCQPew.cjs → decision-t26zsnSR.cjs} +64 -43
  7. package/dist/{event-log-store-CQJq8_v4.d.cts → event-log-store-Bz7HDBkE.d.cts} +11 -12
  8. package/dist/{event-log-store-B-1fcfkT.mjs → event-log-store-DmIDosD6.mjs} +22 -14
  9. package/dist/{event-log-store-yquOV1TX.cjs → event-log-store-a_TKy1gk.cjs} +22 -14
  10. package/dist/{event-log-store-BrC9Q1xW.d.mts → event-log-store-hrA1vqtN.d.mts} +11 -12
  11. package/dist/index.cjs +266 -109
  12. package/dist/index.d.cts +148 -80
  13. package/dist/index.d.mts +148 -80
  14. package/dist/index.mjs +262 -107
  15. package/dist/machines.cjs +13 -17
  16. package/dist/machines.d.cts +14 -17
  17. package/dist/machines.d.mts +14 -17
  18. package/dist/machines.mjs +13 -17
  19. package/dist/otel.cjs +1 -0
  20. package/dist/otel.d.cts +1 -1
  21. package/dist/otel.d.mts +1 -1
  22. package/dist/{run-agent-BxjGaVpL.d.cts → run-agent--4bbms-D.d.cts} +121 -48
  23. package/dist/{run-agent-COHoCgQd.d.mts → run-agent-CwmzAZwj.d.mts} +121 -48
  24. package/dist/{setup-agent-D_EyJ0Ik.cjs → setup-agent-BFA4VKpN.cjs} +51 -33
  25. package/dist/{setup-agent-CTg57Pa4.mjs → setup-agent-CPFPN06s.mjs} +45 -28
  26. package/dist/sqlite.cjs +3 -9
  27. package/dist/sqlite.d.cts +1 -1
  28. package/dist/sqlite.d.mts +1 -1
  29. package/dist/sqlite.mjs +3 -9
  30. package/dist/{text-logic-BFX5q7fM.d.cts → text-logic-Cavva1W6.d.cts} +25 -9
  31. package/dist/{text-logic-DQW8_DWW.d.mts → text-logic-Er5KkTX6.d.mts} +25 -9
  32. package/dist/validate.cjs +436 -0
  33. package/dist/validate.d.cts +31 -0
  34. package/dist/validate.d.mts +31 -0
  35. package/dist/validate.mjs +411 -0
  36. package/package.json +15 -1
  37. package/schemas/agent-workflow.json +2 -2
  38. package/skills/generate-machine/SKILL.md +12 -14
@@ -0,0 +1,411 @@
1
+ import Ajv2020 from "ajv/dist/2020.js";
2
+ //#region schemas/agent-workflow.json
3
+ var agent_workflow_default = {
4
+ $schema: "https://json-schema.org/draft/2020-12/schema",
5
+ $id: "https://stately.ai/schemas/agent-workflow.json",
6
+ title: "Stately Agent Workflow Definition",
7
+ description: "Static, declarative agent workflow definition that can be lowered to a setupAgent(...) XState machine.",
8
+ type: "object",
9
+ required: ["initial", "states"],
10
+ properties: {
11
+ "$schema": {
12
+ "description": "JSON Schema reference an editor attaches to the config file. Ignored by the lowering.",
13
+ "type": "string"
14
+ },
15
+ "key": {
16
+ "description": "Stable workflow key used by tools, storage, and visual editors.",
17
+ "type": "string",
18
+ "minLength": 1
19
+ },
20
+ "id": {
21
+ "description": "Optional XState machine id.",
22
+ "type": "string",
23
+ "minLength": 1
24
+ },
25
+ "version": {
26
+ "description": "Definition version chosen by the author.",
27
+ "type": "string"
28
+ },
29
+ "description": { "type": "string" },
30
+ "schemas": { "$ref": "#/$defs/AgentSchemas" },
31
+ "context": {
32
+ "description": "Initial XState context. Values may be JSON literals or whole-string {{ }} expressions evaluated with machine input.",
33
+ "$ref": "#/$defs/ExpressionObject"
34
+ },
35
+ "requests": {
36
+ "description": "Named model requests that become typed invoke sources.",
37
+ "type": "object",
38
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
39
+ "additionalProperties": { "$ref": "#/$defs/Request" },
40
+ "default": {}
41
+ },
42
+ "actors": {
43
+ "description": "Placeholder actor sources declared by key. JSON wires no execution; provide the logic via machine.provide({ actors }) after setupAgent.fromConfig(...).",
44
+ "type": "object",
45
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
46
+ "additionalProperties": { "$ref": "#/$defs/Actor" },
47
+ "default": {}
48
+ },
49
+ "initial": {
50
+ "description": "Initial child state key.",
51
+ "type": "string"
52
+ },
53
+ "states": {
54
+ "description": "Root state nodes.",
55
+ "type": "object",
56
+ "minProperties": 1,
57
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
58
+ "additionalProperties": { "$ref": "#/$defs/State" }
59
+ },
60
+ "idleTags": {
61
+ "description": "State tags that mark an intentional wait for an external event (a human approval, an inbound webhook, ...). Lowered by setupAgent.fromConfig(...) into a snapshot.hasTag(...) idle predicate so runAgent settles those states idle deterministically. Every listed tag must appear in some state's 'tags'.",
62
+ "type": "array",
63
+ "items": {
64
+ "type": "string",
65
+ "minLength": 1
66
+ }
67
+ },
68
+ "meta": { "$ref": "#/$defs/JsonObject" }
69
+ },
70
+ additionalProperties: false,
71
+ $defs: {
72
+ "Identifier": {
73
+ "type": "string",
74
+ "minLength": 1,
75
+ "pattern": "^[A-Za-z_$][A-Za-z0-9_$.-]*$"
76
+ },
77
+ "ExpressionString": {
78
+ "description": "Whole-string expression delimited by {{ }}.",
79
+ "type": "string",
80
+ "pattern": "^\\{\\{[\\s\\S]*\\}\\}$"
81
+ },
82
+ "TemplateString": {
83
+ "description": "String that may contain {{ }} template expressions. Escape literal delimiters according to the selected expression/template evaluator.",
84
+ "type": "string"
85
+ },
86
+ "JsonValue": { "anyOf": [
87
+ { "type": "null" },
88
+ { "type": "boolean" },
89
+ { "type": "number" },
90
+ { "type": "string" },
91
+ {
92
+ "type": "array",
93
+ "items": { "$ref": "#/$defs/JsonValue" }
94
+ },
95
+ { "$ref": "#/$defs/JsonObject" }
96
+ ] },
97
+ "JsonObject": {
98
+ "type": "object",
99
+ "propertyNames": { "type": "string" },
100
+ "additionalProperties": { "$ref": "#/$defs/JsonValue" }
101
+ },
102
+ "ExpressionValue": {
103
+ "description": "JSON value or whole-string {{ }} expression. Use this for typed values, not prompt templating.",
104
+ "anyOf": [
105
+ { "$ref": "#/$defs/ExpressionString" },
106
+ { "type": "null" },
107
+ { "type": "boolean" },
108
+ { "type": "number" },
109
+ { "type": "string" },
110
+ {
111
+ "type": "array",
112
+ "items": { "$ref": "#/$defs/ExpressionValue" }
113
+ },
114
+ {
115
+ "type": "object",
116
+ "propertyNames": { "type": "string" },
117
+ "additionalProperties": { "$ref": "#/$defs/ExpressionValue" }
118
+ }
119
+ ]
120
+ },
121
+ "ExpressionObject": {
122
+ "type": "object",
123
+ "propertyNames": { "type": "string" },
124
+ "additionalProperties": { "$ref": "#/$defs/ExpressionValue" }
125
+ },
126
+ "JsonSchema": {
127
+ "description": "JSON Schema object.",
128
+ "type": "object",
129
+ "propertyNames": { "type": "string" },
130
+ "additionalProperties": true
131
+ },
132
+ "AgentSchemas": {
133
+ "type": "object",
134
+ "properties": {
135
+ "input": { "$ref": "#/$defs/JsonSchema" },
136
+ "context": { "$ref": "#/$defs/JsonSchema" },
137
+ "events": {
138
+ "type": "object",
139
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
140
+ "additionalProperties": { "$ref": "#/$defs/JsonSchema" }
141
+ },
142
+ "emitted": {
143
+ "type": "object",
144
+ "description": "Schemas for events emitted outward by emit actions, keyed by emitted event type.",
145
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
146
+ "additionalProperties": { "$ref": "#/$defs/JsonSchema" }
147
+ },
148
+ "output": { "$ref": "#/$defs/JsonSchema" },
149
+ "meta": { "$ref": "#/$defs/JsonSchema" }
150
+ },
151
+ "additionalProperties": false
152
+ },
153
+ "Request": {
154
+ "type": "object",
155
+ "required": [
156
+ "model",
157
+ "input",
158
+ "output"
159
+ ],
160
+ "properties": {
161
+ "mode": {
162
+ "type": "string",
163
+ "enum": ["generate", "stream"],
164
+ "default": "generate"
165
+ },
166
+ "description": { "type": "string" },
167
+ "model": { "$ref": "#/$defs/TemplateString" },
168
+ "system": { "$ref": "#/$defs/TemplateString" },
169
+ "prompt": { "$ref": "#/$defs/TemplateString" },
170
+ "messages": { "anyOf": [{ "$ref": "#/$defs/ExpressionString" }, {
171
+ "type": "array",
172
+ "items": { "$ref": "#/$defs/Message" }
173
+ }] },
174
+ "input": { "$ref": "#/$defs/JsonSchema" },
175
+ "output": { "$ref": "#/$defs/JsonSchema" },
176
+ "tools": {
177
+ "type": "object",
178
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
179
+ "additionalProperties": { "$ref": "#/$defs/Tool" }
180
+ },
181
+ "toolChoice": {
182
+ "description": "Passed to the provider as-is; unlike other request fields it is NOT template-evaluated, so {{ }} expressions are not allowed here.",
183
+ "anyOf": [{
184
+ "type": "string",
185
+ "enum": [
186
+ "auto",
187
+ "none",
188
+ "required"
189
+ ]
190
+ }, {
191
+ "type": "object",
192
+ "required": ["type", "name"],
193
+ "properties": {
194
+ "type": { "const": "tool" },
195
+ "name": { "$ref": "#/$defs/Identifier" }
196
+ },
197
+ "additionalProperties": false
198
+ }]
199
+ },
200
+ "reasoning": {
201
+ "description": "Opt into the structured-output envelope's `reasoning` field.",
202
+ "type": "boolean"
203
+ },
204
+ "temperature": { "$ref": "#/$defs/ExpressionValue" },
205
+ "maxOutputTokens": { "$ref": "#/$defs/ExpressionValue" },
206
+ "topP": { "$ref": "#/$defs/ExpressionValue" },
207
+ "topK": { "$ref": "#/$defs/ExpressionValue" },
208
+ "seed": { "$ref": "#/$defs/ExpressionValue" },
209
+ "stopSequences": { "anyOf": [{ "$ref": "#/$defs/ExpressionString" }, {
210
+ "type": "array",
211
+ "items": { "type": "string" }
212
+ }] },
213
+ "metadata": { "$ref": "#/$defs/ExpressionValue" }
214
+ },
215
+ "additionalProperties": false
216
+ },
217
+ "Message": {
218
+ "type": "object",
219
+ "required": ["role", "content"],
220
+ "properties": {
221
+ "role": { "$ref": "#/$defs/TemplateString" },
222
+ "content": { "$ref": "#/$defs/TemplateString" }
223
+ },
224
+ "additionalProperties": { "$ref": "#/$defs/ExpressionValue" }
225
+ },
226
+ "Actor": {
227
+ "description": "A placeholder actor source: schemas describe it for authors/editors; the implementation is provided by the host.",
228
+ "type": "object",
229
+ "properties": {
230
+ "description": { "type": "string" },
231
+ "input": { "$ref": "#/$defs/JsonSchema" },
232
+ "output": { "$ref": "#/$defs/JsonSchema" }
233
+ },
234
+ "additionalProperties": false
235
+ },
236
+ "Tool": {
237
+ "type": "object",
238
+ "properties": {
239
+ "description": { "type": "string" },
240
+ "inputSchema": { "$ref": "#/$defs/JsonSchema" },
241
+ "outputSchema": { "$ref": "#/$defs/JsonSchema" }
242
+ },
243
+ "additionalProperties": { "$ref": "#/$defs/JsonValue" }
244
+ },
245
+ "State": {
246
+ "type": "object",
247
+ "properties": {
248
+ "description": { "type": "string" },
249
+ "type": {
250
+ "type": "string",
251
+ "enum": [
252
+ "parallel",
253
+ "history",
254
+ "final",
255
+ "choice"
256
+ ]
257
+ },
258
+ "initial": { "type": "string" },
259
+ "states": {
260
+ "type": "object",
261
+ "propertyNames": { "$ref": "#/$defs/Identifier" },
262
+ "additionalProperties": { "$ref": "#/$defs/State" }
263
+ },
264
+ "invoke": { "anyOf": [{ "$ref": "#/$defs/Invoke" }, {
265
+ "type": "array",
266
+ "items": { "$ref": "#/$defs/Invoke" }
267
+ }] },
268
+ "choice": {
269
+ "description": "Choice-state routing, equivalent to XState's type: 'choice' + choice transition function.",
270
+ "$ref": "#/$defs/TransitionOrArray"
271
+ },
272
+ "on": {
273
+ "type": "object",
274
+ "propertyNames": { "type": "string" },
275
+ "additionalProperties": { "$ref": "#/$defs/TransitionOrArray" }
276
+ },
277
+ "always": { "$ref": "#/$defs/TransitionOrArray" },
278
+ "onDone": { "$ref": "#/$defs/TransitionOrArray" },
279
+ "after": {
280
+ "type": "object",
281
+ "propertyNames": { "type": "string" },
282
+ "additionalProperties": { "$ref": "#/$defs/TransitionOrArray" }
283
+ },
284
+ "entry": { "$ref": "#/$defs/ActionOrArray" },
285
+ "exit": { "$ref": "#/$defs/ActionOrArray" },
286
+ "tags": {
287
+ "type": "array",
288
+ "items": { "type": "string" }
289
+ },
290
+ "output": { "$ref": "#/$defs/ExpressionValue" },
291
+ "meta": {
292
+ "description": "Static state metadata, passed through verbatim. Not template-evaluated.",
293
+ "$ref": "#/$defs/JsonObject"
294
+ }
295
+ },
296
+ "additionalProperties": false
297
+ },
298
+ "Invoke": {
299
+ "type": "object",
300
+ "required": ["src"],
301
+ "properties": {
302
+ "id": { "type": "string" },
303
+ "src": {
304
+ "description": "Actor source name. 'agent.decide' invokes are special: decision delivery is automatic (the chosen event is sent as soon as the model decides) and 'onDone' must be omitted — see the 'onDone' description.",
305
+ "$ref": "#/$defs/Identifier"
306
+ },
307
+ "input": { "$ref": "#/$defs/ExpressionValue" },
308
+ "onDone": {
309
+ "description": "Not valid on an 'agent.decide' invoke — the built-in lowering rejects it at setupAgent.fromConfig(...) time. A decision has no output value of its own (its output IS the chosen event), so delivery is automatic and not configurable from JSON. 'onError' (retries exhausted) still applies to 'agent.decide' invokes.",
310
+ "$ref": "#/$defs/TransitionOrArray"
311
+ },
312
+ "onError": { "$ref": "#/$defs/TransitionOrArray" }
313
+ },
314
+ "additionalProperties": false
315
+ },
316
+ "TransitionOrArray": { "anyOf": [{ "$ref": "#/$defs/Transition" }, {
317
+ "type": "array",
318
+ "items": { "$ref": "#/$defs/Transition" }
319
+ }] },
320
+ "Transition": {
321
+ "type": "object",
322
+ "properties": {
323
+ "target": { "anyOf": [{ "type": "string" }, {
324
+ "type": "array",
325
+ "items": { "type": "string" }
326
+ }] },
327
+ "guard": {
328
+ "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.",
329
+ "anyOf": [{ "$ref": "#/$defs/ExpressionString" }, { "$ref": "#/$defs/Identifier" }]
330
+ },
331
+ "assign": {
332
+ "description": "Context assignments applied when this transition is taken.",
333
+ "$ref": "#/$defs/ExpressionObject"
334
+ },
335
+ "actions": { "$ref": "#/$defs/ActionOrArray" },
336
+ "description": { "type": "string" },
337
+ "reenter": { "type": "boolean" },
338
+ "meta": {
339
+ "description": "Static transition metadata, passed through verbatim. Not template-evaluated.",
340
+ "$ref": "#/$defs/JsonObject"
341
+ }
342
+ },
343
+ "additionalProperties": false
344
+ },
345
+ "ActionOrArray": { "anyOf": [{ "$ref": "#/$defs/Action" }, {
346
+ "type": "array",
347
+ "items": { "$ref": "#/$defs/Action" }
348
+ }] },
349
+ "Action": {
350
+ "type": "object",
351
+ "properties": {
352
+ "type": { "type": "string" },
353
+ "params": { "$ref": "#/$defs/ExpressionValue" },
354
+ "assign": { "$ref": "#/$defs/ExpressionObject" },
355
+ "emit": {
356
+ "description": "Event emitted outward through runAgent's on handlers.",
357
+ "$ref": "#/$defs/ExpressionValue"
358
+ }
359
+ },
360
+ "anyOf": [
361
+ { "required": ["type"] },
362
+ { "required": ["assign"] },
363
+ { "required": ["emit"] }
364
+ ],
365
+ "additionalProperties": { "$ref": "#/$defs/ExpressionValue" }
366
+ }
367
+ }
368
+ };
369
+ //#endregion
370
+ //#region src/validate/index.ts
371
+ /**
372
+ * Schema validation for agent workflow configs.
373
+ *
374
+ * Lives behind the `@statelyai/agent/validate` subpath so the core package
375
+ * stays dependency-free. This module imports `ajv`, which is declared as an
376
+ * optional peer dependency.
377
+ */
378
+ let compiledWorkflowValidator;
379
+ function getWorkflowValidator() {
380
+ if (!compiledWorkflowValidator) compiledWorkflowValidator = new Ajv2020({
381
+ strict: false,
382
+ allowUnionTypes: true,
383
+ allErrors: true
384
+ }).compile(agent_workflow_default);
385
+ return compiledWorkflowValidator;
386
+ }
387
+ /**
388
+ * Validates a value against the shipped `schemas/agent-workflow.json` before
389
+ * you hand it to `setupAgent(...).fromConfig(...)`. Returns diagnostics rather
390
+ * than throwing; `valid: true` means the config is structurally sound (it does
391
+ * not check that named guards/actions/actors are implemented, which
392
+ * `fromConfig` does). The compiled validator is cached across calls.
393
+ */
394
+ function validateAgentConfig(config) {
395
+ const validate = getWorkflowValidator();
396
+ if (validate(config)) return {
397
+ valid: true,
398
+ errors: []
399
+ };
400
+ return {
401
+ valid: false,
402
+ errors: (validate.errors ?? []).map((error) => ({
403
+ severity: "error",
404
+ path: error.instancePath || "/",
405
+ message: error.message ?? "is invalid",
406
+ keyword: error.keyword
407
+ }))
408
+ };
409
+ }
410
+ //#endregion
411
+ export { validateAgentConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statelyai/agent",
3
- "version": "2.0.0-alpha.17",
3
+ "version": "2.0.0-alpha.19",
4
4
  "description": "Make invalid agent actions impossible. Agent logic as state machines: deterministic, inspectable, resumable, runs anywhere.",
5
5
  "keywords": [
6
6
  "agent",
@@ -68,6 +68,16 @@
68
68
  "default": "./dist/otel.cjs"
69
69
  }
70
70
  },
71
+ "./validate": {
72
+ "import": {
73
+ "types": "./dist/validate.d.mts",
74
+ "default": "./dist/validate.mjs"
75
+ },
76
+ "require": {
77
+ "types": "./dist/validate.d.cts",
78
+ "default": "./dist/validate.cjs"
79
+ }
80
+ },
71
81
  "./sqlite": {
72
82
  "import": {
73
83
  "types": "./dist/sqlite.d.mts",
@@ -127,6 +137,7 @@
127
137
  "peerDependencies": {
128
138
  "@opentelemetry/api": "^1",
129
139
  "ai": "^6.0.67",
140
+ "ajv": "^8.20.0",
130
141
  "xstate": ">=6.0.0-alpha.25 <6.0.0"
131
142
  },
132
143
  "peerDependenciesMeta": {
@@ -135,6 +146,9 @@
135
146
  },
136
147
  "ai": {
137
148
  "optional": true
149
+ },
150
+ "ajv": {
151
+ "optional": true
138
152
  }
139
153
  },
140
154
  "engines": {
@@ -71,8 +71,8 @@
71
71
  "$ref": "#/$defs/State"
72
72
  }
73
73
  },
74
- "suspendedTags": {
75
- "description": "State tags that mark an intentional wait for an external event (a human approval, an inbound webhook, ...). Lowered by setupAgent.fromConfig(...) into a snapshot.hasTag(...) suspension predicate so runAgent settles those states idle deterministically. Every listed tag must appear in some state's 'tags'.",
74
+ "idleTags": {
75
+ "description": "State tags that mark an intentional wait for an external event (a human approval, an inbound webhook, ...). Lowered by setupAgent.fromConfig(...) into a snapshot.hasTag(...) idle predicate so runAgent settles those states idle deterministically. Every listed tag must appear in some state's 'tags'.",
76
76
  "type": "array",
77
77
  "items": {
78
78
  "type": "string",
@@ -8,7 +8,7 @@ description: Author a @statelyai/agent workflow as JSON and check it before it r
8
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
9
 
10
10
  ```
11
- author → validate (Ajv 2020) → lower (fromConfig) → lint (assertAgentMachine) → simulate → hand back
11
+ author → validate (validateAgentConfig) → lower (fromConfig) → lint (lintAgentMachine) → simulate → hand back
12
12
  ```
13
13
 
14
14
  ## 1. Read the schema
@@ -132,28 +132,26 @@ Reference config — decision, text request, idle human step, one final state:
132
132
  }
133
133
  ```
134
134
 
135
- ## 3. Validate with Ajv 2020
135
+ ## 3. Validate with `validateAgentConfig`
136
136
 
137
- The workflow schema is draft 2020-12, so it needs Ajv's 2020 build. Run this before anything else touches the config.
137
+ `validateAgentConfig` from `@statelyai/agent/validate` checks a candidate against the shipped workflow schema with Ajv 2020 built in. It needs `ajv` installed (an optional peer of the package). Run it before anything else touches the config.
138
138
 
139
139
  ```ts
140
- import Ajv2020 from "ajv/dist/2020.js";
141
- import workflowSchema from "@statelyai/agent/agent-workflow.json";
140
+ import { validateAgentConfig } from "@statelyai/agent/validate";
142
141
  import type { AgentWorkflowConfig } from "@statelyai/agent";
143
142
 
144
- const validateWorkflow = new Ajv2020({ strict: false }).compile(workflowSchema);
145
-
146
143
  function validateGeneratedConfig(candidate: unknown): AgentWorkflowConfig {
147
- if (validateWorkflow(candidate)) return candidate as AgentWorkflowConfig;
144
+ const result = validateAgentConfig(candidate);
145
+ if (result.valid) return candidate as AgentWorkflowConfig;
148
146
  throw new Error(
149
- (validateWorkflow.errors ?? [])
150
- .map((error) => `${error.instancePath || "(root)"} ${error.message}`)
147
+ result.errors
148
+ .map((error) => `${error.path || "(root)"} ${error.message}`)
151
149
  .join("\n"),
152
150
  );
153
151
  }
154
152
  ```
155
153
 
156
- Ajv errors carry `instancePath`, so a repair prompt can name the exact bad field.
154
+ Each error carries a JSON Pointer `path`, so a repair prompt can name the exact bad field.
157
155
 
158
156
  ## 4. Lower with `fromConfig`
159
157
 
@@ -193,10 +191,10 @@ Lowering is itself a gate: it throws on an unresolved named guard/action and on
193
191
  ## 5. Lint
194
192
 
195
193
  ```ts
196
- import { assertAgentMachine, lintAgentMachine } from "@statelyai/agent";
194
+ import { lintAgentMachine } from "@statelyai/agent";
197
195
 
198
196
  const diagnostics = lintAgentMachine(machine);
199
- assertAgentMachine(machine); // throws AgentLintError on error-severity findings
197
+ lintAgentMachine(machine, { throw: true }); // throws AgentLintError on error-severity findings
200
198
  ```
201
199
 
202
200
  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.
@@ -224,7 +222,7 @@ Derive the script from the config rather than guessing: take the first entry of
224
222
 
225
223
  - `'exhausted'` → the machine loops. Reject it.
226
224
  - `'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`.
225
+ - To cover every branch instead of one path, use `explorePaths` / `canReach` (its result is `{ reachable, witness }`).
228
226
 
229
227
  ## 7. Repair loop
230
228