@statelyai/agent 2.0.0-alpha.28 → 2.0.0-alpha.29

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 (75) hide show
  1. package/dist/ai-sdk.cjs +149 -26
  2. package/dist/ai-sdk.d.cts +57 -34
  3. package/dist/ai-sdk.d.mts +57 -34
  4. package/dist/ai-sdk.mjs +147 -23
  5. package/dist/{decision-RQ0iv0oZ.mjs → decision-Bl_rKQnU.mjs} +73 -7
  6. package/dist/{decision-CmM8-HHz.cjs → decision-oevZnkBe.cjs} +112 -29
  7. package/dist/{text-logic-f5F5sCdc.cjs → event-log-Dtb16p1p.cjs} +813 -6
  8. package/dist/{text-logic-CIqHAmBi.mjs → event-log-GPZO9p7_.mjs} +679 -3
  9. package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CN9ygggF.d.cts} +90 -5
  10. package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-DS836wch.d.mts} +90 -5
  11. package/dist/event-log-store-bCdskPNS.cjs +95 -0
  12. package/dist/event-log-store-yalq8C77.mjs +84 -0
  13. package/dist/index.cjs +571 -58
  14. package/dist/index.d.cts +104 -510
  15. package/dist/index.d.mts +104 -510
  16. package/dist/index.mjs +542 -32
  17. package/dist/log.cjs +77 -20
  18. package/dist/log.d.cts +5 -5
  19. package/dist/log.d.mts +5 -5
  20. package/dist/log.mjs +58 -3
  21. package/dist/machines.cjs +1 -1
  22. package/dist/machines.d.cts +15 -15
  23. package/dist/machines.d.mts +15 -15
  24. package/dist/machines.mjs +1 -1
  25. package/dist/model-source-C7FFP2Sj.mjs +39 -0
  26. package/dist/model-source-DjJPjGSP.cjs +44 -0
  27. package/dist/openai.cjs +10 -10
  28. package/dist/openai.d.cts +27 -25
  29. package/dist/openai.d.mts +27 -25
  30. package/dist/openai.mjs +2 -2
  31. package/dist/otel.cjs +5 -4
  32. package/dist/otel.d.cts +5 -7
  33. package/dist/otel.d.mts +5 -7
  34. package/dist/otel.mjs +5 -3
  35. package/dist/{run-agent-COXXc0RW.d.mts → run-agent-C9ubX8Tx.d.cts} +87 -30
  36. package/dist/{run-agent-xNTxpt82.mjs → run-agent-CuQT1xSS.mjs} +170 -42
  37. package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CxSyZmIE.d.mts} +87 -30
  38. package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-cToZ3K42.cjs} +219 -86
  39. package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DTF3NQg3.cjs} +31 -20
  40. package/dist/setup-agent-DlbgwsIJ.d.cts +577 -0
  41. package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-DniLvDu7.mjs} +15 -9
  42. package/dist/setup-agent-tsc3vm5I.d.mts +577 -0
  43. package/dist/tanstack-ai.cjs +627 -0
  44. package/dist/tanstack-ai.d.cts +204 -0
  45. package/dist/tanstack-ai.d.mts +204 -0
  46. package/dist/tanstack-ai.mjs +618 -0
  47. package/dist/testing.cjs +98 -985
  48. package/dist/testing.d.cts +51 -347
  49. package/dist/testing.d.mts +51 -347
  50. package/dist/testing.mjs +86 -973
  51. package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-9fv1i9aK.d.cts} +50 -10
  52. package/dist/{text-logic-D-czVPYp.d.mts → text-logic-BDbGjYMh.d.mts} +50 -10
  53. package/dist/{types-BgNbNKnJ.d.mts → types-Dp5_Ru4R.d.cts} +2 -2
  54. package/dist/{types-hR5f5YXa.d.cts → types-Dp5_Ru4R.d.mts} +2 -2
  55. package/dist/usage-D8UphgEA.d.mts +17 -0
  56. package/dist/usage-R1NTmLxY.d.cts +17 -0
  57. package/dist/{utils-_q7F-AZu.d.mts → utils-BnXdWih3.d.mts} +1 -2
  58. package/dist/{utils-o4YRdHJX.d.cts → utils-ebVpCYoF.d.cts} +1 -2
  59. package/dist/validate.cjs +239 -8
  60. package/dist/validate.d.cts +110 -3
  61. package/dist/validate.d.mts +110 -3
  62. package/dist/validate.mjs +238 -7
  63. package/dist/verify-B5qGC5WN.mjs +1181 -0
  64. package/dist/verify-CBuYIpsa.d.mts +414 -0
  65. package/dist/verify-Cqghu4gz.cjs +1222 -0
  66. package/dist/verify-DYH_2VzU.d.cts +414 -0
  67. package/package.json +44 -31
  68. package/readme.md +38 -26
  69. package/skills/generate-machine/SKILL.md +140 -66
  70. package/skills/improve-machine/SKILL.md +190 -0
  71. package/skills/improve-machine/reference.md +96 -0
  72. package/dist/event-log-store-2pAFiWuc.mjs +0 -576
  73. package/dist/event-log-store-C-COhIXP.cjs +0 -702
  74. package/dist/usage-BdEjFuIi.mjs +0 -5
  75. package/dist/usage-efOQPdf_.cjs +0 -10
