@statelyai/agent 2.0.0-alpha.25 → 2.0.0-alpha.27

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 (53) hide show
  1. package/dist/ai-sdk.cjs +4 -4
  2. package/dist/ai-sdk.d.cts +6 -6
  3. package/dist/ai-sdk.d.mts +6 -6
  4. package/dist/ai-sdk.mjs +4 -4
  5. package/dist/{decision-CM9CYZ6t.cjs → decision-CmM8-HHz.cjs} +10 -9
  6. package/dist/{decision-DzSv1NpD.mjs → decision-RQ0iv0oZ.mjs} +10 -9
  7. package/dist/{event-log-store-Ba2fsc4E.mjs → event-log-store-2pAFiWuc.mjs} +1 -1
  8. package/dist/{event-log-store-DvOcs10J.d.cts → event-log-store-2xzFmgr2.d.cts} +1 -1
  9. package/dist/{event-log-store-DxyVgnas.d.mts → event-log-store-BIPOz-di.d.mts} +1 -1
  10. package/dist/{event-log-store-DQmImLYk.cjs → event-log-store-C-COhIXP.cjs} +1 -1
  11. package/dist/index.cjs +13 -195
  12. package/dist/index.d.cts +25 -105
  13. package/dist/index.d.mts +25 -105
  14. package/dist/index.mjs +12 -189
  15. package/dist/log.cjs +1 -1
  16. package/dist/log.d.cts +1 -1
  17. package/dist/log.d.mts +1 -1
  18. package/dist/log.mjs +1 -1
  19. package/dist/machines.cjs +4 -7
  20. package/dist/machines.d.cts +5 -7
  21. package/dist/machines.d.mts +5 -7
  22. package/dist/machines.mjs +4 -7
  23. package/dist/openai.cjs +3 -3
  24. package/dist/openai.d.cts +5 -5
  25. package/dist/openai.d.mts +5 -5
  26. package/dist/openai.mjs +3 -3
  27. package/dist/otel.cjs +2 -2
  28. package/dist/otel.d.cts +3 -3
  29. package/dist/otel.d.mts +3 -3
  30. package/dist/otel.mjs +2 -2
  31. package/dist/{run-agent-CG343kFs.d.mts → run-agent-ByO-Bxyd.d.cts} +184 -179
  32. package/dist/{run-agent-BOfoJg5B.d.cts → run-agent-COXXc0RW.d.mts} +184 -179
  33. package/dist/{run-agent-Cb1-LS5P.cjs → run-agent-Dmf8GV2d.cjs} +539 -737
  34. package/dist/{run-agent-DvDQd4If.mjs → run-agent-xNTxpt82.mjs} +537 -705
  35. package/dist/{setup-agent-CFW-jPI8.mjs → setup-agent-BuanDXeh.mjs} +6 -28
  36. package/dist/{setup-agent-CPf7MxnG.cjs → setup-agent-DV4cTou-.cjs} +6 -28
  37. package/dist/testing.cjs +285 -170
  38. package/dist/testing.d.cts +39 -22
  39. package/dist/testing.d.mts +39 -22
  40. package/dist/testing.mjs +270 -155
  41. package/dist/{text-logic-Bl_ZRxDi.mjs → text-logic-CIqHAmBi.mjs} +10 -26
  42. package/dist/{text-logic-BFEOxyxv.d.cts → text-logic-D-czVPYp.d.mts} +27 -28
  43. package/dist/{text-logic-hZSiRUo1.d.mts → text-logic-IjfYy9DP.d.cts} +27 -28
  44. package/dist/{text-logic-C8xyr6Ag.cjs → text-logic-f5F5sCdc.cjs} +9 -43
  45. package/dist/{types-B1Qs2q3q.d.cts → types-BgNbNKnJ.d.mts} +3 -3
  46. package/dist/{types-tA3SdczG.d.mts → types-hR5f5YXa.d.cts} +3 -3
  47. package/dist/{utils-C1p37X_c.d.mts → utils-_q7F-AZu.d.mts} +3 -103
  48. package/dist/{utils-RalRn9sk.d.cts → utils-o4YRdHJX.d.cts} +3 -103
  49. package/dist/validate.cjs +0 -8
  50. package/dist/validate.mjs +0 -8
  51. package/package.json +5 -4
  52. package/readme.md +8 -4
  53. package/schemas/agent-workflow.json +0 -8
