@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
package/dist/testing.cjs CHANGED
@@ -1,973 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_text_logic = require("./text-logic-f5F5sCdc.cjs");
3
- const require_decision = require("./decision-CmM8-HHz.cjs");
4
- const require_run_agent = require("./run-agent-Dmf8GV2d.cjs");
5
- require("./validate.cjs");
6
- let xstate = require("xstate");
7
- //#region src/steps.ts
8
- /**
9
- * Internal: the pure `(state, event) => (nextState, requests)` view of an
10
- * agent machine, used by model-free simulation (`simulateAgent`) and tests.
11
- * An {@link AgentStep} is a snapshot plus the model requests the machine is
12
- * now waiting on; nothing here executes anything. Hosts use
13
- * `createAgentRuntime`.
14
- * @module
15
- */
16
- /** @internal Normalizes current and legacy XState invoke effect shapes. */
17
- function getInvokeEffectMetadata(action) {
18
- if (action.type === "@xstate.spawn") return action;
19
- if (action.type === "xstate.spawnChild") {
20
- const params = action.params;
21
- return params ? {
22
- ...params,
23
- logic: action.logic
24
- } : void 0;
25
- }
26
- if (action.type === "@xstate.start" && typeof action.src === "string") return action;
27
- }
28
- /**
29
- * The invokes a snapshot is still waiting on, lowered to the same spawn-effect
30
- * shape {@link getAgentRequests} reads. Derived from the snapshot's live
31
- * children rather than the last transition's actions, so a state that invokes
32
- * several actors at once (an `invoke: [...]` array, or parallel regions each
33
- * invoking) keeps reporting the siblings after the first one settles —
34
- * including when the settled invoke's `onDone` is targetless and starts
35
- * nothing new.
36
- *
37
- * Entries are ordered by invoke id, so a walker that resolves one invoke per
38
- * step is deterministic.
39
- *
40
- * @internal
41
- */
42
- function getPendingInvokes(snapshot) {
43
- const children = snapshot.children ?? {};
44
- return Object.keys(children).sort().flatMap((id) => {
45
- const child = children[id];
46
- const childSnapshot = child?.getSnapshot?.();
47
- if (!child || childSnapshot?.status !== "active") return [];
48
- return [{
49
- type: "@xstate.spawn",
50
- id,
51
- src: child.src,
52
- input: childSnapshot.input,
53
- logic: child.logic
54
- }];
55
- });
56
- }
57
- /**
58
- * Scans a set of executable actions (as returned by xstate's `transition`/
59
- * `initialTransition`) for spawned `TextLogic`/`DecisionLogic` invokes and
60
- * lowers each into an {@link AgentStepRequest}. Pass `options.machine` to
61
- * pre-fill `schemas`/`actors` from the machine's registered `setupAgent`
62
- * options instead of passing them by hand.
63
- * `options.snapshot` is required to resolve a decision's candidate events
64
- * (intersecting declared `allowedEvents` with what's currently legal) — omit
65
- * it and decision requests report an empty `events` list.
66
- *
67
- * @internal
68
- */
69
- function getAgentRequests(actions, { machine, ...options } = {}) {
70
- if (machine) options = {
71
- ...require_text_logic.getRegisteredAgentExecutionOptions(machine),
72
- ...options
73
- };
74
- return actions.flatMap((action) => {
75
- const params = getInvokeEffectMetadata(action);
76
- if (!params || typeof params.src !== "string") return [];
77
- if (typeof params.id !== "string" || params.id.length === 0) throw new Error(`Agent invoke '${params.src}' must define a durable string id.`);
78
- const registeredLogic = require_text_logic.isTextLogic(params.logic) || require_decision.isDecisionLogic(params.logic) ? params.logic : options.actors?.[params.src];
79
- if (require_decision.isDecisionLogic(registeredLogic)) {
80
- const decisionRequest = registeredLogic.request(params.input);
81
- const allowedEventTypes = registeredLogic.allowedEventTypes?.(params.input);
82
- const events = options.snapshot ? require_decision.getAcceptedEvents(options.snapshot, {
83
- events: options.events,
84
- schemas: options.schemas,
85
- eventTypes: allowedEventTypes
86
- }) : [];
87
- return [{
88
- ...decisionRequest,
89
- id: params.id,
90
- events
91
- }];
92
- }
93
- const textLogic = require_text_logic.isTextLogic(registeredLogic) ? registeredLogic : void 0;
94
- const input = textLogic ? textLogic.request(params.input) : void 0;
95
- if (!input) return [];
96
- return [{
97
- kind: "text",
98
- id: params.id,
99
- src: params.src,
100
- ...textLogic ? { mode: textLogic.mode } : {},
101
- input,
102
- tools: input.tools ?? {},
103
- events: []
104
- }];
105
- });
106
- }
107
- /**
108
- * Builds the canonical `xstate.done.actor` event xstate's `transition()`
109
- * expects to resolve a spawned invoke — including the child session identity
110
- * that rejects a stale completion from an earlier incarnation.
111
- *
112
- * @internal
113
- */
114
- function doneEvent(snapshot, request, output) {
115
- const id = typeof request === "string" ? request : request.id;
116
- const child = snapshot.children[id];
117
- return {
118
- type: "xstate.done.actor",
119
- actorId: id,
120
- ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
121
- output
122
- };
123
- }
124
- /**
125
- * The `xstate.error.actor` counterpart of {@link doneEvent} — the event a
126
- * failed invoke delivers, which `onError` transitions observe. Carries the
127
- * same child session identity, so xstate does not discard it as stale.
128
- *
129
- * @internal
130
- */
131
- function errorEvent(snapshot, request, error) {
132
- const id = typeof request === "string" ? request : request.id;
133
- const child = snapshot.children[id];
134
- return {
135
- type: "xstate.error.actor",
136
- actorId: id,
137
- ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
138
- error
139
- };
140
- }
141
- /**
142
- * Applies a request's `output` as a done event (or, with `outcome: 'error'`,
143
- * its `output` as the invoke's error) via `transition(...)`, returning the raw
144
- * `[snapshot, actions]` tuple. Lower-level than {@link resolveAgentStep} /
145
- * {@link rejectAgentStep} — those helpers wrap this and also run
146
- * {@link getAgentRequests} to produce the next {@link AgentStep}.
147
- *
148
- * @internal
149
- */
150
- function transitionResult(logic, snapshot, request, output, outcome = "output") {
151
- const event = outcome === "error" ? errorEvent(snapshot, request, output) : doneEvent(snapshot, request, output);
152
- const result = (0, xstate.transition)(logic, snapshot, event);
153
- applyFinalStateOutput(logic, result[0], event);
154
- return result;
155
- }
156
- /**
157
- * Starts a machine and returns its first {@link AgentStep} — the step-path
158
- * equivalent of `initialTransition` plus request discovery. Begins the
159
- * durable/per-model-call-checkpoint loop: resolve each `step.requests` entry
160
- * (via {@link executeAgentRequest} for `kind: 'text'`, or
161
- * {@link resolveDecision} for `kind: 'decision'`), then advance with
162
- * {@link resolveAgentStep} or {@link transitionAgentStep}.
163
- */
164
- function initialAgentStep(machine, input, options) {
165
- const [snapshot, actions] = (0, xstate.initialTransition)(machine, input);
166
- return createAgentStep(machine, snapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
167
- }
168
- /**
169
- * Applies an externally-sent event (e.g. a decision's chosen event, or a
170
- * human's reply) and returns the next {@link AgentStep}. Accepts **either**
171
- * a raw snapshot **or** a prior `AgentStep` as the second argument —
172
- * `.snapshot` is unwrapped automatically, so callers can thread the whole
173
- * step object through without manually plucking the snapshot out.
174
- */
175
- function transitionAgentStep(machine, snapshotOrStep, event, options) {
176
- const [nextSnapshot, actions] = (0, xstate.transition)(machine, isAgentStep(snapshotOrStep) ? snapshotOrStep.snapshot : snapshotOrStep, event);
177
- return createAgentStep(machine, nextSnapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
178
- }
179
- /**
180
- * Applies a resolved text request's output (a `kind: 'text'`
181
- * {@link AgentRequest} — not a decision) as a done event and returns the
182
- * next {@link AgentStep}. For decisions, resolve with `resolveDecision`
183
- * (which returns a {@link ChosenEvent}) and apply it with
184
- * {@link transitionAgentStep} instead — a decision has no output value of
185
- * its own to feed here.
186
- */
187
- function resolveAgentStep(machine, step, request, output, options) {
188
- const [snapshot, actions] = transitionResult(machine, step.snapshot, request, output);
189
- return createAgentStep(machine, snapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
190
- }
191
- /**
192
- * Rejects a pending invoke — the failure counterpart of
193
- * {@link resolveAgentStep}. Delivers `error` as the invoke's
194
- * `xstate.error.actor` event, so the machine takes that invoke's `onError`
195
- * transition and returns the next {@link AgentStep}. With no `onError` in
196
- * scope the returned step's snapshot has `status: 'error'`, exactly as a live
197
- * run's failed invoke does.
198
- *
199
- * A durable host replays a failed call with this: the log says the call
200
- * failed, so the replay rejects the invoke instead of resolving it. Scripted
201
- * playthroughs use it too — see `simulateAgent`'s `script.errors`.
202
- *
203
- * `error` can be any value. An `Error` instance is the usual one, but a plain
204
- * object (`{ code: 'truncated' }`) reaches the `onError` transition as
205
- * `event.error` untouched, so a machine that branches on a failure code is
206
- * testable without constructing a provider's error class.
207
- */
208
- function rejectAgentStep(machine, step, request, error, options) {
209
- const [snapshot, actions] = transitionResult(machine, step.snapshot, request, error, "error");
210
- return createAgentStep(machine, snapshot, actions, require_text_logic.getRegisteredAgentExecutionOptions(machine, options));
211
- }
212
- function createAgentStep(machine, snapshot, actions, options) {
213
- applyFinalStateOutput(machine, snapshot);
214
- return {
215
- snapshot,
216
- actions,
217
- requests: getAgentRequests(getPendingInvokes(snapshot), {
218
- ...options,
219
- snapshot
220
- }),
221
- done: snapshot.status === "done"
222
- };
223
- }
224
- function resolveStateValueConfig(config, value) {
225
- if (typeof value === "string") return config.states?.[value];
226
- if (!value || typeof value !== "object") return;
227
- for (const [key, childValue] of Object.entries(value)) {
228
- const childConfig = config.states?.[key];
229
- if (!childConfig) continue;
230
- if (childConfig.type === "final") return childConfig;
231
- const nested = resolveStateValueConfig(childConfig, childValue);
232
- if (nested) return nested;
233
- }
234
- }
235
- function applyFinalStateOutput(logic, snapshot, event) {
236
- const machineSnapshot = snapshot;
237
- if (machineSnapshot.status !== "done" || machineSnapshot.output !== void 0 || !("config" in logic)) return;
238
- const config = logic.config;
239
- if (!config) return;
240
- const output = resolveStateValueConfig(config, machineSnapshot.value)?.output;
241
- if (output === void 0) return;
242
- machineSnapshot.output = typeof output === "function" ? output({
243
- context: machineSnapshot.context,
244
- event
245
- }) : output;
246
- }
247
- function isAgentStep(value) {
248
- return !!value && typeof value === "object" && "snapshot" in value && "actions" in value && "requests" in value;
249
- }
250
- //#endregion
251
- //#region src/verify.ts
252
- const DECIDE_SRC = "agent.decide";
253
- function normalizeInvokes(invoke) {
254
- if (invoke === void 0 || invoke === null) return [];
255
- return Array.isArray(invoke) ? invoke : [invoke];
256
- }
257
- function buildStateIndex(rootConfig) {
258
- const index = /* @__PURE__ */ new Map();
259
- const walk = (states, parentPath) => {
260
- for (const [name, config] of Object.entries(states ?? {})) {
261
- const path = parentPath ? `${parentPath}.${name}` : name;
262
- const hasChildren = !!config.states && Object.keys(config.states).length > 0;
263
- index.set(path, {
264
- path,
265
- config,
266
- parentPath,
267
- isFinal: config.type === "final",
268
- isParallel: config.type === "parallel",
269
- isCompound: hasChildren && config.type !== "parallel",
270
- invokes: normalizeInvokes(config.invoke)
271
- });
272
- if (hasChildren) walk(config.states, path);
273
- }
274
- };
275
- walk(rootConfig.states, "");
276
- return index;
277
- }
278
- function ancestorChain(node, index) {
279
- const chain = [];
280
- let parent = node.parentPath;
281
- while (parent) {
282
- const parentNode = index.get(parent);
283
- if (!parentNode) break;
284
- chain.push(parentNode);
285
- parent = parentNode.parentPath;
286
- }
287
- return chain;
288
- }
289
- function hasNonEmptyOn(config) {
290
- return !!config.on && Object.keys(config.on).length > 0;
291
- }
292
- function isDecisionInvoke(src, actors) {
293
- if (typeof src === "string") return src === DECIDE_SRC || require_decision.isDecisionLogic(actors[src]);
294
- return require_decision.isDecisionLogic(src);
295
- }
296
- function isAgentLogicNeedingBinding(src) {
297
- return (require_text_logic.isTextLogic(src) || require_decision.isDecisionLogic(src)) && !require_text_logic.executorBoundLogics.has(src);
298
- }
299
- function checkDecideWithoutEvents(ctx) {
300
- const out = [];
301
- for (const node of ctx.index.values()) for (const invoke of node.invokes) {
302
- if (!isDecisionInvoke(invoke.src, ctx.actors)) continue;
303
- const selfHandles = hasNonEmptyOn(node.config);
304
- const ancestorHandles = ancestorChain(node, ctx.index).some((ancestor) => hasNonEmptyOn(ancestor.config));
305
- const invokeObserves = invoke.onDone !== void 0;
306
- if (selfHandles || ancestorHandles || invokeObserves) continue;
307
- const srcName = typeof invoke.src === "string" ? invoke.src : "(inline logic)";
308
- out.push({
309
- code: "decide-without-events",
310
- severity: "error",
311
- path: node.path,
312
- 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.`
313
- });
314
- }
315
- return out;
316
- }
317
- function checkDirectObjectSrc(ctx) {
318
- const out = [];
319
- for (const node of ctx.index.values()) for (const invoke of node.invokes) {
320
- const src = invoke.src;
321
- if (typeof src === "string" || !src || typeof src !== "object") continue;
322
- if (!isAgentLogicNeedingBinding(src)) continue;
323
- out.push({
324
- code: "direct-object-src",
325
- severity: "warning",
326
- path: node.path,
327
- 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.`
328
- });
329
- }
330
- return out;
331
- }
332
- function handlesActorError(config) {
333
- if (config["onError"] !== void 0) return true;
334
- return Object.keys(config.on ?? {}).some((type) => type === "*" || type.startsWith("xstate.error"));
335
- }
336
- function checkInvokeWithoutOnError(ctx) {
337
- const out = [];
338
- for (const node of ctx.index.values()) for (const invoke of node.invokes) {
339
- if (invoke.onError !== void 0 || handlesActorError(node.config)) continue;
340
- if (handlesActorError(ctx.config) || ancestorChain(node, ctx.index).some((ancestor) => handlesActorError(ancestor.config))) continue;
341
- const srcName = typeof invoke.src === "string" ? invoke.src : "(inline logic)";
342
- out.push({
343
- code: "invoke-without-on-error",
344
- severity: "warning",
345
- path: node.path,
346
- 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).`
347
- });
348
- }
349
- return out;
350
- }
351
- const LINT_CHECKS = [
352
- checkDecideWithoutEvents,
353
- checkDirectObjectSrc,
354
- checkInvokeWithoutOnError
355
- ];
356
- /**
357
- * Runs static structural checks over a built agent machine and returns the
358
- * findings ({@link AgentLintDiagnostic}[], empty when clean). Works for
359
- * TS-authored (`setupAgent(...).createMachine(...)`) and
360
- * `setupAgent.fromConfig(...)`-compiled machines alike, reading `machine.config`
361
- * plus the schemas/actor sources the library already retains per machine.
362
- *
363
- * No model calls, no API keys — a coding agent that emits an agent machine can
364
- * call this to catch agent-specific mistakes such as undeliverable decisions,
365
- * un-rebindable request sources, and an unhandled transcript event. General
366
- * state-machine lint belongs to XState tooling.
367
- *
368
- * Pass `{ throw: true }` for the one-liner form used in tests and generation
369
- * loops: it returns silently when the machine is clean and throws
370
- * {@link AgentLintError} (findings on `.diagnostics`) on error-severity
371
- * findings, or on warnings too with `{ throw: true, warnings: true }`.
372
- *
373
- * @example
374
- * ```ts
375
- * const errors = lintAgentMachine(machine).filter((d) => d.severity === 'error');
376
- * if (errors.length) throw new Error(errors.map((e) => `${e.path}: ${e.message}`).join('\n'));
377
- * ```
378
- *
379
- * @example Throwing form
380
- * ```ts
381
- * test('agent machine is structurally sound', () => {
382
- * lintAgentMachine(machine, { throw: true });
383
- * });
384
- * ```
385
- */
386
- function lintAgentMachine(machine, options = {}) {
387
- const config = machine.config ?? {};
388
- const index = buildStateIndex(config);
389
- const registered = require_text_logic.getRegisteredAgentExecutionOptions(machine);
390
- const ctx = {
391
- config,
392
- index,
393
- schemas: registered.schemas,
394
- actors: registered.actors ?? machine.sources?.actors ?? {}
395
- };
396
- const disabled = new Set(options.disable ?? []);
397
- const diagnostics = LINT_CHECKS.flatMap((check) => check(ctx)).filter((d) => !disabled.has(d.code));
398
- if (options.throw) {
399
- const failing = options.warnings ? diagnostics : diagnostics.filter((d) => d.severity === "error");
400
- if (failing.length > 0) throw new AgentLintError(machine.id ?? "(machine)", failing);
401
- }
402
- return diagnostics;
403
- }
404
- /**
405
- * Thrown by `lintAgentMachine(machine, { throw: true })` (and its
406
- * {@link assertAgentMachine} alias) when lint finds failing diagnostics.
407
- * `diagnostics` holds the findings; the message lists them one per finding,
408
- * so a test runner's failure output reads like the CLI's lint report.
409
- */
410
- var AgentLintError = class extends require_text_logic.AgentError {
411
- diagnostics;
412
- constructor(machineId, diagnostics) {
413
- const lines = diagnostics.map((d) => ` ${d.severity === "error" ? "error" : "warn "} ${d.code} ${d.path}\n ${d.message}`);
414
- super("lint-failed", `Agent machine '${machineId}' failed lint (${diagnostics.length} finding(s)):\n${lines.join("\n")}`);
415
- this.name = "AgentLintError";
416
- this.diagnostics = diagnostics;
417
- }
418
- };
419
- /**
420
- * Asserts a machine passes {@link lintAgentMachine}: returns silently when
421
- * clean, throws {@link AgentLintError} (with the findings on `.diagnostics`)
422
- * otherwise — sugar for `lintAgentMachine(machine, { ...options, throw: true })`.
423
- * Fails on error-severity findings; set `warnings: true` to fail on warnings
424
- * too. The one-liner for tests and generation loops:
425
- *
426
- * @example
427
- * ```ts
428
- * test('agent machine is structurally sound', () => {
429
- * assertAgentMachine(machine);
430
- * });
431
- * ```
432
- */
433
- function assertAgentMachine(machine, options = {}) {
434
- lintAgentMachine(machine, {
435
- ...options,
436
- throw: true
437
- });
438
- }
439
- function pendingInvokes(step) {
440
- const out = [];
441
- for (const metadata of getPendingInvokes(step.snapshot)) if (typeof metadata.src === "string") out.push({
442
- id: metadata.id,
443
- src: metadata.src
444
- });
445
- return out;
446
- }
447
- function takeFromQueue(channel, src) {
448
- const queue = channel?.[src];
449
- if (queue && queue.length > 0) return {
450
- found: true,
451
- value: queue.shift()
452
- };
453
- return { found: false };
454
- }
455
- /**
456
- * Deterministically plays a machine through, resolving each request from a
457
- * {@link SimulationScript} instead of a model — no API keys, no model calls.
458
- * Runs on the pure step path ({@link initialAgentStep} etc.), so it exercises
459
- * the real transition logic. Returns the terminal `status`, final `snapshot`,
460
- * and a `trail` of every step taken.
461
- *
462
- * Decisions run through the live run's own validation/retry core,
463
- * {@link resolveDecision}, with the script standing in for the model: each
464
- * attempt consumes the next queued {@link ChosenEvent} for that src, so an
465
- * unknown, payload-invalid, or guard-rejected event is NOT silently swallowed
466
- * — the next queued decision is tried, exactly as a live run re-asks the
467
- * model. The decision logic's `maxRetries` caps attempts as it would live;
468
- * when retries continue past the end of the queue, the last queued decision
469
- * repeats (a scripted model that insists). The repeat applies only within one
470
- * decision request's retries — each new decision request must have its own
471
- * queued entry, or the dry-script error throws as usual. Exhausting all
472
- * attempts delivers
473
- * the resulting {@link AgentDecisionExhaustedError} to the machine as the
474
- * decision invoke's error (so an `onError` transition observes it, as it
475
- * would live); with no `onError` to catch it, it is thrown.
476
- *
477
- * Throws a descriptive error when the script runs dry mid-request, naming the
478
- * pending request's kind, src, and id so the missing scripted response is
479
- * obvious.
480
- *
481
- * @example
482
- * ```ts
483
- * const { status, snapshot } = simulateAgent(machine, {
484
- * input: { topic: 'state machines' },
485
- * script: {
486
- * decisions: { 'agent.decide': [{ type: 'ESCALATE' }] },
487
- * events: [{ type: 'APPROVE' }], // crosses the human gate
488
- * },
489
- * });
490
- * ```
491
- */
492
- async function simulateAgent(machine, options) {
493
- const maxSteps = options.maxSteps ?? 100;
494
- const script = {
495
- text: mapValues(options.script.text ?? {}, (arr) => [...arr]),
496
- decisions: mapValues(options.script.decisions ?? {}, (arr) => [...arr]),
497
- invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr]),
498
- errors: mapValues(options.script.errors ?? {}, (arr) => [...arr]),
499
- events: [...options.script.events ?? []]
500
- };
501
- let step = initialAgentStep(machine, options.input);
502
- const trail = [{ state: step.snapshot.value }];
503
- for (let i = 0; i < maxSteps; i++) {
504
- if (step.done) return {
505
- status: "done",
506
- snapshot: step.snapshot,
507
- trail
508
- };
509
- const request = step.requests[0];
510
- if (request) {
511
- if (request.kind === "decision") {
512
- const invokeMeta = findInvokeMetadata(step, request.id);
513
- const src = invokeMeta?.src ?? request.id;
514
- const decisionSrc = src in (script.decisions ?? {}) ? src : request.id in (script.decisions ?? {}) ? request.id : src;
515
- const errorSrc = queueKey(script.errors, src, request.id);
516
- const failure = takeFromQueue(script.errors, errorSrc);
517
- if (failure.found) {
518
- step = rejectScriptedRequest(machine, step, request.id, failure.value, {
519
- kind: "decision",
520
- src: errorSrc,
521
- trail
522
- });
523
- continue;
524
- }
525
- step = await applyScriptedDecision(machine, step, request, decisionSrc, invokeMeta?.logic, script, trail);
526
- continue;
527
- }
528
- const failure = takeFromQueue(script.errors, request.src);
529
- if (failure.found) {
530
- step = rejectScriptedRequest(machine, step, request, failure.value, {
531
- kind: "text",
532
- src: request.src,
533
- trail
534
- });
535
- continue;
536
- }
537
- const taken = takeFromQueue(script.text, request.src);
538
- if (!taken.found) throw scriptDryError("text", request.src, request.id);
539
- step = resolveAgentStep(machine, step, request, asTextResult(taken.value));
540
- trail.push({
541
- state: step.snapshot.value,
542
- resolvedRequest: {
543
- kind: "text",
544
- src: request.src,
545
- id: request.id,
546
- outcome: "output"
547
- }
548
- });
549
- continue;
550
- }
551
- const [invoke] = pendingInvokes(step);
552
- if (invoke) {
553
- const failure = takeFromQueue(script.errors, invoke.src);
554
- if (failure.found) {
555
- step = rejectScriptedRequest(machine, step, invoke.id, failure.value, {
556
- kind: "invoke",
557
- src: invoke.src,
558
- trail
559
- });
560
- continue;
561
- }
562
- const taken = takeFromQueue(script.invokes, invoke.src);
563
- if (!taken.found) throw scriptDryError("invoke", invoke.src, invoke.id);
564
- step = resolveAgentStep(machine, step, invoke.id, taken.value);
565
- trail.push({
566
- state: step.snapshot.value,
567
- resolvedRequest: {
568
- kind: "invoke",
569
- src: invoke.src,
570
- id: invoke.id,
571
- outcome: "output"
572
- }
573
- });
574
- continue;
575
- }
576
- const external = script.events;
577
- if (external.length > 0) {
578
- const event = external.shift();
579
- 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.`);
580
- step = transitionAgentStep(machine, step, event);
581
- trail.push({
582
- state: step.snapshot.value,
583
- appliedEvent: event,
584
- external: true
585
- });
586
- continue;
587
- }
588
- return {
589
- status: "idle",
590
- snapshot: step.snapshot,
591
- trail
592
- };
593
- }
594
- return {
595
- status: "exhausted",
596
- snapshot: step.snapshot,
597
- trail
598
- };
599
- }
600
- function queueKey(channel, src, id) {
601
- return channel && !(src in channel) && id in channel ? id : src;
602
- }
603
- function rejectScriptedRequest(machine, step, request, error, entry) {
604
- const id = typeof request === "string" ? request : request.id;
605
- const next = rejectAgentStep(machine, step, request, error);
606
- if (next.snapshot.status === "error") throw unhandledScriptedError(error, entry.kind, entry.src, id);
607
- entry.trail.push({
608
- state: next.snapshot.value,
609
- resolvedRequest: {
610
- kind: entry.kind,
611
- src: entry.src,
612
- id,
613
- outcome: "error",
614
- error
615
- }
616
- });
617
- return next;
618
- }
619
- function unhandledScriptedError(error, kind, src, id) {
620
- if (error instanceof Error) return error;
621
- 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 });
622
- }
623
- async function applyScriptedDecision(machine, step, request, decisionSrc, invokeLogic, script, trail) {
624
- const dequeued = [];
625
- const decide = async () => {
626
- const taken = takeFromQueue(script.decisions, decisionSrc);
627
- if (taken.found) {
628
- dequeued.push(taken.value);
629
- return { event: taken.value };
630
- }
631
- if (dequeued.length === 0) throw scriptDryError("decision", decisionSrc, request.id, request);
632
- return { event: dequeued[dequeued.length - 1] };
633
- };
634
- const maxRetries = (require_decision.isDecisionLogic(invokeLogic) ? invokeLogic : resolveRegisteredDecisionLogic(machine, decisionSrc))?.maxRetries;
635
- let chosen;
636
- try {
637
- chosen = await require_decision.resolveDecision(request, { decide }, {
638
- maxRetries,
639
- canTake: (event) => step.snapshot.can(event)
640
- });
641
- } catch (error) {
642
- if (!(error instanceof require_decision.AgentDecisionExhaustedError)) throw error;
643
- const next = rejectAgentStep(machine, step, request.id, error);
644
- if (next.snapshot.status === "error") throw error;
645
- trail.push({
646
- state: next.snapshot.value,
647
- rejectedEvents: dequeued
648
- });
649
- return next;
650
- }
651
- const next = transitionAgentStep(machine, step, chosen);
652
- const rejected = dequeued.slice(0, -1);
653
- trail.push({
654
- state: next.snapshot.value,
655
- appliedEvent: chosen,
656
- ...rejected.length > 0 ? { rejectedEvents: rejected } : {}
657
- });
658
- return next;
659
- }
660
- function findInvokeMetadata(step, id) {
661
- for (const metadata of getPendingInvokes(step.snapshot)) {
662
- if (metadata.id !== id) continue;
663
- return {
664
- ...typeof metadata.src === "string" ? { src: metadata.src } : {},
665
- logic: metadata.logic ?? (typeof metadata.src === "object" ? metadata.src : void 0)
666
- };
667
- }
668
- }
669
- function resolveRegisteredDecisionLogic(machine, src) {
670
- const candidate = require_text_logic.getRegisteredAgentExecutionOptions(machine).actors?.[src] ?? machine.sources?.actors?.[src];
671
- return require_decision.isDecisionLogic(candidate) ? candidate : void 0;
672
- }
673
- function asTextResult(value) {
674
- return {
675
- result: value,
676
- messages: []
677
- };
678
- }
679
- function mapValues(obj, fn) {
680
- return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, fn(value)]));
681
- }
682
- function scriptDryError(kind, src, id, request) {
683
- const events = request?.kind === "decision" ? ` Candidate events: ${request.events.map((e) => e.type).join(", ") || "(none)"}.` : "";
684
- const key = kind === "text" ? `text['${src}']` : kind === "decision" ? `decisions['${src}']` : `invokes['${src}']`;
685
- 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}`);
686
- }
687
- const MAX_ADVANCE_STEPS = 1e3;
688
- function invokeFork(id, src, outputs) {
689
- return src in outputs ? {
690
- id,
691
- src,
692
- hasOutput: true,
693
- output: outputs[src]
694
- } : {
695
- id,
696
- src,
697
- hasOutput: false
698
- };
699
- }
700
- async function explore(machine, options, stopWhen) {
701
- const maxDepth = options.maxDepth ?? 8;
702
- const maxPaths = options.maxPaths ?? 200;
703
- const textScript = options.text ?? {};
704
- const invokeOutputs = { ...options.invokes };
705
- const errorScript = options.errors ?? {};
706
- const reachedStates = /* @__PURE__ */ new Set();
707
- const reachedValues = [];
708
- const terminals = [];
709
- const unexplored = [];
710
- let prunedByGuard = 0;
711
- let pathsExplored = 0;
712
- let hitPathCap = false;
713
- let witness;
714
- const recordState = (snapshot) => {
715
- const key = JSON.stringify(snapshot.value);
716
- if (!reachedStates.has(key)) {
717
- reachedStates.add(key);
718
- reachedValues.push(snapshot.value);
719
- }
720
- };
721
- const initial = initialAgentStep(machine, options.input);
722
- recordState(initial.snapshot);
723
- if (stopWhen?.(initial.snapshot)) witness = [];
724
- const advance = (step) => {
725
- let current = step;
726
- for (let i = 0; i < MAX_ADVANCE_STEPS; i++) {
727
- if (stopWhen?.(current.snapshot)) return {
728
- step: current,
729
- hit: true
730
- };
731
- if (current.done) return { step: current };
732
- const request = current.requests[0];
733
- if (request && request.kind === "text") {
734
- if (request.src in errorScript) return {
735
- step: current,
736
- fork: invokeFork(request.id, request.src, textScript)
737
- };
738
- if (!(request.src in textScript)) return {
739
- step: current,
740
- blockedSrc: request.src
741
- };
742
- current = resolveAgentStep(machine, current, request, asTextResult(textScript[request.src]));
743
- recordState(current.snapshot);
744
- continue;
745
- }
746
- if (request && request.kind === "decision") {
747
- const src = findInvokeMetadata(current, request.id)?.src;
748
- const errorSrc = src !== void 0 && src in errorScript ? src : request.id in errorScript ? request.id : void 0;
749
- return errorSrc !== void 0 ? {
750
- step: current,
751
- fork: {
752
- id: request.id,
753
- src: errorSrc,
754
- hasOutput: false,
755
- isDecision: true
756
- }
757
- } : { step: current };
758
- }
759
- const [invoke] = pendingInvokes(current);
760
- if (invoke) {
761
- if (invoke.src in errorScript) return {
762
- step: current,
763
- fork: invokeFork(invoke.id, invoke.src, invokeOutputs)
764
- };
765
- if (!(invoke.src in invokeOutputs)) return {
766
- step: current,
767
- blockedSrc: invoke.src
768
- };
769
- current = resolveAgentStep(machine, current, invoke.id, invokeOutputs[invoke.src]);
770
- recordState(current.snapshot);
771
- continue;
772
- }
773
- return { step: current };
774
- }
775
- return { step: current };
776
- };
777
- const visit = async (step, path, startDepth) => {
778
- let depth = startDepth;
779
- if (witness !== void 0) return;
780
- if (pathsExplored >= maxPaths) {
781
- hitPathCap = true;
782
- return;
783
- }
784
- const { step: settled, blockedSrc, hit, fork } = advance(step);
785
- if (hit) {
786
- witness = path;
787
- return;
788
- }
789
- if (fork) {
790
- if (depth >= maxDepth) {
791
- pathsExplored++;
792
- terminals.push({
793
- status: "max-depth",
794
- path,
795
- state: settled.snapshot.value
796
- });
797
- unexplored.push(`max-depth: stopped at path [${path.map((e) => e.type).join(", ")}]`);
798
- return;
799
- }
800
- const failed = rejectAgentStep(machine, settled, fork.id, errorScript[fork.src]);
801
- recordState(failed.snapshot);
802
- await visit(failed, [...path, { type: `xstate.error.actor.${fork.id}` }], depth + 1);
803
- if (!fork.isDecision) {
804
- if (fork.hasOutput) {
805
- const resolved = resolveAgentStep(machine, settled, fork.id, fork.output);
806
- recordState(resolved.snapshot);
807
- await visit(resolved, path, depth + 1);
808
- }
809
- return;
810
- }
811
- depth += 1;
812
- if (witness !== void 0) return;
813
- }
814
- if (blockedSrc) {
815
- pathsExplored++;
816
- terminals.push({
817
- status: "needs-output",
818
- path,
819
- state: settled.snapshot.value,
820
- missingSrc: blockedSrc
821
- });
822
- unexplored.push(`needs-output: no canned output for src '${blockedSrc}' at path [${path.map((e) => e.type).join(", ")}]`);
823
- return;
824
- }
825
- if (settled.done) {
826
- pathsExplored++;
827
- terminals.push({
828
- status: "done",
829
- path,
830
- state: settled.snapshot.value
831
- });
832
- return;
833
- }
834
- if (settled.snapshot.status === "error") {
835
- pathsExplored++;
836
- terminals.push({
837
- status: "error",
838
- path,
839
- state: settled.snapshot.value,
840
- error: settled.snapshot.error
841
- });
842
- return;
843
- }
844
- const request = settled.requests[0];
845
- const branchEvents = request?.kind === "decision" ? request.events.map((descriptor) => ({ type: descriptor.type })) : require_decision.getAcceptedEvents(settled.snapshot).map((descriptor) => ({ type: descriptor.type }));
846
- if (branchEvents.length === 0) {
847
- pathsExplored++;
848
- terminals.push({
849
- status: "idle",
850
- path,
851
- state: settled.snapshot.value
852
- });
853
- return;
854
- }
855
- if (depth >= maxDepth) {
856
- pathsExplored++;
857
- terminals.push({
858
- status: "max-depth",
859
- path,
860
- state: settled.snapshot.value
861
- });
862
- unexplored.push(`max-depth: stopped at path [${path.map((e) => e.type).join(", ")}]`);
863
- return;
864
- }
865
- for (const event of branchEvents) {
866
- if (witness !== void 0 || pathsExplored >= maxPaths) {
867
- if (pathsExplored >= maxPaths) hitPathCap = true;
868
- return;
869
- }
870
- if (!settled.snapshot.can(event)) {
871
- prunedByGuard++;
872
- continue;
873
- }
874
- const next = transitionAgentStep(machine, settled, event);
875
- recordState(next.snapshot);
876
- await visit(next, [...path, event], depth + 1);
877
- }
878
- };
879
- if (witness === void 0) await visit(initial, [], 0);
880
- return {
881
- report: {
882
- reachedStates: reachedValues,
883
- terminals,
884
- prunedByGuard,
885
- unexplored,
886
- pathsExplored,
887
- hitPathCap
888
- },
889
- witness
890
- };
891
- }
892
- /**
893
- * Enumerates a machine's decision and external-event branches to a bounded
894
- * depth, model-free, and reports which states are reached and how each path
895
- * terminates. At each decision request it forks one branch per candidate event
896
- * (guard-rejected candidates are counted in `prunedByGuard`, not explored); at
897
- * an idle wait it forks per externally-accepted event. Text requests resolve
898
- * from `text`, other invokes from `invokes` — all by-src canned-output maps, and a missing src halts
899
- * that branch with a `needs-output` terminal rather than throwing. A src in
900
- * `errors` forks an extra branch where that invoke is rejected, so states
901
- * behind an `onError` are explored too.
902
- *
903
- * Combinatorics are bounded by `maxDepth` (default 8) and `maxPaths` (default
904
- * 200, reported via `hitPathCap`).
905
- *
906
- * @example
907
- * ```ts
908
- * const report = await explorePaths(refundMachine, { input: { request: 'x', amount: 5000 } });
909
- * // report.terminals → both 'refunded' and 'denied'; report.prunedByGuard → 1
910
- * ```
911
- */
912
- async function explorePaths(machine, options = {}) {
913
- return (await explore(machine, options)).report;
914
- }
915
- /** Thrown when a string reachability target is not a state in the machine. */
916
- var AgentUnknownStateError = class extends require_text_logic.AgentError {
917
- target;
918
- constructor(machineId, target) {
919
- super("unknown-state", `canReach: machine '${machineId}' has no state matching '${target}'.`);
920
- this.name = "AgentUnknownStateError";
921
- this.target = target;
922
- }
923
- };
924
- function resolveStateTarget(machine, target) {
925
- const stack = [machine.root];
926
- while (stack.length > 0) {
927
- const node = stack.pop();
928
- if (node.id === target.replace(/^#/, "") || node.path.join(".") === target) return node.id;
929
- stack.push(...Object.values(node.states));
930
- }
931
- throw new AgentUnknownStateError(machine.id, target);
932
- }
933
- /**
934
- * Answers "can the machine reach this?" by exploring its branches (a thin
935
- * wrapper over {@link explorePaths}). The target is either a state path string
936
- * (`snapshot.matches(...)` semantics) or a snapshot predicate — the predicate
937
- * form checks any property (a context invariant, a tag, a state+context
938
- * combination) without reifying a sentinel state for it. Returns
939
- * `{ reachable: true, witness }` with the event sequence that reaches it, or
940
- * `{ reachable: false }`.
941
- *
942
- * @example
943
- * ```ts
944
- * const { reachable, witness } = await canReach(refundMachine, 'denied', { input: { request: 'x', amount: 5000 } });
945
- * // reachable → true; witness → [{ type: 'NEEDS_REVIEW' }, { type: 'DENY' }]
946
- * ```
947
- *
948
- * @example Predicate target — a violation property, no sentinel state needed
949
- * ```ts
950
- * const violation = await canReach(
951
- * refundMachine,
952
- * (snapshot) => snapshot.matches('issued') && !snapshot.context.approved,
953
- * { input: { amount: 5000 } },
954
- * );
955
- * // violation.reachable → false is the safety proof
956
- * ```
957
- */
958
- async function canReach(machine, target, options = {}) {
959
- const resolvedTarget = typeof target === "string" ? resolveStateTarget(machine, target) : void 0;
960
- const { witness } = await explore(machine, options, typeof target === "function" ? target : (snapshot) => snapshot.nodes.some((node) => node.id === resolvedTarget));
961
- return witness !== void 0 ? {
962
- reachable: true,
963
- witness,
964
- ...resolvedTarget ? { target: resolvedTarget } : {}
965
- } : {
966
- reachable: false,
967
- ...resolvedTarget ? { target: resolvedTarget } : {}
968
- };
969
- }
970
- //#endregion
2
+ const require_event_log = require("./event-log-Dtb16p1p.cjs");
3
+ const require_decision = require("./decision-oevZnkBe.cjs");
4
+ const require_run_agent = require("./run-agent-cToZ3K42.cjs");
5
+ const require_verify = require("./verify-Cqghu4gz.cjs");
971
6
  //#region src/trajectory.ts
972
7
  /**
973
8
  * Classifies one item. Event-shaped things (log entries, events) become events;
@@ -996,17 +31,17 @@ function normalize(item) {
996
31
  };
997
32
  }
998
33
  /** Every leaf of a state value as a dot path (`{ review: 'editing' }` → `review.editing`). @internal */
999
- function statePaths(value) {
34
+ function statePaths$1(value) {
1000
35
  if (typeof value === "string") return [value];
1001
36
  if (!require_decision.isRecord(value)) return [];
1002
- return Object.entries(value).flatMap(([key, child]) => statePaths(child).map((path) => `${key}.${path}`));
37
+ return Object.entries(value).flatMap(([key, child]) => statePaths$1(child).map((path) => `${key}.${path}`));
1003
38
  }
1004
39
  /**
1005
40
  * A string expectation matches a state value when it names one of its leaves or
1006
41
  * an ancestor of one, so `'review'` matches `{ review: 'editing' }`. @internal
1007
42
  */
1008
43
  function stateMatchesPath(path, value) {
1009
- return statePaths(value).some((leaf) => leaf === path || leaf.startsWith(`${path}.`));
44
+ return statePaths$1(value).some((leaf) => leaf === path || leaf.startsWith(`${path}.`));
1010
45
  }
1011
46
  function deepEqual(a, b) {
1012
47
  if (Object.is(a, b)) return true;
@@ -1079,7 +114,7 @@ function matcherFor(expected) {
1079
114
  */
1080
115
  function matchesTrajectory(actual, expected, options = {}) {
1081
116
  const expectedCount = expected.length;
1082
- if (expectedCount === 0) throw new require_text_logic.AgentError("empty-trajectory", "matchesTrajectory: expected trajectory must contain at least one item.");
117
+ if (expectedCount === 0) throw new require_event_log.AgentError("empty-trajectory", "matchesTrajectory: expected trajectory must contain at least one item.");
1083
118
  const exact = options.exact === true;
1084
119
  let cursor = 0;
1085
120
  let matchedCount = 0;
@@ -1120,7 +155,7 @@ function matchesTrajectory(actual, expected, options = {}) {
1120
155
  //#endregion
1121
156
  //#region src/seam.ts
1122
157
  /** The only own keys an executor result carries. @internal */
1123
- const TEXT_RESULT_KEYS = new Set([
158
+ const TEXT_RESULT_KEYS = /* @__PURE__ */ new Set([
1124
159
  "result",
1125
160
  "messages",
1126
161
  "usage",
@@ -1162,7 +197,7 @@ function emitScriptedChunk(result, info) {
1162
197
  */
1163
198
  async function seamOutputOf(result, request) {
1164
199
  if (!require_decision.isRecord(result)) return;
1165
- return await require_text_logic.normalizeGeneratorResult(result, `seam '${request.name ?? request.model}'`);
200
+ return await require_event_log.normalizeGeneratorResult(result, `seam '${request.name ?? request.model}'`);
1166
201
  }
1167
202
  /**
1168
203
  * Runs a machine end to end with one model call under test, and slices the run
@@ -1219,7 +254,7 @@ async function runSeam(machine, options) {
1219
254
  };
1220
255
  const scriptedAnswer = async (request, info) => {
1221
256
  const scripted = await takeScriptedSlot(request, info);
1222
- if (!scripted) throw new require_text_logic.AgentError("seam-script-exhausted", `runSeam: no scripted answer left for request ${describeText(request)}. Add an entry to \`scripts.${queueKeyOf(request)}\`, or pass \`repeatLast: true\` to replay its last entry down a longer branch.`);
257
+ if (!scripted) throw new require_event_log.AgentError("seam-script-exhausted", `runSeam: no scripted answer left for request ${describeText(request)}. Add an entry to \`scripts.${queueKeyOf(request)}\`, or pass \`repeatLast: true\` to replay its last entry down a longer branch.`);
1223
258
  return scripted;