@@ -0,0 +1,1222 @@
1
+ const require_event_log = require("./event-log-Dtb16p1p.cjs");
2
+ const require_decision = require("./decision-oevZnkBe.cjs");
3
+ let xstate = require("xstate");
4
+ //#region src/steps.ts
5
+ /**
6
+ * Internal: the pure `(state, event) => (nextState, requests)` view of an
7
+ * agent machine, used by model-free simulation (`simulateAgent`) and tests.
8
+ * An {@link AgentStep} is a snapshot plus the model requests the machine is
9
+ * now waiting on; nothing here executes anything. Hosts use
10
+ * `createAgentRuntime`.
11
+ * @module
12
+ */
13
+ /** @internal Normalizes current and legacy XState invoke effect shapes. */
14
+ function getInvokeEffectMetadata(action) {
15
+ if (action.type === "@xstate.spawn") return action;
16
+ if (action.type === "xstate.spawnChild") {
17
+ const params = action.params;
18
+ return params ? {
19
+ ...params,
20
+ logic: action.logic
21
+ } : void 0;
22
+ }
23
+ if (action.type === "@xstate.start" && typeof action.src === "string") return action;
24
+ }
25
+ /**
26
+ * The invokes a snapshot is still waiting on, lowered to the same spawn-effect
27
+ * shape {@link getAgentRequests} reads. Derived from the snapshot's live
28
+ * children rather than the last transition's actions, so a state that invokes
29
+ * several actors at once (an `invoke: [...]` array, or parallel regions each
30
+ * invoking) keeps reporting the siblings after the first one settles —
31
+ * including when the settled invoke's `onDone` is targetless and starts
32
+ * nothing new.
33
+ *
34
+ * Entries are ordered by invoke id, so a walker that resolves one invoke per
35
+ * step is deterministic.
36
+ *
37
+ * @internal
38
+ */
39
+ function getPendingInvokes(snapshot) {
40
+ const children = snapshot.children ?? {};
41
+ return Object.keys(children).sort().flatMap((id) => {
42
+ const child = children[id];
43
+ const childSnapshot = child?.getSnapshot?.();
44
+ if (!child || childSnapshot?.status !== "active") return [];
45
+ return [{
46
+ type: "@xstate.spawn",
47
+ id,
48
+ src: child.src,
49
+ input: childSnapshot.input,
50
+ logic: child.logic
51
+ }];
52
+ });
53
+ }
54
+ /**
55
+ * Scans a set of executable actions (as returned by xstate's `transition`/
56
+ * `initialTransition`) for spawned `TextLogic`/`DecisionLogic` invokes and
57
+ * lowers each into an {@link AgentStepRequest}. Pass `options.machine` to
58
+ * pre-fill `schemas`/`actors` from the machine's registered `setupAgent`
59
+ * options instead of passing them by hand.
60
+ * `options.snapshot` is required to resolve a decision's candidate events
61
+ * (intersecting declared `allowedEvents` with what's currently legal) — omit
62
+ * it and decision requests report an empty `events` list.
63
+ *
64
+ * @internal
65
+ */
66
+ function getAgentRequests(actions, { machine, ...options } = {}) {
67
+ if (machine) options = {
68
+ ...require_event_log.getRegisteredAgentExecutionOptions(machine),
69
+ ...options
70
+ };
71
+ return actions.flatMap((action) => {
72
+ const params = getInvokeEffectMetadata(action);
73
+ if (!params || typeof params.src !== "string") return [];
74
+ if (typeof params.id !== "string" || params.id.length === 0) throw new Error(`Agent invoke '${params.src}' must define a durable string id.`);
75
+ const registeredLogic = require_event_log.isTextLogic(params.logic) || require_decision.isDecisionLogic(params.logic) ? params.logic : options.actors?.[params.src];
76
+ if (require_decision.isDecisionLogic(registeredLogic)) {
77
+ const decisionRequest = registeredLogic.request(params.input);
78
+ const allowedEventTypes = registeredLogic.allowedEventTypes?.(params.input);
79
+ const events = options.snapshot ? require_decision.getAcceptedEvents(options.snapshot, {
80
+ events: options.events,
81
+ schemas: options.schemas,
82
+ eventTypes: allowedEventTypes
83
+ }) : [];
84
+ return [{
85
+ ...decisionRequest,
86
+ id: params.id,
87
+ events
88
+ }];
89
+ }
90
+ const textLogic = require_event_log.isTextLogic(registeredLogic) ? registeredLogic : void 0;
91
+ const input = textLogic ? textLogic.request(params.input) : void 0;
92
+ if (!input) return [];
93
+ return [{
94
+ kind: "text",
95
+ id: params.id,
96
+ src: params.src,
97
+ ...textLogic ? { mode: textLogic.mode } : {},
98
+ input,
99
+ tools: input.tools ?? {},
100
+ events: []
101
+ }];
102
+ });
103
+ }
104
+ /**
105
+ * Builds the canonical `xstate.done.actor` event xstate's `transition()`
106
+ * expects to resolve a spawned invoke — including the child session identity
107
+ * that rejects a stale completion from an earlier incarnation.
108
+ *
109
+ * @internal
110
+ */
111
+ function doneEvent(snapshot, request, output) {
112
+ const id = typeof request === "string" ? request : request.id;
113
+ const child = snapshot.children[id];
114
+ return {
115
+ type: "xstate.done.actor",
116
+ actorId: id,
117
+ ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
118
+ output
119
+ };
120
+ }
121
+ /**
122
+ * The `xstate.error.actor` counterpart of {@link doneEvent} — the event a
123
+ * failed invoke delivers, which `onError` transitions observe. Carries the
124
+ * same child session identity, so xstate does not discard it as stale.
125
+ *
126
+ * @internal
127
+ */
128
+ function errorEvent(snapshot, request, error) {
129
+ const id = typeof request === "string" ? request : request.id;
130
+ const child = snapshot.children[id];
131
+ return {
132
+ type: "xstate.error.actor",
133
+ actorId: id,
134
+ ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
135
+ error
136
+ };
137
+ }
138
+ /**
139
+ * Applies a request's `output` as a done event (or, with `outcome: 'error'`,
140
+ * its `output` as the invoke's error) via `transition(...)`, returning the raw
141
+ * `[snapshot, actions]` tuple. Lower-level than {@link resolveAgentStep} /
142
+ * {@link rejectAgentStep} — those helpers wrap this and also run
143
+ * {@link getAgentRequests} to produce the next {@link AgentStep}.
144
+ *
145
+ * @internal
146
+ */
147
+ function transitionResult(logic, snapshot, request, output, outcome = "output") {
148
+ const event = outcome === "error" ? errorEvent(snapshot, request, output) : doneEvent(snapshot, request, output);
149
+ const result = (0, xstate.transition)(logic, snapshot, event);
150
+ applyFinalStateOutput(logic, result[0], event);
151
+ return result;
152
+ }
153
+ /**
154
+ * Starts a machine and returns its first {@link AgentStep} — the step-path
155
+ * equivalent of `initialTransition` plus request discovery. Begins the
156
+ * durable/per-model-call-checkpoint loop: resolve each `step.requests` entry
157
+ * (via {@link executeAgentRequest} for `kind: 'text'`, or
158
+ * {@link resolveDecision} for `kind: 'decision'`), then advance with
159
+ * {@link resolveAgentStep} or {@link transitionAgentStep}.
160
+ */
161
+ function initialAgentStep(machine, input, options) {
162
+ const [snapshot, actions] = (0, xstate.initialTransition)(machine, input);
163
+ return createAgentStep(machine, snapshot, actions, require_event_log.getRegisteredAgentExecutionOptions(machine, options));
164
+ }
165
+ /**
166
+ * Applies an externally-sent event (e.g. a decision's chosen event, or a
167
+ * human's reply) and returns the next {@link AgentStep}. Accepts **either**
168
+ * a raw snapshot **or** a prior `AgentStep` as the second argument —
169
+ * `.snapshot` is unwrapped automatically, so callers can thread the whole
170
+ * step object through without manually plucking the snapshot out.
171
+ */
172
+ function transitionAgentStep(machine, snapshotOrStep, event, options) {
173
+ const snapshot = isAgentStep(snapshotOrStep) ? snapshotOrStep.snapshot : snapshotOrStep;
174
+ const [nextSnapshot, actions] = (0, xstate.transition)(machine, snapshot, event);
175
+ return createAgentStep(machine, nextSnapshot, actions, require_event_log.getRegisteredAgentExecutionOptions(machine, options));
176
+ }
177
+ /**
178
+ * Applies a resolved text request's output (a `kind: 'text'`
179
+ * {@link AgentRequest} — not a decision) as a done event and returns the
180
+ * next {@link AgentStep}. For decisions, resolve with `resolveDecision`
181
+ * (which returns a {@link ChosenEvent}) and apply it with
182
+ * {@link transitionAgentStep} instead — a decision has no output value of
183
+ * its own to feed here.
184
+ */
185
+ function resolveAgentStep(machine, step, request, output, options) {
186
+ const [snapshot, actions] = transitionResult(machine, step.snapshot, request, output);
187
+ return createAgentStep(machine, snapshot, actions, require_event_log.getRegisteredAgentExecutionOptions(machine, options));
188
+ }
189
+ /**
190
+ * Rejects a pending invoke — the failure counterpart of
191
+ * {@link resolveAgentStep}. Delivers `error` as the invoke's
192
+ * `xstate.error.actor` event, so the machine takes that invoke's `onError`
193
+ * transition and returns the next {@link AgentStep}. With no `onError` in
194
+ * scope the returned step's snapshot has `status: 'error'`, exactly as a live
195
+ * run's failed invoke does.
196
+ *
197
+ * A durable host replays a failed call with this: the log says the call
198
+ * failed, so the replay rejects the invoke instead of resolving it. Scripted
199
+ * playthroughs use it too — see `simulateAgent`'s `script.errors`.
200
+ *
201
+ * `error` can be any value. An `Error` instance is the usual one, but a plain
202
+ * object (`{ code: 'truncated' }`) reaches the `onError` transition as
203
+ * `event.error` untouched, so a machine that branches on a failure code is
204
+ * testable without constructing a provider's error class.
205
+ */
206
+ function rejectAgentStep(machine, step, request, error, options) {
207
+ const [snapshot, actions] = transitionResult(machine, step.snapshot, request, error, "error");
208
+ return createAgentStep(machine, snapshot, actions, require_event_log.getRegisteredAgentExecutionOptions(machine, options));
209
+ }
210
+ function createAgentStep(machine, snapshot, actions, options) {
211
+ applyFinalStateOutput(machine, snapshot);
212
+ return {
213
+ snapshot,
214
+ actions,
215
+ requests: getAgentRequests(getPendingInvokes(snapshot), {
216
+ ...options,
217
+ snapshot
218
+ }),
219
+ done: snapshot.status === "done"
220
+ };
221
+ }
222
+ function resolveStateValueConfig(config, value) {
223
+ if (typeof value === "string") return config.states?.[value];
224
+ if (!value || typeof value !== "object") return;
225
+ for (const [key, childValue] of Object.entries(value)) {
226
+ const childConfig = config.states?.[key];
227
+ if (!childConfig) continue;
228
+ if (childConfig.type === "final") return childConfig;
229
+ const nested = resolveStateValueConfig(childConfig, childValue);
230
+ if (nested) return nested;
231
+ }
232
+ }
233
+ function applyFinalStateOutput(logic, snapshot, event) {
234
+ const machineSnapshot = snapshot;
235
+ if (machineSnapshot.status !== "done" || machineSnapshot.output !== void 0 || !("config" in logic)) return;
236
+ const config = logic.config;
237
+ if (!config) return;
238
+ const output = resolveStateValueConfig(config, machineSnapshot.value)?.output;
239
+ if (output === void 0) return;
240
+ machineSnapshot.output = typeof output === "function" ? output({
241
+ context: machineSnapshot.context,
242
+ event
243
+ }) : output;
244
+ }
245
+ function isAgentStep(value) {
246
+ return !!value && typeof value === "object" && "snapshot" in value && "actions" in value && "requests" in value;
247
+ }
248
+ //#endregion
249
+ //#region src/verify.ts
250
+ /**
251
+ * Provider-free verification for agent machines — static lint checks plus
252
+ * model-free simulation over the pure step path. Coding agents generate these
253
+ * machines; this module lets them close the loop and self-verify WITHOUT any
254
+ * API keys or model calls.
255
+ *
256
+ * - {@link lintAgentMachine} — static structural checks over a built machine
257
+ * (works for TS-authored and `setupAgent.fromConfig`-compiled machines).
258
+ * - {@link simulateAgent} — a deterministic, scripted playthrough.
259
+ * - {@link explorePaths} — enumerates decision/external branches to a bounded
260
+ * depth and reports reached states and terminal outcomes.
261
+ * - {@link canReach} — a thin wrapper over {@link explorePaths} answering
262
+ * "can this state (or snapshot predicate) be reached?" with a witness path.
263
+ *
264
+ * @module
265
+ */
266
+ const DECIDE_SRC = "agent.decide";
267
+ function normalizeInvokes(invoke) {
268
+ if (invoke === void 0 || invoke === null) return [];
269
+ return Array.isArray(invoke) ? invoke : [invoke];
270
+ }
271
+ function buildStateIndex(rootConfig) {
272
+ const index = /* @__PURE__ */ new Map();
273
+ const walk = (states, parentPath) => {
274
+ for (const [name, config] of Object.entries(states ?? {})) {
275
+ const path = parentPath ? `${parentPath}.${name}` : name;
276
+ const hasChildren = !!config.states && Object.keys(config.states).length > 0;
277
+ index.set(path, {
278
+ path,
279
+ config,
280
+ parentPath,
281
+ isFinal: config.type === "final",
282
+ isParallel: config.type === "parallel",
283
+ isCompound: hasChildren && config.type !== "parallel",
284
+ invokes: normalizeInvokes(config.invoke)
285
+ });
286
+ if (hasChildren) walk(config.states, path);
287
+ }
288
+ };
289
+ walk(rootConfig.states, "");
290
+ return index;
291
+ }
292
+ function ancestorChain(node, index) {
293
+ const chain = [];
294
+ let parent = node.parentPath;
295
+ while (parent) {
296
+ const parentNode = index.get(parent);
297
+ if (!parentNode) break;
298
+ chain.push(parentNode);
299
+ parent = parentNode.parentPath;
300
+ }
301
+ return chain;
302
+ }
303
+ function hasNonEmptyOn(config) {
304
+ return !!config.on && Object.keys(config.on).length > 0;
305
+ }
306
+ function isDecisionInvoke(src, actors) {
307
+ if (typeof src === "string") return src === DECIDE_SRC || require_decision.isDecisionLogic(actors[src]);
308
+ return require_decision.isDecisionLogic(src);
309
+ }
310
+ function isAgentLogicNeedingBinding(src) {
311
+ return (require_event_log.isTextLogic(src) || require_decision.isDecisionLogic(src)) && !require_event_log.executorBoundLogics.has(src);
312
+ }
313
+ function safeJsonSchema(schema) {
314
+ try {
315
+ return require_event_log.getJsonSchemaSync(schema);
316
+ } catch {
317
+ return;
318
+ }
319
+ }
320
+ function checkDecideWithoutEvents(ctx) {
321
+ const out = [];
322
+ for (const node of ctx.index.values()) for (const invoke of node.invokes) {
323
+ if (!isDecisionInvoke(invoke.src, ctx.actors)) continue;
324
+ const selfHandles = hasNonEmptyOn(node.config);
325
+ const ancestorHandles = ancestorChain(node, ctx.index).some((ancestor) => hasNonEmptyOn(ancestor.config));
326
+ const invokeObserves = invoke.onDone !== void 0;
327
+ if (selfHandles || ancestorHandles || invokeObserves) continue;
328
+ const srcName = typeof invoke.src === "string" ? invoke.src : "(inline logic)";
329
+ out.push({
330
+ code: "decide-without-events",
331
+ severity: "error",
332
+ path: node.path,
333
+ message: `State '${node.path}' invokes decision source '${srcName}', but neither it nor any ancestor handles any event (no 'on:'), so the decision's chosen event can never be delivered. Add an 'on:' handler for the candidate events.`
334
+ });
335
+ }
336
+ return out;
337
+ }
338
+ function checkDirectObjectSrc(ctx) {
339
+ const out = [];
340
+ for (const node of ctx.index.values()) for (const invoke of node.invokes) {
341
+ const src = invoke.src;
342
+ if (typeof src === "string" || !src || typeof src !== "object") continue;
343
+ if (!isAgentLogicNeedingBinding(src)) continue;
344
+ out.push({
345
+ code: "direct-object-src",
346
+ severity: "warning",
347
+ path: node.path,
348
+ message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by the agent runtime, so they inherit no host executors — call '.withExecutor(...)' on the logic, or register it as a string-keyed actor source (machine.provide({ actors: { name: logic } })) and invoke it by name.`
349
+ });
350
+ }
351
+ return out;
352
+ }
353
+ function handlesActorError(config) {
354
+ if (config["onError"] !== void 0) return true;
355
+ return Object.keys(config.on ?? {}).some((type) => type === "*" || type.startsWith("xstate.error"));
356
+ }
357
+ function checkInvokeWithoutOnError(ctx) {
358
+ const out = [];
359
+ for (const node of ctx.index.values()) for (const invoke of node.invokes) {
360
+ if (invoke.onError !== void 0 || handlesActorError(node.config)) continue;
361
+ if (handlesActorError(ctx.config) || ancestorChain(node, ctx.index).some((ancestor) => handlesActorError(ancestor.config))) continue;
362
+ const srcName = typeof invoke.src === "string" ? invoke.src : "(inline logic)";
363
+ out.push({
364
+ code: "invoke-without-on-error",
365
+ severity: "warning",
366
+ path: node.path,
367
+ message: `State '${node.path}' invokes '${srcName}' with no 'onError' transition, and no ancestor handles an actor error. A rejected request or actor puts the machine in an error state with no modeled recovery. Add 'onError' (a 'failed' final state, or a bounded retry).`
368
+ });
369
+ }
370
+ return out;
371
+ }
372
+ const LINT_CHECKS = [
373
+ checkDecideWithoutEvents,
374
+ checkDirectObjectSrc,
375
+ checkInvokeWithoutOnError
376
+ ];
377
+ /**
378
+ * Runs static structural checks over a built agent machine and returns the
379
+ * findings ({@link AgentLintDiagnostic}[], empty when clean). Works for
380
+ * TS-authored (`setupAgent(...).createMachine(...)`) and
381
+ * `setupAgent.fromConfig(...)`-compiled machines alike, reading `machine.config`
382
+ * plus the schemas/actor sources the library already retains per machine.
383
+ *
384
+ * No model calls, no API keys — a coding agent that emits an agent machine can
385
+ * call this to catch agent-specific mistakes such as undeliverable decisions,
386
+ * un-rebindable request sources, and an unhandled transcript event. General
387
+ * state-machine lint belongs to XState tooling.
388
+ *
389
+ * Pass `{ throw: true }` for the one-liner form used in tests and generation
390
+ * loops: it returns silently when the machine is clean and throws
391
+ * {@link AgentLintError} (findings on `.diagnostics`) on error-severity
392
+ * findings, or on warnings too with `{ throw: true, warnings: true }`.
393
+ *
394
+ * @example
395
+ * ```ts
396
+ * const errors = lintAgentMachine(machine).filter((d) => d.severity === 'error');
397
+ * if (errors.length) throw new Error(errors.map((e) => `${e.path}: ${e.message}`).join('\n'));
398
+ * ```
399
+ *
400
+ * @example Throwing form
401
+ * ```ts
402
+ * test('agent machine is structurally sound', () => {
403
+ * lintAgentMachine(machine, { throw: true });
404
+ * });
405
+ * ```
406
+ */
407
+ function lintAgentMachine(machine, options = {}) {
408
+ const config = machine.config ?? {};
409
+ const index = buildStateIndex(config);
410
+ const registered = require_event_log.getRegisteredAgentExecutionOptions(machine);
411
+ const ctx = {
412
+ config,
413
+ index,
414
+ schemas: registered.schemas,
415
+ actors: registered.actors ?? machine.sources?.actors ?? {}
416
+ };
417
+ const disabled = new Set(options.disable ?? []);
418
+ const diagnostics = LINT_CHECKS.flatMap((check) => check(ctx)).filter((d) => !disabled.has(d.code));
419
+ if (options.throw) {
420
+ const failing = options.warnings ? diagnostics : diagnostics.filter((d) => d.severity === "error");
421
+ if (failing.length > 0) throw new AgentLintError(machine.id ?? "(machine)", failing);
422
+ }
423
+ return diagnostics;
424
+ }
425
+ /**
426
+ * Thrown by `lintAgentMachine(machine, { throw: true })` (and its
427
+ * {@link assertAgentMachine} alias) when lint finds failing diagnostics.
428
+ * `diagnostics` holds the findings; the message lists them one per finding,
429
+ * so a test runner's failure output reads like the CLI's lint report.
430
+ */
431
+ var AgentLintError = class extends require_event_log.AgentError {
432
+ diagnostics;
433
+ constructor(machineId, diagnostics) {
434
+ const lines = diagnostics.map((d) => ` ${d.severity === "error" ? "error" : "warn "} ${d.code} ${d.path}\n ${d.message}`);
435
+ super("lint-failed", `Agent machine '${machineId}' failed lint (${diagnostics.length} finding(s)):\n${lines.join("\n")}`);
436
+ this.name = "AgentLintError";
437
+ this.diagnostics = diagnostics;
438
+ }
439
+ };
440
+ /**
441
+ * Asserts a machine passes {@link lintAgentMachine}: returns silently when
442
+ * clean, throws {@link AgentLintError} (with the findings on `.diagnostics`)
443
+ * otherwise — sugar for `lintAgentMachine(machine, { ...options, throw: true })`.
444
+ * Fails on error-severity findings; set `warnings: true` to fail on warnings
445
+ * too. The one-liner for tests and generation loops:
446
+ *
447
+ * @example
448
+ * ```ts
449
+ * test('agent machine is structurally sound', () => {
450
+ * assertAgentMachine(machine);
451
+ * });
452
+ * ```
453
+ */
454
+ function assertAgentMachine(machine, options = {}) {
455
+ lintAgentMachine(machine, {
456
+ ...options,
457
+ throw: true
458
+ });
459
+ }
460
+ function pendingInvokes(step) {
461
+ const out = [];
462
+ for (const metadata of getPendingInvokes(step.snapshot)) if (typeof metadata.src === "string") out.push({
463
+ id: metadata.id,
464
+ src: metadata.src
465
+ });
466
+ return out;
467
+ }
468
+ function takeFromQueue(channel, src) {
469
+ const queue = channel?.[src];
470
+ if (queue && queue.length > 0) return {
471
+ found: true,
472
+ value: queue.shift()
473
+ };
474
+ return { found: false };
475
+ }
476
+ /**
477
+ * Deterministically plays a machine through, resolving each request from a
478
+ * {@link SimulationScript} instead of a model — no API keys, no model calls.
479
+ * Runs on the pure step path ({@link initialAgentStep} etc.), so it exercises
480
+ * the real transition logic. Returns the terminal `status`, final `snapshot`,
481
+ * and a `trail` of every step taken.
482
+ *
483
+ * Decisions run through the live run's own validation/retry core,
484
+ * {@link resolveDecision}, with the script standing in for the model: each
485
+ * attempt consumes the next queued {@link ChosenEvent} for that src, so an
486
+ * unknown, payload-invalid, or guard-rejected event is NOT silently swallowed
487
+ * — the next queued decision is tried, exactly as a live run re-asks the
488
+ * model. The decision logic's `maxRetries` caps attempts as it would live;
489
+ * when retries continue past the end of the queue, the last queued decision
490
+ * repeats (a scripted model that insists). The repeat applies only within one
491
+ * decision request's retries — each new decision request must have its own
492
+ * queued entry, or the dry-script error throws as usual. Exhausting all
493
+ * attempts delivers
494
+ * the resulting {@link AgentDecisionExhaustedError} to the machine as the
495
+ * decision invoke's error (so an `onError` transition observes it, as it
496
+ * would live); with no `onError` to catch it, it is thrown.
497
+ *
498
+ * Throws a descriptive error when the script runs dry mid-request, naming the
499
+ * pending request's kind, src, and id so the missing scripted response is
500
+ * obvious.
501
+ *
502
+ * @example
503
+ * ```ts
504
+ * const { status, snapshot } = simulateAgent(machine, {
505
+ * input: { topic: 'state machines' },
506
+ * script: {
507
+ * decisions: { 'agent.decide': [{ type: 'ESCALATE' }] },
508
+ * events: [{ type: 'APPROVE' }], // crosses the human gate
509
+ * },
510
+ * });
511
+ * ```
512
+ */
513
+ async function simulateAgent(machine, options) {
514
+ const maxSteps = options.maxSteps ?? 100;
515
+ const script = {
516
+ text: mapValues(options.script.text ?? {}, (arr) => [...arr]),
517
+ decisions: mapValues(options.script.decisions ?? {}, (arr) => [...arr]),
518
+ invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr]),
519
+ errors: mapValues(options.script.errors ?? {}, (arr) => [...arr]),
520
+ events: [...options.script.events ?? []]
521
+ };
522
+ let step = initialAgentStep(machine, options.input);
523
+ const trail = [{ state: step.snapshot.value }];
524
+ for (let i = 0; i < maxSteps; i++) {
525
+ if (step.done) return {
526
+ status: "done",
527
+ snapshot: step.snapshot,
528
+ trail
529
+ };
530
+ const request = step.requests[0];
531
+ if (request) {
532
+ if (request.kind === "decision") {
533
+ const invokeMeta = findInvokeMetadata(step, request.id);
534
+ const src = invokeMeta?.src ?? request.id;
535
+ const decisionSrc = src in (script.decisions ?? {}) ? src : request.id in (script.decisions ?? {}) ? request.id : src;
536
+ const errorSrc = queueKey(script.errors, src, request.id);
537
+ const failure = takeFromQueue(script.errors, errorSrc);
538
+ if (failure.found) {
539
+ step = rejectScriptedRequest(machine, step, request.id, failure.value, {
540
+ kind: "decision",
541
+ src: errorSrc,
542
+ trail
543
+ });
544
+ continue;
545
+ }
546
+ step = await applyScriptedDecision(machine, step, request, decisionSrc, invokeMeta?.logic, script, trail);
547
+ continue;
548
+ }
549
+ const failure = takeFromQueue(script.errors, request.src);
550
+ if (failure.found) {
551
+ step = rejectScriptedRequest(machine, step, request, failure.value, {
552
+ kind: "text",
553
+ src: request.src,
554
+ trail
555
+ });
556
+ continue;
557
+ }
558
+ const taken = takeFromQueue(script.text, request.src);
559
+ if (!taken.found) throw scriptDryError("text", request.src, request.id);
560
+ step = resolveAgentStep(machine, step, request, asTextResult(taken.value));
561
+ trail.push({
562
+ state: step.snapshot.value,
563
+ resolvedRequest: {
564
+ kind: "text",
565
+ src: request.src,
566
+ id: request.id,
567
+ outcome: "output"
568
+ }
569
+ });
570
+ continue;
571
+ }
572
+ const [invoke] = pendingInvokes(step);
573
+ if (invoke) {
574
+ const failure = takeFromQueue(script.errors, invoke.src);
575
+ if (failure.found) {
576
+ step = rejectScriptedRequest(machine, step, invoke.id, failure.value, {
577
+ kind: "invoke",
578
+ src: invoke.src,
579
+ trail
580
+ });
581
+ continue;
582
+ }
583
+ const taken = takeFromQueue(script.invokes, invoke.src);
584
+ if (!taken.found) throw scriptDryError("invoke", invoke.src, invoke.id);
585
+ step = resolveAgentStep(machine, step, invoke.id, taken.value);
586
+ trail.push({
587
+ state: step.snapshot.value,
588
+ resolvedRequest: {
589
+ kind: "invoke",
590
+ src: invoke.src,
591
+ id: invoke.id,
592
+ outcome: "output"
593
+ }
594
+ });
595
+ continue;
596
+ }
597
+ const external = script.events;
598
+ if (external.length > 0) {
599
+ const event = external.shift();
600
+ if (!step.snapshot.can(event)) throw new Error(`simulateAgent: scripted external event '${event.type}' cannot be taken in state ${JSON.stringify(step.snapshot.value)} (no handler, or its guard rejected it). A live run's send would be silently dropped here; fix the script's \`events\` queue.`);
601
+ step = transitionAgentStep(machine, step, event);
602
+ trail.push({
603
+ state: step.snapshot.value,
604
+ appliedEvent: event,
605
+ external: true
606
+ });
607
+ continue;
608
+ }
609
+ return {
610
+ status: "idle",
611
+ snapshot: step.snapshot,
612
+ trail
613
+ };
614
+ }
615
+ return {
616
+ status: "exhausted",
617
+ snapshot: step.snapshot,
618
+ trail
619
+ };
620
+ }
621
+ function queueKey(channel, src, id) {
622
+ return channel && !(src in channel) && id in channel ? id : src;
623
+ }
624
+ function rejectScriptedRequest(machine, step, request, error, entry) {
625
+ const id = typeof request === "string" ? request : request.id;
626
+ const next = rejectAgentStep(machine, step, request, error);
627
+ if (next.snapshot.status === "error") throw unhandledScriptedError(error, entry.kind, entry.src, id);
628
+ entry.trail.push({
629
+ state: next.snapshot.value,
630
+ resolvedRequest: {
631
+ kind: entry.kind,
632
+ src: entry.src,
633
+ id,
634
+ outcome: "error",
635
+ error
636
+ }
637
+ });
638
+ return next;
639
+ }
640
+ function unhandledScriptedError(error, kind, src, id) {
641
+ if (error instanceof Error) return error;
642
+ return new Error(`simulateAgent: the scripted error for the ${kind} request '${src}' (id '${id}') reached no onError transition, so the machine errored. Add an onError to that invoke, or drop the errors['${src}'] entry.`, { cause: error });
643
+ }
644
+ async function applyScriptedDecision(machine, step, request, decisionSrc, invokeLogic, script, trail) {
645
+ const dequeued = [];
646
+ const decide = async () => {
647
+ const taken = takeFromQueue(script.decisions, decisionSrc);
648
+ if (taken.found) {
649
+ dequeued.push(taken.value);
650
+ return { event: taken.value };
651
+ }
652
+ if (dequeued.length === 0) throw scriptDryError("decision", decisionSrc, request.id, request);
653
+ return { event: dequeued[dequeued.length - 1] };
654
+ };
655
+ const logic = require_decision.isDecisionLogic(invokeLogic) ? invokeLogic : resolveRegisteredDecisionLogic(machine, decisionSrc);
656
+ const maxRetries = request.maxRetries ?? logic?.maxRetries;
657
+ const can = (event) => step.snapshot.can(event);
658
+ const options = require_decision.acceptedDecisionOptions(request, can);
659
+ const offered = options ? {
660
+ ...request,
661
+ options
662
+ } : request;
663
+ let chosen;
664
+ try {
665
+ chosen = await require_decision.resolveDecision(offered, { decide }, {
666
+ maxRetries,
667
+ canTake: can
668
+ });
669
+ } catch (error) {
670
+ if (!(error instanceof require_decision.AgentDecisionExhaustedError)) throw error;
671
+ const next = rejectAgentStep(machine, step, request.id, error);
672
+ if (next.snapshot.status === "error") throw error;
673
+ trail.push({
674
+ state: next.snapshot.value,
675
+ rejectedEvents: dequeued
676
+ });
677
+ return next;
678
+ }
679
+ const next = transitionAgentStep(machine, step, chosen);
680
+ const rejected = dequeued.slice(0, -1);
681
+ trail.push({
682
+ state: next.snapshot.value,
683
+ appliedEvent: chosen,
684
+ ...rejected.length > 0 ? { rejectedEvents: rejected } : {}
685
+ });
686
+ return next;
687
+ }
688
+ function findInvokeMetadata(step, id) {
689
+ for (const metadata of getPendingInvokes(step.snapshot)) {
690
+ if (metadata.id !== id) continue;
691
+ return {
692
+ ...typeof metadata.src === "string" ? { src: metadata.src } : {},
693
+ logic: metadata.logic ?? (typeof metadata.src === "object" ? metadata.src : void 0)
694
+ };
695
+ }
696
+ }
697
+ function resolveRegisteredDecisionLogic(machine, src) {
698
+ const candidate = require_event_log.getRegisteredAgentExecutionOptions(machine).actors?.[src] ?? machine.sources?.actors?.[src];
699
+ return require_decision.isDecisionLogic(candidate) ? candidate : void 0;
700
+ }
701
+ function asTextResult(value) {
702
+ return {
703
+ result: value,
704
+ messages: []
705
+ };
706
+ }
707
+ function mapValues(obj, fn) {
708
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, fn(value)]));
709
+ }
710
+ function scriptDryError(kind, src, id, request) {
711
+ const events = request?.kind === "decision" ? ` Candidate events: ${request.events.map((e) => e.type).join(", ") || "(none)"}.` : "";
712
+ const key = kind === "text" ? `text['${src}']` : kind === "decision" ? `decisions['${src}']` : `invokes['${src}']`;
713
+ return /* @__PURE__ */ new Error(`simulateAgent: script ran dry on a pending ${kind} request for src '${src}' (id '${id}'). Add an entry to the script's \`${key}\` queue.${events}`);
714
+ }
715
+ const MAX_ADVANCE_STEPS = 1e3;
716
+ const FINITE_PAYLOAD_CAP = 64;
717
+ const DEFAULT_MAX_NODES = 1e5;
718
+ function finiteDomain(schema) {
719
+ if (!require_decision.isRecord(schema)) return;
720
+ if ("const" in schema) return [schema["const"]];
721
+ if (Array.isArray(schema["enum"])) return schema["enum"];
722
+ const union = schema["anyOf"] ?? schema["oneOf"];
723
+ if (Array.isArray(union)) {
724
+ const parts = union.map(finiteDomain);
725
+ return parts.every((part) => part !== void 0) ? [...new Set(parts.flat())] : void 0;
726
+ }
727
+ const types = Array.isArray(schema["type"]) ? schema["type"] : [schema["type"]];
728
+ if (types.every((type) => type === "boolean" || type === "null")) return types.flatMap((type) => type === "boolean" ? [true, false] : [null]);
729
+ }
730
+ const OMITTED = Symbol("omitted");
731
+ function finitePayloads(jsonSchema) {
732
+ const bare = {
733
+ payloads: [{}],
734
+ complete: false
735
+ };
736
+ if (!require_decision.isRecord(jsonSchema) || jsonSchema["type"] !== "object" && !require_decision.isRecord(jsonSchema["properties"])) return bare;
737
+ const properties = require_decision.isRecord(jsonSchema["properties"]) ? jsonSchema["properties"] : {};
738
+ const required = new Set(Array.isArray(jsonSchema["required"]) ? jsonSchema["required"] : []);
739
+ if ([...required].some((key) => key !== "type" && !(key in properties))) return bare;
740
+ let product = [{}];
741
+ let complete = true;
742
+ for (const [key, schema] of Object.entries(properties)) {
743
+ if (key === "type") continue;
744
+ const domain = finiteDomain(schema);
745
+ if (!domain || domain.length === 0) {
746
+ if (required.has(key)) return bare;
747
+ complete = false;
748
+ continue;
749
+ }
750
+ const choices = required.has(key) ? domain : [OMITTED, ...domain];
751
+ if (product.length * choices.length > FINITE_PAYLOAD_CAP) return bare;
752
+ product = product.flatMap((payload) => choices.map((value) => value === OMITTED ? payload : {
753
+ ...payload,
754
+ [key]: value
755
+ }));
756
+ }
757
+ return {
758
+ payloads: product,
759
+ complete
760
+ };
761
+ }
762
+ function mayReadPayload(snapshot, type) {
763
+ return (0, xstate.getNextTransitions)(snapshot).some((transition) => transition.eventType === type && (transition.to !== void 0 || transition.guard !== void 0 || transition.matches !== void 0 || typeof transition.context === "function"));
764
+ }
765
+ function explore(machine, options, stopWhen) {
766
+ const maxDepth = options.maxDepth ?? 8;
767
+ const maxPaths = options.maxPaths ?? 200;
768
+ const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES;
769
+ const errorScript = options.errors ?? {};
770
+ const outputScript = options.outputs ?? {};
771
+ const eventSchemas = require_event_log.getRegisteredAgentExecutionOptions(machine).schemas?.events ?? machine.schemas?.events ?? {};
772
+ const finiteCache = /* @__PURE__ */ new Map();
773
+ const schemaPayloads = (type, snapshot) => {
774
+ const schema = eventSchemas[type];
775
+ let fill = finiteCache.get(type);
776
+ if (schema && !fill) {
777
+ fill = finitePayloads(safeJsonSchema(schema));
778
+ finiteCache.set(type, fill);
779
+ }
780
+ fill ??= {
781
+ payloads: [{}],
782
+ complete: !mayReadPayload(snapshot, type)
783
+ };
784
+ if (!fill.complete && !unexpanded.has(type)) {
785
+ unexpanded.add(type);
786
+ unexplored.push(schema ? `unexpanded: event '${type}' has payload fields the schema fill cannot enumerate; add a payloads entry for it` : `unexpanded: event '${type}' has no schema and a transition that may read its payload; add a payloads entry for it`);
787
+ }
788
+ return fill.payloads;
789
+ };
790
+ const variantsOf = (type, snapshot, table, requestOptions) => {
791
+ const listed = table[type];
792
+ if (listed) return listed.map((payload) => ({
793
+ ...payload,
794
+ type
795
+ }));
796
+ if (requestOptions) return requestOptions.filter((option) => option.event.type === type).map((option) => option.event);
797
+ return schemaPayloads(type, snapshot).map((payload) => ({
798
+ ...payload,
799
+ type
800
+ }));
801
+ };
802
+ const settleFor = (id, src, isText) => {
803
+ const outputs = outputScript[src] ?? [];
804
+ if (src in errorScript || outputs.length > 1) return { fork: {
805
+ id,
806
+ ...src in errorScript ? { errorKey: src } : {},
807
+ outputs,
808
+ isText
809
+ } };
810
+ return outputs.length === 1 ? { output: outputs[0] } : { blocked: true };
811
+ };
812
+ const reachedStates = /* @__PURE__ */ new Set();
813
+ const reachedValues = [];
814
+ const terminals = [];
815
+ const unexplored = [];
816
+ const unexpanded = /* @__PURE__ */ new Set();
817
+ let prunedByGuard = 0;
818
+ let pathsExplored = 0;
819
+ let nodesExpanded = 0;
820
+ let hitPathCap = false;
821
+ const capped = () => {
822
+ if (pathsExplored >= maxPaths || nodesExpanded >= maxNodes) hitPathCap = true;
823
+ return hitPathCap;
824
+ };
825
+ let witness;
826
+ const visited = /* @__PURE__ */ new Map();
827
+ const recordState = (snapshot) => {
828
+ const key = JSON.stringify(snapshot.value);
829
+ if (!reachedStates.has(key)) {
830
+ reachedStates.add(key);
831
+ reachedValues.push(snapshot.value);
832
+ }
833
+ };
834
+ const initial = initialAgentStep(machine, options.input);
835
+ recordState(initial.snapshot);
836
+ if (stopWhen?.(initial.snapshot)) witness = [];
837
+ const advance = (step) => {
838
+ let current = step;
839
+ for (let i = 0; i < MAX_ADVANCE_STEPS; i++) {
840
+ if (stopWhen?.(current.snapshot)) return {
841
+ step: current,
842
+ hit: true
843
+ };
844
+ if (current.done) return { step: current };
845
+ const request = current.requests[0];
846
+ if (request && request.kind === "text") {
847
+ const settle = settleFor(request.id, request.src, true);
848
+ if ("fork" in settle) return {
849
+ step: current,
850
+ fork: settle.fork
851
+ };
852
+ if ("blocked" in settle) return {
853
+ step: current,
854
+ blockedSrc: request.src
855
+ };
856
+ current = resolveAgentStep(machine, current, request, asTextResult(settle.output));
857
+ recordState(current.snapshot);
858
+ continue;
859
+ }
860
+ if (request && request.kind === "decision") {
861
+ const src = findInvokeMetadata(current, request.id)?.src;
862
+ const errorSrc = src !== void 0 && src in errorScript ? src : request.id in errorScript ? request.id : void 0;
863
+ return errorSrc !== void 0 ? {
864
+ step: current,
865
+ fork: {
866
+ id: request.id,
867
+ errorKey: errorSrc,
868
+ outputs: [],
869
+ isDecision: true
870
+ }
871
+ } : { step: current };
872
+ }
873
+ const [invoke] = pendingInvokes(current);
874
+ if (invoke) {
875
+ const settle = settleFor(invoke.id, invoke.src, false);
876
+ if ("fork" in settle) return {
877
+ step: current,
878
+ fork: settle.fork
879
+ };
880
+ if ("blocked" in settle) return {
881
+ step: current,
882
+ blockedSrc: invoke.src
883
+ };
884
+ current = resolveAgentStep(machine, current, invoke.id, settle.output);
885
+ recordState(current.snapshot);
886
+ continue;
887
+ }
888
+ return { step: current };
889
+ }
890
+ return { step: current };
891
+ };
892
+ function* expand({ step, path, depth }) {
893
+ if (witness !== void 0 || capped()) return;
894
+ nodesExpanded++;
895
+ const { step: settled, blockedSrc, hit, fork } = advance(step);
896
+ if (hit) {
897
+ witness = path;
898
+ return;
899
+ }
900
+ if (options.dedupeBy) {
901
+ const key = options.dedupeBy(settled.snapshot);
902
+ const seenAt = visited.get(key);
903
+ if (seenAt !== void 0 && seenAt <= depth) return;
904
+ visited.set(key, depth);
905
+ }
906
+ if (fork) {
907
+ if (depth >= maxDepth) {
908
+ pathsExplored++;
909
+ terminals.push({
910
+ status: "max-depth",
911
+ path,
912
+ state: settled.snapshot.value
913
+ });
914
+ unexplored.push(`max-depth: stopped at path [${path.map((e) => e.type).join(", ")}]`);
915
+ return;
916
+ }
917
+ if (fork.errorKey !== void 0) {
918
+ const failed = rejectAgentStep(machine, settled, fork.id, errorScript[fork.errorKey]);
919
+ recordState(failed.snapshot);
920
+ yield {
921
+ step: failed,
922
+ path: [...path, { type: `xstate.error.actor.${fork.id}` }],
923
+ depth: depth + 1
924
+ };
925
+ }
926
+ if (!fork.isDecision) {
927
+ for (const output of fork.outputs) {
928
+ if (witness !== void 0 || capped()) return;
929
+ const resolved = resolveAgentStep(machine, settled, fork.id, fork.isText ? asTextResult(output) : output);
930
+ recordState(resolved.snapshot);
931
+ yield {
932
+ step: resolved,
933
+ path: [...path, {
934
+ type: `xstate.done.actor.${fork.id}`,
935
+ output
936
+ }],
937
+ depth: depth + 1
938
+ };
939
+ }
940
+ return;
941
+ }
942
+ if (witness !== void 0) return;
943
+ }
944
+ if (blockedSrc) {
945
+ pathsExplored++;
946
+ terminals.push({
947
+ status: "needs-output",
948
+ path,
949
+ state: settled.snapshot.value,
950
+ missingSrc: blockedSrc
951
+ });
952
+ unexplored.push(`needs-output: no \`outputs\` entry for src '${blockedSrc}' at path [${path.map((e) => e.type).join(", ")}]`);
953
+ return;
954
+ }
955
+ if (settled.done) {
956
+ pathsExplored++;
957
+ terminals.push({
958
+ status: "done",
959
+ path,
960
+ state: settled.snapshot.value
961
+ });
962
+ return;
963
+ }
964
+ if (settled.snapshot.status === "error") {
965
+ pathsExplored++;
966
+ terminals.push({
967
+ status: "error",
968
+ path,
969
+ state: settled.snapshot.value,
970
+ error: settled.snapshot.error
971
+ });
972
+ return;
973
+ }
974
+ const request = settled.requests[0];
975
+ const snapshot = settled.snapshot;
976
+ const table = options.payloads?.(snapshot) ?? {};
977
+ const requestOptions = request?.kind === "decision" ? require_decision.acceptedDecisionOptions(request, () => true) : void 0;
978
+ const branchEvents = request?.kind === "decision" ? request.events.flatMap((descriptor) => variantsOf(descriptor.type, snapshot, table, requestOptions)) : require_decision.getAcceptedEvents(snapshot).flatMap((descriptor) => {
979
+ if (table[descriptor.type]?.length === 0) {
980
+ unexpanded.add(descriptor.type);
981
+ unexplored.push(`skipped: payloads lists no ${descriptor.type} at path [${path.map((e) => e.type).join(", ")}], so its branches were not explored`);
982
+ }
983
+ return variantsOf(descriptor.type, snapshot, table);
984
+ });
985
+ if (branchEvents.length === 0 && request?.kind !== "decision") {
986
+ pathsExplored++;
987
+ terminals.push({
988
+ status: "idle",
989
+ path,
990
+ state: settled.snapshot.value
991
+ });
992
+ return;
993
+ }
994
+ if (depth >= maxDepth) {
995
+ pathsExplored++;
996
+ terminals.push({
997
+ status: "max-depth",
998
+ path,
999
+ state: settled.snapshot.value
1000
+ });
1001
+ unexplored.push(`max-depth: stopped at path [${path.map((e) => e.type).join(", ")}]`);
1002
+ return;
1003
+ }
1004
+ let taken = 0;
1005
+ for (const event of branchEvents) {
1006
+ if (witness !== void 0 || capped()) return;
1007
+ if (!snapshot.can(event)) {
1008
+ prunedByGuard++;
1009
+ continue;
1010
+ }
1011
+ taken++;
1012
+ const next = transitionAgentStep(machine, settled, event);
1013
+ recordState(next.snapshot);
1014
+ yield {
1015
+ step: next,
1016
+ path: [...path, event],
1017
+ depth: depth + 1
1018
+ };
1019
+ }
1020
+ if (request?.kind !== "decision" && taken === 0 && witness === void 0 && !capped()) {
1021
+ pathsExplored++;
1022
+ terminals.push({
1023
+ status: "idle",
1024
+ path,
1025
+ state: settled.snapshot.value
1026
+ });
1027
+ const refused = [...new Set(branchEvents.map((event) => event.type))];
1028
+ for (const type of refused) unexpanded.add(type);
1029
+ unexplored.push(`stuck: every offered ${refused.join(", ")} event at path [${path.map((e) => e.type).join(", ")}] was refused by the machine's guards; add a payloads entry it accepts`);
1030
+ return;
1031
+ }
1032
+ if (request?.kind === "decision" && taken === 0) {
1033
+ if (witness !== void 0 || capped()) return;
1034
+ const exhausted = new require_decision.AgentDecisionExhaustedError([]);
1035
+ exhausted.message = `Decision '${request.name ?? request.id}' has no candidate the current snapshot accepts (explored, no model call).`;
1036
+ const failed = rejectAgentStep(machine, settled, request.id, exhausted);
1037
+ recordState(failed.snapshot);
1038
+ yield {
1039
+ step: failed,
1040
+ path: [...path, { type: `xstate.error.actor.${request.id}` }],
1041
+ depth: depth + 1
1042
+ };
1043
+ }
1044
+ }
1045
+ const root = {
1046
+ step: initial,
1047
+ path: [],
1048
+ depth: 0
1049
+ };
1050
+ if (witness === void 0 && stopWhen) {
1051
+ const levels = [[root]];
1052
+ search: for (let length = 0; length < levels.length; length++) {
1053
+ const level = levels[length] ?? [];
1054
+ for (let i = 0; i < level.length; i++) {
1055
+ const node = level[i];
1056
+ level[i] = void 0;
1057
+ for (const child of expand(node)) (levels[child.path.length] ??= []).push(child);
1058
+ if (witness !== void 0 || hitPathCap) break search;
1059
+ }
1060
+ levels[length] = [];
1061
+ }
1062
+ } else if (witness === void 0) {
1063
+ const visit = (node) => {
1064
+ for (const child of expand(node)) visit(child);
1065
+ };
1066
+ visit(root);
1067
+ }
1068
+ return {
1069
+ report: {
1070
+ reachedStates: reachedValues,
1071
+ terminals,
1072
+ prunedByGuard,
1073
+ unexplored,
1074
+ pathsExplored,
1075
+ hitPathCap,
1076
+ unexpandedEvents: [...unexpanded]
1077
+ },
1078
+ witness
1079
+ };
1080
+ }
1081
+ /**
1082
+ * Enumerates a machine's decision and external-event branches to a bounded
1083
+ * depth, model-free, and reports which states are reached and how each path
1084
+ * terminates. At each decision request it forks one branch per candidate event
1085
+ * (guard-rejected candidates are counted in `prunedByGuard`, not explored); a
1086
+ * decision with no takeable candidate exhausts as it does live, taking its
1087
+ * `onError` (an `error` terminal without one). At an idle wait it forks per
1088
+ * externally-accepted event. Text requests and other
1089
+ * invokes resolve from `outputs`, a by-src map of outputs to try (a
1090
+ * one-element list is a canned output; several fork one branch each), and a
1091
+ * missing src halts that branch with a `needs-output` terminal rather than
1092
+ * throwing. A src in
1093
+ * `errors` forks an extra branch where that invoke is rejected, so states
1094
+ * behind an `onError` are explored too.
1095
+ *
1096
+ * Events are applied as `{ type }` unless a payload is known: `payloads`
1097
+ * forks a type once per listed payload, a decide input's own `payloads`
1098
+ * become the branch set, and an event schema whose fields are finite (enums,
1099
+ * booleans, consts) expands to their product, each optional field also
1100
+ * omitted. A type that could not be fully expanded is listed in
1101
+ * `unexpandedEvents`, as is a type with no schema whose transition could read
1102
+ * the payload (a function transition, guard, `matches`, or context mapper).
1103
+ * `dedupeBy` explores each projected snapshot once, which keeps a large
1104
+ * payload table tractable.
1105
+ *
1106
+ * Combinatorics are bounded by `maxDepth` (default 8), `maxPaths` (default
1107
+ * 200), and `maxNodes` (default 100,000), the last two reported via
1108
+ * `hitPathCap`.
1109
+ *
1110
+ * @example
1111
+ * ```ts
1112
+ * const report = await explorePaths(refundMachine, { input: { request: 'x', amount: 5000 } });
1113
+ * // report.terminals → both 'refunded' and 'denied'; report.prunedByGuard → 1
1114
+ * ```
1115
+ */
1116
+ async function explorePaths(machine, options = {}) {
1117
+ return explore(machine, options).report;
1118
+ }
1119
+ /** Thrown when a string reachability target is not a state in the machine. */
1120
+ var AgentUnknownStateError = class extends require_event_log.AgentError {
1121
+ target;
1122
+ constructor(machineId, target) {
1123
+ super("unknown-state", `canReach: machine '${machineId}' has no state matching '${target}'.`);
1124
+ this.name = "AgentUnknownStateError";
1125
+ this.target = target;
1126
+ }
1127
+ };
1128
+ function resolveStateTarget(machine, target) {
1129
+ const stack = [machine.root];
1130
+ while (stack.length > 0) {
1131
+ const node = stack.pop();
1132
+ if (node.id === target.replace(/^#/, "") || node.path.join(".") === target) return node.id;
1133
+ stack.push(...Object.values(node.states));
1134
+ }
1135
+ throw new AgentUnknownStateError(machine.id, target);
1136
+ }
1137
+ /**
1138
+ * Answers "can the machine reach this?" by exploring its branches (a thin
1139
+ * wrapper over {@link explorePaths}). The target is either a state path string
1140
+ * (`snapshot.matches(...)` semantics) or a snapshot predicate — the predicate
1141
+ * form checks any property (a context invariant, a tag, a state+context
1142
+ * combination) without reifying a sentinel state for it. Returns
1143
+ * `{ reachable: true, witness }` with the event sequence that reaches it, or
1144
+ * `{ reachable: false }`. Check `complete` before treating `reachable: false`
1145
+ * as a proof: it is false when a cap or `maxDepth` cut exploration short, a
1146
+ * branch needed an `outputs` entry it did not have, or an event's payloads were
1147
+ * not all tried.
1148
+ *
1149
+ * @example
1150
+ * ```ts
1151
+ * const { reachable, witness } = await canReach(refundMachine, 'denied', { input: { request: 'x', amount: 5000 } });
1152
+ * // reachable → true; witness → [{ type: 'NEEDS_REVIEW' }, { type: 'DENY' }]
1153
+ * ```
1154
+ *
1155
+ * @example Predicate target — a violation property, no sentinel state needed
1156
+ * ```ts
1157
+ * const violation = await canReach(
1158
+ * refundMachine,
1159
+ * (snapshot) => snapshot.matches('issued') && !snapshot.context.approved,
1160
+ * { input: { amount: 5000 } },
1161
+ * );
1162
+ * // violation.reachable → false is the safety proof
1163
+ * ```
1164
+ */
1165
+ async function canReach(machine, target, options = {}) {
1166
+ const resolvedTarget = typeof target === "string" ? resolveStateTarget(machine, target) : void 0;
1167
+ const { witness, report } = explore(machine, options, typeof target === "function" ? target : (snapshot) => snapshot.nodes.some((node) => node.id === resolvedTarget));
1168
+ if (witness !== void 0) return {
1169
+ reachable: true,
1170
+ witness,
1171
+ complete: true,
1172
+ ...resolvedTarget ? { target: resolvedTarget } : {}
1173
+ };
1174
+ return {
1175
+ reachable: false,
1176
+ complete: !report.hitPathCap && report.unexpandedEvents.length === 0 && report.terminals.every((terminal) => terminal.status === "done" || terminal.status === "idle" || terminal.status === "error"),
1177
+ ...resolvedTarget ? { target: resolvedTarget } : {}
1178
+ };
1179
+ }
1180
+ //#endregion
1181
+ Object.defineProperty(exports, "AgentLintError", {
1182
+ enumerable: true,
1183
+ get: function() {
1184
+ return AgentLintError;
1185
+ }
1186
+ });
1187
+ Object.defineProperty(exports, "AgentUnknownStateError", {
1188
+ enumerable: true,
1189
+ get: function() {
1190
+ return AgentUnknownStateError;
1191
+ }
1192
+ });
1193
+ Object.defineProperty(exports, "assertAgentMachine", {
1194
+ enumerable: true,
1195
+ get: function() {
1196
+ return assertAgentMachine;
1197
+ }
1198
+ });
1199
+ Object.defineProperty(exports, "canReach", {
1200
+ enumerable: true,
1201
+ get: function() {
1202
+ return canReach;
1203
+ }
1204
+ });
1205
+ Object.defineProperty(exports, "explorePaths", {
1206
+ enumerable: true,
1207
+ get: function() {
1208
+ return explorePaths;
1209
+ }
1210
+ });
1211
+ Object.defineProperty(exports, "lintAgentMachine", {
1212
+ enumerable: true,
1213
+ get: function() {
1214
+ return lintAgentMachine;
1215
+ }
1216
+ });
1217
+ Object.defineProperty(exports, "simulateAgent", {
1218
+ enumerable: true,
1219
+ get: function() {
1220
+ return simulateAgent;
1221
+ }
1222
+ });