package/dist/testing.cjs CHANGED
@@ -1,7 +1,253 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_text_logic = require("./text-logic-C8xyr6Ag.cjs");
3
- const require_decision = require("./decision-CM9CYZ6t.cjs");
4
- const require_run_agent = require("./run-agent-Cb1-LS5P.cjs");
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
5
251
  //#region src/verify.ts
6
252
  const DECIDE_SRC = "agent.decide";
7
253
  function normalizeInvokes(invoke) {
@@ -78,7 +324,7 @@ function checkDirectObjectSrc(ctx) {
78
324
  code: "direct-object-src",
79
325
  severity: "warning",
80
326
  path: node.path,
81
- message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by runAgent, 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.`
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.`
82
328
  });
83
329
  }
84
330
  return out;
@@ -192,7 +438,7 @@ function assertAgentMachine(machine, options = {}) {
192
438
  }
193
439
  function pendingInvokes(step) {
194
440
  const out = [];
195
- for (const metadata of require_run_agent.getPendingInvokes(step.snapshot)) if (typeof metadata.src === "string") out.push({
441
+ for (const metadata of getPendingInvokes(step.snapshot)) if (typeof metadata.src === "string") out.push({
196
442
  id: metadata.id,
197
443
  src: metadata.src
198
444
  });
@@ -252,7 +498,7 @@ async function simulateAgent(machine, options) {
252
498
  errors: mapValues(options.script.errors ?? {}, (arr) => [...arr]),
253
499
  events: [...options.script.events ?? []]
254
500
  };
255
- let step = require_run_agent.initialAgentStep(machine, options.input);
501
+ let step = initialAgentStep(machine, options.input);
256
502
  const trail = [{ state: step.snapshot.value }];
257
503
  for (let i = 0; i < maxSteps; i++) {
258
504
  if (step.done) return {
@@ -290,7 +536,7 @@ async function simulateAgent(machine, options) {
290
536
  }
291
537
  const taken = takeFromQueue(script.text, request.src);
292
538
  if (!taken.found) throw scriptDryError("text", request.src, request.id);
293
- step = require_run_agent.resolveAgentStep(machine, step, request, asTextResult(taken.value));
539
+ step = resolveAgentStep(machine, step, request, asTextResult(taken.value));
294
540
  trail.push({
295
541
  state: step.snapshot.value,
296
542
  resolvedRequest: {
@@ -315,7 +561,7 @@ async function simulateAgent(machine, options) {
315
561
  }
316
562
  const taken = takeFromQueue(script.invokes, invoke.src);
317
563
  if (!taken.found) throw scriptDryError("invoke", invoke.src, invoke.id);
318
- step = require_run_agent.resolveAgentStep(machine, step, invoke.id, taken.value);
564
+ step = resolveAgentStep(machine, step, invoke.id, taken.value);
319
565
  trail.push({
320
566
  state: step.snapshot.value,
321
567
  resolvedRequest: {
@@ -331,7 +577,7 @@ async function simulateAgent(machine, options) {
331
577
  if (external.length > 0) {
332
578
  const event = external.shift();
333
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.`);
334
- step = require_run_agent.transitionAgentStep(machine, step, event);
580
+ step = transitionAgentStep(machine, step, event);
335
581
  trail.push({
336
582
  state: step.snapshot.value,
337
583
  appliedEvent: event,
@@ -356,7 +602,7 @@ function queueKey(channel, src, id) {
356
602
  }
357
603
  function rejectScriptedRequest(machine, step, request, error, entry) {
358
604
  const id = typeof request === "string" ? request : request.id;
359
- const next = require_run_agent.rejectAgentStep(machine, step, request, error);
605
+ const next = rejectAgentStep(machine, step, request, error);
360
606
  if (next.snapshot.status === "error") throw unhandledScriptedError(error, entry.kind, entry.src, id);
361
607
  entry.trail.push({
362
608
  state: next.snapshot.value,
@@ -394,7 +640,7 @@ async function applyScriptedDecision(machine, step, request, decisionSrc, invoke
394
640
  });
395
641
  } catch (error) {
396
642
  if (!(error instanceof require_decision.AgentDecisionExhaustedError)) throw error;
397
- const next = require_run_agent.rejectAgentStep(machine, step, request.id, error);
643
+ const next = rejectAgentStep(machine, step, request.id, error);
398
644
  if (next.snapshot.status === "error") throw error;
399
645
  trail.push({
400
646
  state: next.snapshot.value,
@@ -402,7 +648,7 @@ async function applyScriptedDecision(machine, step, request, decisionSrc, invoke
402
648
  });
403
649
  return next;
404
650
  }
405
- const next = require_run_agent.transitionAgentStep(machine, step, chosen);
651
+ const next = transitionAgentStep(machine, step, chosen);
406
652
  const rejected = dequeued.slice(0, -1);
407
653
  trail.push({
408
654
  state: next.snapshot.value,
@@ -412,7 +658,7 @@ async function applyScriptedDecision(machine, step, request, decisionSrc, invoke
412
658
  return next;
413
659
  }
414
660
  function findInvokeMetadata(step, id) {
415
- for (const metadata of require_run_agent.getPendingInvokes(step.snapshot)) {
661
+ for (const metadata of getPendingInvokes(step.snapshot)) {
416
662
  if (metadata.id !== id) continue;
417
663
  return {
418
664
  ...typeof metadata.src === "string" ? { src: metadata.src } : {},
@@ -472,7 +718,7 @@ async function explore(machine, options, stopWhen) {
472
718
  reachedValues.push(snapshot.value);
473
719
  }
474
720
  };
475
- const initial = require_run_agent.initialAgentStep(machine, options.input);
721
+ const initial = initialAgentStep(machine, options.input);
476
722
  recordState(initial.snapshot);
477
723
  if (stopWhen?.(initial.snapshot)) witness = [];
478
724
  const advance = (step) => {
@@ -493,7 +739,7 @@ async function explore(machine, options, stopWhen) {
493
739
  step: current,
494
740
  blockedSrc: request.src
495
741
  };
496
- current = require_run_agent.resolveAgentStep(machine, current, request, asTextResult(textScript[request.src]));
742
+ current = resolveAgentStep(machine, current, request, asTextResult(textScript[request.src]));
497
743
  recordState(current.snapshot);
498
744
  continue;
499
745
  }
@@ -520,7 +766,7 @@ async function explore(machine, options, stopWhen) {
520
766
  step: current,
521
767
  blockedSrc: invoke.src
522
768
  };
523
- current = require_run_agent.resolveAgentStep(machine, current, invoke.id, invokeOutputs[invoke.src]);
769
+ current = resolveAgentStep(machine, current, invoke.id, invokeOutputs[invoke.src]);
524
770
  recordState(current.snapshot);
525
771
  continue;
526
772
  }
@@ -551,12 +797,12 @@ async function explore(machine, options, stopWhen) {
551
797
  unexplored.push(`max-depth: stopped at path [${path.map((e) => e.type).join(", ")}]`);
552
798
  return;
553
799
  }
554
- const failed = require_run_agent.rejectAgentStep(machine, settled, fork.id, errorScript[fork.src]);
800
+ const failed = rejectAgentStep(machine, settled, fork.id, errorScript[fork.src]);
555
801
  recordState(failed.snapshot);
556
802
  await visit(failed, [...path, { type: `xstate.error.actor.${fork.id}` }], depth + 1);
557
803
  if (!fork.isDecision) {
558
804
  if (fork.hasOutput) {
559
- const resolved = require_run_agent.resolveAgentStep(machine, settled, fork.id, fork.output);
805
+ const resolved = resolveAgentStep(machine, settled, fork.id, fork.output);
560
806
  recordState(resolved.snapshot);
561
807
  await visit(resolved, path, depth + 1);
562
808
  }
@@ -625,7 +871,7 @@ async function explore(machine, options, stopWhen) {
625
871
  prunedByGuard++;
626
872
  continue;
627
873
  }
628
- const next = require_run_agent.transitionAgentStep(machine, settled, event);
874
+ const next = transitionAgentStep(machine, settled, event);
629
875
  recordState(next.snapshot);
630
876
  await visit(next, [...path, event], depth + 1);
631
877
  }
@@ -812,11 +1058,13 @@ function matcherFor(expected) {
812
1058
  * @example Trajectory assertions in a test
813
1059
  * ```ts
814
1060
  * const statePath: unknown[] = [];
815
- * const result = await runAgent(machine, {
816
- * input,
817
- * executors,
818
- * onTransition: (snapshot) => statePath.push(snapshot.value),
819
- * });
1061
+ * const result = await runToQuiescence(
1062
+ * createAgentRuntime(machine, {
1063
+ * executors,
1064
+ * onTransition: (snapshot) => statePath.push(snapshot.value),
1065
+ * }),
1066
+ * { input },
1067
+ * );
820
1068
  *
821
1069
  * const path = matchesTrajectory(statePath, ['prompting', 'drafting', 'sent']);
822
1070
  * expect(path.matched, JSON.stringify(path.firstMiss)).toBe(true);
@@ -870,43 +1118,7 @@ function matchesTrajectory(actual, expected, options = {}) {
870
1118
  };
871
1119
  }
872
1120
  //#endregion
873
- //#region src/scripted-executors.ts
874
- /**
875
- * Scripted executors — a deterministic stand-in for a model host.
876
- *
877
- * `createScriptedExecutors` builds a full `{ generateText, streamText, decide }`
878
- * set that plays back canned answers keyed by
879
- * request name, so `runAgent` (or `provideExecutors`, or a bare
880
- * `TextLogic.execute`) runs with no API key and no network.
881
- *
882
- * One rule: entries for a name are consumed in order, and the last entry
883
- * repeats forever.
884
- *
885
- * @module
886
- */
887
- function toQueues(value) {
888
- return new Map(Object.entries(value ?? {}).map(([name, entries]) => [name, {
889
- entries: Array.isArray(entries) ? [...entries] : [entries],
890
- index: 0
891
- }]));
892
- }
893
- /**
894
- * Takes the next entry for `name`, holding on the last entry once the list is
895
- * spent. `undefined` means the script has no route for this name at all.
896
- * @internal
897
- */
898
- function takeEntry(queues, name) {
899
- const queue = queues.get(name) ?? queues.get("*");
900
- if (!queue || queue.entries.length === 0) return;
901
- const entry = queue.entries[Math.min(queue.index, queue.entries.length - 1)];
902
- queue.index++;
903
- return entry;
904
- }
905
- /** @internal */
906
- function noAnswer(name, queues) {
907
- const known = [...queues.keys()].join(", ") || "(none)";
908
- return /* @__PURE__ */ new Error(`No scripted answer for request '${name}'. Known: ${known}`);
909
- }
1121
+ //#region src/seam.ts
910
1122
  /** The only own keys an executor result carries. @internal */
911
1123
  const TEXT_RESULT_KEYS = new Set([
912
1124
  "result",
@@ -925,123 +1137,26 @@ function isTextResult(value) {
925
1137
  return Object.hasOwn(value, "result") && Object.keys(value).every((key) => TEXT_RESULT_KEYS.has(key));
926
1138
  }
927
1139
  /**
928
- * Resolves ONE scripted text entry to an executor result: a function entry is
1140
+ * Resolves ONE scripted entry to an executor result: a function entry is
929
1141
  * called with the request, and the value is taken as the executor result only
930
- * when it is one (see {@link isTextResult}). Shared with `runSeam`, whose routed
931
- * queues follow the same entry conventions. @internal
1142
+ * when it is one (see {@link isTextResult}). @internal
932
1143
  */
933
- async function resolveScriptedTextEntry(entry, request, info) {
1144
+ async function resolveSeamScriptEntry(entry, request, info) {
934
1145
  const value = typeof entry === "function" ? await entry(request, info) : entry;
935
1146
  return require_decision.isRecord(value) && isTextResult(value) ? value : { result: value };
936
1147
  }
937
- /**
938
- * Names a pending text request in an error message. Shared with `runSeam`.
939
- * @internal
940
- */
1148
+ /** Names a pending text request in an error message. @internal */
941
1149
  function describeText(request) {
942
1150
  return request.name ? `'${request.name}' (model '${request.model}')` : `(model '${request.model}')`;
943
1151
  }
944
1152
  /**
945
- * Stream semantics with no model: the whole text lands as one chunk. Shared
946
- * with `runSeam`, whose scripted answers stream the same way. @internal
1153
+ * Stream semantics with no model: the whole text lands as one chunk. @internal
947
1154
  */
948
1155
  function emitScriptedChunk(result, info) {
949
1156
  const text = require_decision.isRecord(result) ? result["result"] : void 0;
950
1157
  if (typeof text === "string") info?.onChunk?.(text);
951
1158
  }
952
1159
  /**
953
- * Executors that replay a script instead of calling a model. Every slot is
954
- * provided, so any machine binds.
955
- *
956
- * Answers are keyed by the request's resolved name — its authored `name`, else
957
- * the invoke `id` — and `"*"` serves anything unmatched.
958
- * Entries for a name are consumed in order and the last one repeats forever, so
959
- * a looping machine needs no extra option. A request the script has no route
960
- * for throws an ordinary `Error` from inside the executor, which reaches the
961
- * machine as an actor error.
962
- *
963
- * Queues are copied on creation, so the caller's arrays are never mutated and
964
- * each call builds a fresh, independent playthrough. Entries may be plain
965
- * values or functions of the request.
966
- *
967
- * @example
968
- * ```ts
969
- * const result = await runAgent(moderationMachine, {
970
- * input: { comment: 'honestly this update is terrible', trust: 20 },
971
- * executors: createScriptedExecutors({
972
- * decisions: { moderate: [{ type: 'FLAG', reason: 'Borderline tone.' }] },
973
- * }),
974
- * });
975
- * ```
976
- *
977
- * @example Dynamic entries
978
- * ```ts
979
- * createScriptedExecutors({
980
- * text: { draft: (request) => `Draft about ${request.prompt}` },
981
- * decisions: { route: (request) => ({ type: request.events[0]!.type }) },
982
- * });
983
- * ```
984
- */
985
- function createScriptedExecutors(script = {}) {
986
- const decisions = toQueues(script.decisions);
987
- const text = toQueues(script.text);
988
- const calls = [];
989
- const withDefaultUsage = (result) => result.usage || !script.usage ? result : {
990
- ...result,
991
- usage: script.usage
992
- };
993
- const nextText = async (kind, request, info) => {
994
- const name = request.name ?? "*";
995
- calls.push({
996
- kind,
997
- name,
998
- input: request.input,
999
- request
1000
- });
1001
- const entry = takeEntry(text, name);
1002
- if (entry === void 0) throw noAnswer(name, text);
1003
- return withDefaultUsage(await resolveScriptedTextEntry(entry, request, info));
1004
- };
1005
- return {
1006
- calls,
1007
- generateText: (request, info) => nextText("generateText", request, info),
1008
- streamText: async (request, info) => {
1009
- const name = request.name ?? "*";
1010
- const streamEntry = script.stream?.[name] ?? script.stream?.["*"];
1011
- if (streamEntry !== void 0) {
1012
- calls.push({
1013
- kind: "streamText",
1014
- name,
1015
- input: request.input,
1016
- request
1017
- });
1018
- const resolved = typeof streamEntry === "function" ? await streamEntry(request, info) : streamEntry;
1019
- const chunks = typeof resolved === "string" ? [resolved] : [...resolved];
1020
- for (const chunk of chunks) info?.onChunk?.(chunk);
1021
- return withDefaultUsage({ result: chunks.join("") });
1022
- }
1023
- const result = await nextText("streamText", request, info);
1024
- emitScriptedChunk(result, info);
1025
- return result;
1026
- },
1027
- decide: async (request) => {
1028
- const name = request.name ?? request.id;
1029
- calls.push({
1030
- kind: "decide",
1031
- name,
1032
- input: request.input,
1033
- request
1034
- });
1035
- const entry = takeEntry(decisions, name);
1036
- if (entry === void 0) throw noAnswer(name, decisions);
1037
- const value = typeof entry === "function" ? await entry(request) : entry;
1038
- return withDefaultUsage(require_decision.isRecord(value) && typeof value["type"] !== "string" && "event" in value ? value : { event: value });
1039
- }
1040
- };
1041
- }
1042
- //#endregion
1043
- //#region src/seam.ts
1044
- /**
1045
1160
  * The seam's own answer, for scoring: the executor result's `result`.
1046
1161
  * @internal
1047
1162
  */
@@ -1063,8 +1178,8 @@ async function seamOutputOf(result, request) {
1063
1178
  * @example No API key: the seam is scripted too, so the whole thing runs offline.
1064
1179
  * ```ts
1065
1180
  * const run = await runSeam(emailDrafter, {
1066
- * scripts: { promptEvaluator: [vague, complete], emailDrafter: [draft] },
1067
- * seam: { request: 'evaluatePrompt' },
1181
+ * scripts: { writeFollowUps: [followUps], emailDrafter: [draft] },
1182
+ * seam: { request: 'writeFollowUps' },
1068
1183
  * respond: ({ state }) => (state === 'prompting' ? { type: 'PROMPT_SUBMITTED', prompt } : null),
1069
1184
  * });
1070
1185
  *
@@ -1100,7 +1215,7 @@ async function runSeam(machine, options) {
1100
1215
  const takeScriptedSlot = async (request, info) => {
1101
1216
  const queue = queues.get(queueKeyOf(request));
1102
1217
  if (!queue?.length) return;
1103
- return resolveScriptedTextEntry(options.repeatLast && queue.length === 1 ? queue[0] : queue.shift(), request, info);
1218
+ return resolveSeamScriptEntry(options.repeatLast && queue.length === 1 ? queue[0] : queue.shift(), request, info);
1104
1219
  };
1105
1220
  const scriptedAnswer = async (request, info) => {
1106
1221
  const scripted = await takeScriptedSlot(request, info);
@@ -1157,9 +1272,7 @@ async function runSeam(machine, options) {
1157
1272
  let result;
1158
1273
  const maxTurns = options.maxTurns ?? 12;
1159
1274
  for (let turn = 0; turn <= maxTurns; turn++) {
1160
- result = await require_run_agent.runAgent(machine, {
1161
- ...snapshot ? { snapshot } : { input: options.input },
1162
- ...event ? { event } : {},
1275
+ result = await require_run_agent.runToQuiescence(require_run_agent.createAgentRuntime(machine, {
1163
1276
  ...options.actors ? { actors: options.actors } : {},
1164
1277
  executors,
1165
1278
  onTransition: (next, causedBy) => {
@@ -1168,6 +1281,9 @@ async function runSeam(machine, options) {
1168
1281
  liveEvents = events.length;
1169
1282
  statePath.push(next.value);
1170
1283
  }
1284
+ }), {
1285
+ ...snapshot ? { snapshot } : { input: options.input },
1286
+ ...event ? { event } : {}
1171
1287
  });
1172
1288
  if (result.status !== "idle" || turn === maxTurns) break;
1173
1289
  const next = options.respond?.({
@@ -1214,7 +1330,6 @@ exports.AgentLintError = AgentLintError;
1214
1330
  exports.AgentUnknownStateError = AgentUnknownStateError;
1215
1331
  exports.assertAgentMachine = assertAgentMachine;
1216
1332
  exports.canReach = canReach;
1217
- exports.createScriptedExecutors = createScriptedExecutors;
1218
1333
  exports.explorePaths = explorePaths;
1219
1334
  exports.lintAgentMachine = lintAgentMachine;
1220
1335
  exports.matchesTrajectory = matchesTrajectory;