1224
259
  };
1225
260
  const route = async (request, info) => {
@@ -1242,7 +277,7 @@ async function runSeam(machine, options) {
1242
277
  seamEventAt = liveEvents;
1243
278
  const result = await candidate(request, info);
1244
279
  seamOutput = await seamOutputOf(result, request);
1245
- seamUsage = require_text_logic.getCallUsage(result);
280
+ seamUsage = require_event_log.getCallUsage(result);
1246
281
  ledger("candidate");
1247
282
  return result;
1248
283
  }
@@ -1289,7 +324,7 @@ async function runSeam(machine, options) {
1289
324
  const next = options.respond?.({
1290
325
  snapshot: result.snapshot,
1291
326
  state: result.snapshot.value,
1292
- meta: require_text_logic.getStateMeta(result.snapshot),
327
+ meta: require_event_log.getStateMeta(result.snapshot),
1293
328
  turn,
1294
329
  result
1295
330
  });
@@ -1326,12 +361,90 @@ async function runSeam(machine, options) {
1326
361
  };
1327
362
  }
1328
363
  //#endregion
1329
- exports.AgentLintError = AgentLintError;
1330
- exports.AgentUnknownStateError = AgentUnknownStateError;
1331
- exports.assertAgentMachine = assertAgentMachine;
1332
- exports.canReach = canReach;
1333
- exports.explorePaths = explorePaths;
1334
- exports.lintAgentMachine = lintAgentMachine;
364
+ //#region src/fold-traces.ts
365
+ /**
366
+ * Folds many runs' traces into per-state stats: how many runs visited each
367
+ * state, how many of those failed, and how many options the guards withheld
368
+ * at decisions made there. Pure; reads only:
369
+ *
370
+ * - `machine.transition`: the run visited `from.value` and `snapshot.value`.
371
+ * - `request.start` of a decision, first attempt only: its `request.withheld`
372
+ * counts toward the state the run was in, the `from.value` of the
373
+ * transition its choice causes.
374
+ *
375
+ * A trace is one run's events, live (`AgentTraceEvent`) or serialized
376
+ * (`serializeTraceEvent`). A host-driven `decideNext` traces nothing; push
377
+ * `{ type: 'request.start', request: result.request }` onto the trace before
378
+ * sending its event. Parallel states count once per active leaf.
379
+ *
380
+ * @example
381
+ * ```ts no-check
382
+ * const stats = foldTraces(traces, {
383
+ * failed: (trace) => trace.at(-1)?.type === 'run.end' && trace.at(-1).status === 'error',
384
+ * });
385
+ * ```
386
+ */
387
+ function foldTraces(traces, options) {
388
+ const stats = /* @__PURE__ */ new Map();
389
+ const statsFor = (state) => {
390
+ let entry = stats.get(state);
391
+ if (!entry) {
392
+ entry = {
393
+ state,
394
+ visits: 0,
395
+ failures: 0,
396
+ withheld: 0
397
+ };
398
+ stats.set(state, entry);
399
+ }
400
+ return entry;
401
+ };
402
+ for (const trace of traces) {
403
+ const visited = /* @__PURE__ */ new Set();
404
+ let current = [];
405
+ const visit = (state) => {
406
+ statsFor(state);
407
+ visited.add(state);
408
+ };
409
+ for (const event of trace) if (event.type === "machine.transition") {
410
+ for (const state of statePaths(valueOf(event.from))) visit(state);
411
+ current = statePaths(valueOf(event.snapshot));
412
+ for (const state of current) visit(state);
413
+ } else if (event.type === "request.start" && isFirstDecision(event.request)) {
414
+ const withheld = event.request.withheld;
415
+ if (!Array.isArray(withheld)) continue;
416
+ for (const state of current) statsFor(state).withheld += withheld.length;
417
+ }
418
+ const failed = options.failed(trace);
419
+ for (const state of visited) {
420
+ const entry = statsFor(state);
421
+ entry.visits++;
422
+ if (failed) entry.failures++;
423
+ }
424
+ }
425
+ return [...stats.values()];
426
+ }
427
+ function valueOf(holder) {
428
+ return require_decision.isRecord(holder) ? holder.value : void 0;
429
+ }
430
+ function isFirstDecision(request) {
431
+ return require_decision.isRecord(request) && request.kind === "decision" && (!Array.isArray(request.attempts) || request.attempts.length === 0);
432
+ }
433
+ function statePaths(value, prefix = "") {
434
+ if (typeof value === "string") return [prefix + value];
435
+ if (!require_decision.isRecord(value)) return [];
436
+ const entries = Object.entries(value);
437
+ if (entries.length === 0) return prefix ? [prefix.slice(0, -1)] : [];
438
+ return entries.flatMap(([key, child]) => statePaths(child, `${prefix}${key}.`));
439
+ }
440
+ //#endregion
441
+ exports.AgentLintError = require_verify.AgentLintError;
442
+ exports.AgentUnknownStateError = require_verify.AgentUnknownStateError;
443
+ exports.assertAgentMachine = require_verify.assertAgentMachine;
444
+ exports.canReach = require_verify.canReach;
445
+ exports.explorePaths = require_verify.explorePaths;
446
+ exports.foldTraces = foldTraces;
447
+ exports.lintAgentMachine = require_verify.lintAgentMachine;
1335
448
  exports.matchesTrajectory = matchesTrajectory;
1336
449
  exports.runSeam = runSeam;
1337
- exports.simulateAgent = simulateAgent;
450
+ exports.simulateAgent = require_verify.simulateAgent;