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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/ai-sdk.cjs +4 -2
  2. package/dist/ai-sdk.d.cts +1 -1
  3. package/dist/ai-sdk.d.mts +1 -1
  4. package/dist/ai-sdk.mjs +3 -2
  5. package/dist/{decision-C11xuud2.mjs → decision-DsIkEuHz.mjs} +48 -28
  6. package/dist/{decision-DnQCQPew.cjs → decision-t26zsnSR.cjs} +64 -43
  7. package/dist/{event-log-store-CQJq8_v4.d.cts → event-log-store-Bz7HDBkE.d.cts} +11 -12
  8. package/dist/{event-log-store-B-1fcfkT.mjs → event-log-store-DmIDosD6.mjs} +22 -14
  9. package/dist/{event-log-store-yquOV1TX.cjs → event-log-store-a_TKy1gk.cjs} +22 -14
  10. package/dist/{event-log-store-BrC9Q1xW.d.mts → event-log-store-hrA1vqtN.d.mts} +11 -12
  11. package/dist/index.cjs +265 -109
  12. package/dist/index.d.cts +136 -80
  13. package/dist/index.d.mts +136 -80
  14. package/dist/index.mjs +262 -107
  15. package/dist/machines.cjs +13 -17
  16. package/dist/machines.d.cts +14 -17
  17. package/dist/machines.d.mts +14 -17
  18. package/dist/machines.mjs +13 -17
  19. package/dist/otel.cjs +1 -0
  20. package/dist/otel.d.cts +1 -1
  21. package/dist/otel.d.mts +1 -1
  22. package/dist/{run-agent-r9OD4z8F.d.cts → run-agent--4bbms-D.d.cts} +121 -48
  23. package/dist/{run-agent-DQIDikfd.d.mts → run-agent-CwmzAZwj.d.mts} +121 -48
  24. package/dist/{setup-agent-C3ETi_HZ.cjs → setup-agent-BFA4VKpN.cjs} +31 -33
  25. package/dist/{setup-agent-BrE2zFDy.mjs → setup-agent-CPFPN06s.mjs} +31 -28
  26. package/dist/sqlite.cjs +3 -9
  27. package/dist/sqlite.d.cts +1 -1
  28. package/dist/sqlite.d.mts +1 -1
  29. package/dist/sqlite.mjs +3 -9
  30. package/dist/{text-logic-VcWqO-Cl.d.cts → text-logic-Cavva1W6.d.cts} +24 -8
  31. package/dist/{text-logic-RvnlD-An.d.mts → text-logic-Er5KkTX6.d.mts} +24 -8
  32. package/dist/validate.cjs +436 -0
  33. package/dist/validate.d.cts +31 -0
  34. package/dist/validate.d.mts +31 -0
  35. package/dist/validate.mjs +411 -0
  36. package/package.json +15 -1
  37. package/schemas/agent-workflow.json +2 -2
  38. package/skills/generate-machine/SKILL.md +12 -14
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as AgentError } from "./errors-CeSXQx0v.mjs";
2
- import { _ as initialAgentStep, a as AGENT_USAGE_EVENT_TYPE, b as appendMessages, c as createReplayEntry, d as initEntry, f as replay, g as getInvokeEffectMetadata, h as executeAgentRequest, i as AGENT_INIT_EVENT_TYPE, l as diffEventLogs, m as verifyReplay, n as getAgentSchemas, o as AgentReplayDivergenceError, p as validateReplayEntries, r as setupAgent, s as AgentReplayMachineMismatchError, t as createAgentSchemas, u as getAgentEffects, v as resolveAgentStep, x as messagesSchema, y as transitionAgentStep } from "./setup-agent-BrE2zFDy.mjs";
3
- import { A as isUnboundPlaceholder, B as getMachineStructuralHash, C as parseStructuredEnvelope, D as getMachineStaticTransitionTargets, E as executorBoundLogics, G as toolMessage, H as isStandardSchema, I as findNonSerializableContextPaths, K as userMessage, L as getAgentMessages, O as getMachineSuspensionPredicate, P as assistantMessage, R as getJsonSchema, S as parseOutput, U as persistSnapshot, V as getStateMeta, W as systemMessage, _ as getAgentOutputMode, a as resolveDecision, b as normalizeGeneratorResult, c as AGENT_USAGE_TOKEN_FIELDS, f as bindRequestExecutor, h as createTextLogic, i as renderDecisionAttempts, k as getRegisteredAgentExecutionOptions, o as getAcceptedEvents, p as buildEnvelopeSchema, q as validateSchemaSync, r as isDecisionLogic, s as parseAgentEvent, t as AgentDecisionExhaustedError, u as INTERPRET_SOURCE, v as getCallUsage, x as parseModelRef, y as isTextLogic, z as getJsonSchemaSync } from "./decision-C11xuud2.mjs";
4
- import { a as assertJsonSerializable, i as assertAgentLogEntry, n as AgentEventLogConflictError, o as createInMemoryEventLogStore, r as NonSerializableAgentEventError, s as assertEventLogStoreConformance, t as AGENT_EVENT_SCHEMA_VERSION } from "./event-log-store-B-1fcfkT.mjs";
2
+ import { _ as resolveAgentStep, a as AGENT_USAGE_EVENT_TYPE, b as messagesSchema, c as createReplayEntry, d as initEntry, f as replay, g as initialAgentStep, h as getInvokeEffectMetadata, i as AGENT_INIT_EVENT_TYPE, l as diffEventLogs, m as executeAgentRequest, n as getAgentSchemas, o as AgentReplayDivergenceError, p as validateReplayEntries, r as setupAgent, s as AgentReplayMachineMismatchError, t as createAgentSchemas, u as getAgentEffects, v as transitionAgentStep, y as appendMessages } from "./setup-agent-CPFPN06s.mjs";
3
+ import { A as isUnboundPlaceholder, B as getMachineStructuralHash, C as parseStructuredEnvelope, D as getMachineIdlePredicate, E as executorBoundLogics, G as toolMessage, H as isStandardSchema, I as findNonSerializableContextPaths, K as userMessage, L as getAgentMessages, O as getMachineStaticTransitionTargets, P as assistantMessage, R as getJsonSchema, S as parseOutput, U as resolveMachineVersion, V as getStateMeta, W as systemMessage, _ as getAgentOutputMode, a as resolveDecision, b as normalizeGeneratorResult, c as AGENT_USAGE_TOKEN_FIELDS, f as bindRequestExecutor, h as createTextLogic, i as renderDecisionAttempts, k as getRegisteredAgentExecutionOptions, o as getAcceptedEvents, p as buildEnvelopeSchema, q as validateSchemaSync, r as isDecisionLogic, s as parseAgentEvent, t as AgentDecisionExhaustedError, u as INTERPRET_SOURCE, v as getCallUsage, x as parseModelRef, y as isTextLogic, z as getJsonSchemaSync } from "./decision-DsIkEuHz.mjs";
4
+ import { a as assertJsonSerializable, i as assertAgentLogEntry, n as AgentEventLogConflictError, o as createInMemoryEventLogStore, r as NonSerializableAgentEventError, s as assertEventLogStoreConformance, t as AGENT_EVENT_SCHEMA_VERSION } from "./event-log-store-DmIDosD6.mjs";
5
5
  import { createActor, createAsyncLogic, getNextTransitions, isMachineSnapshot } from "xstate";
6
6
  //#region src/internal/state-request-pass.ts
7
7
  async function runTextPhase(stateRequest, baseMessages, deps) {
@@ -102,7 +102,7 @@ async function runAdvancePhase(plan, deps) {
102
102
  messages: [...deps.messages],
103
103
  events,
104
104
  attempts: []
105
- }, deps.decide, {
105
+ }, { decide: deps.decide }, {
106
106
  signal: deps.signal,
107
107
  canTake: (event) => deps.getSnapshot().can(event)
108
108
  });
@@ -138,8 +138,7 @@ async function runStateRequestPass(requests, deps) {
138
138
  * {@link getAcceptedEvents}). A programmer/integration error, in the same
139
139
  * class as runAgent's bind-time throws — it throws rather than settling an
140
140
  * `error` result. A type-legal event a guard rejects is NOT this error (the
141
- * machine simply takes no transition). Opt out with
142
- * {@link RunAgentOptions.onIllegalResumeEvent} `'ignore'`.
141
+ * machine simply takes no transition). Always enforced; there is no opt-out.
143
142
  */
144
143
  var AgentIllegalResumeEventError = class extends AgentError {
145
144
  eventType;
@@ -257,7 +256,7 @@ function toJsonValue(value, ancestors) {
257
256
  * in a JSONL file). Live values are sanitized rather than trusted:
258
257
  *
259
258
  * - Snapshots (`run.start`, `machine.transition`, `run.end`) go through the
260
- * same JSON round-trip as {@link persistSnapshot}, so what lands on disk is
259
+ * same JSON round-trip as `machine.getPersistedSnapshot(...)`, so what lands on disk is
261
260
  * what a resume would see.
262
261
  * - `request.end`'s `raw` (a provider SDK object, frequently cyclic) is DROPPED
263
262
  * unless `includeRaw` is set, in which case it is sanitized like everything
@@ -284,11 +283,92 @@ function serializeTraceEvent(event, options = {}) {
284
283
  }
285
284
  return out;
286
285
  }
286
+ function snapshotNodes(snapshot) {
287
+ return (snapshot._nodes ?? []).map((raw) => {
288
+ const node = raw;
289
+ return {
290
+ id: node.id ?? "",
291
+ key: node.key ?? "",
292
+ ...node.description !== void 0 ? { description: node.description } : {},
293
+ tags: [...node.tags ?? []],
294
+ ...node.meta !== void 0 ? { meta: node.meta } : {},
295
+ ownEvents: [...node.ownEvents ?? []],
296
+ leaf: Object.keys(node.states ?? {}).length === 0
297
+ };
298
+ });
299
+ }
300
+ /**
301
+ * The active state nodes of a snapshot, as plain {@link AgentSnapshotNode}
302
+ * descriptors. The escape hatch under {@link getSnapshotRequests}: use it when
303
+ * your `getRequests` hook needs to build requests some other way, so host code
304
+ * never touches xstate's private `snapshot._nodes`.
305
+ */
306
+ function getSnapshotNodes(snapshot) {
307
+ return snapshotNodes(snapshot);
308
+ }
309
+ /**
310
+ * Builds the {@link AgentStateRequest}s for a snapshot straight from its active
311
+ * state nodes — the prompts-in-descriptions recipe as a function, so a
312
+ * {@link RunAgentOptions.getRequests} hook is one line and never reaches into
313
+ * xstate's private `snapshot._nodes`:
314
+ *
315
+ * ```ts
316
+ * runAgent(machine, {
317
+ * executors,
318
+ * getRequests: (snapshot) => getSnapshotRequests(snapshot, { model: 'writer' }),
319
+ * });
320
+ * ```
321
+ *
322
+ * Each described active node becomes one request: `prompt` from the node's
323
+ * `description`, `kind: 'decision'` when it is tagged `'decision'`, `system`
324
+ * from `meta.role` when present, `allowedEvents` scoped to the node's own
325
+ * events, and an explicit `onDone` when the node has exactly one own event
326
+ * (single-outcome states advance deterministically; anything else falls
327
+ * through to a `decide` call). Nodes tagged `'waiting'` produce nothing, so the
328
+ * run settles idle for a human. Override any of it with `filter`/`map`.
329
+ */
330
+ function getSnapshotRequests(snapshot, options) {
331
+ const filter = options.filter ?? ((node) => !!node.description && !node.tags.includes("waiting"));
332
+ const requests = [];
333
+ for (const node of snapshotNodes(snapshot)) {
334
+ if (!filter(node)) continue;
335
+ const system = node.meta?.role;
336
+ const request = {
337
+ model: options.model,
338
+ prompt: node.description ?? "",
339
+ kind: node.tags.includes("decision") ? "decision" : "text",
340
+ ...system !== void 0 ? { system } : {},
341
+ ...node.ownEvents.length > 0 ? { allowedEvents: node.ownEvents } : {},
342
+ ...node.ownEvents.length === 1 ? { onDone: { type: node.ownEvents[0] } } : {}
343
+ };
344
+ const mapped = options.map ? options.map(request, node) : request;
345
+ if (mapped) requests.push(mapped);
346
+ }
347
+ return requests;
348
+ }
287
349
  let nextRunAgentTraceId = 1;
350
+ /**
351
+ * Thrown into the invoke that would have made the call once
352
+ * {@link RunAgentOptions.maxModelCalls} is spent. It reaches the machine
353
+ * through the normal error channel, so an invoke's `onError` can branch on it
354
+ * (`error.code === 'max-model-calls'`, the same string the settled result's
355
+ * `cause` uses) and route to a degraded/finish state instead of failing the
356
+ * run. Unhandled, it settles `{ status: 'error', cause: 'max-model-calls' }`.
357
+ *
358
+ * ```ts
359
+ * onError: [
360
+ * { guard: ({ event }) => event.error?.code === 'max-model-calls', target: 'budgetSpent' },
361
+ * { target: 'failed' },
362
+ * ]
363
+ * ```
364
+ */
288
365
  var AgentMaxModelCallsExceededError = class extends AgentError {
289
- constructor() {
290
- super("max-model-calls-exceeded", "runAgent exceeded maxModelCalls.");
366
+ /** The budget that was exceeded (`options.maxModelCalls`). */
367
+ maxModelCalls;
368
+ constructor(maxModelCalls) {
369
+ super("max-model-calls", `runAgent exceeded maxModelCalls (${maxModelCalls}). Raise the budget, or handle it in the invoke's onError (error.code === 'max-model-calls').`);
291
370
  this.name = "AgentMaxModelCallsExceededError";
371
+ this.maxModelCalls = maxModelCalls;
292
372
  }
293
373
  };
294
374
  function wrapsDecisionExhausted(error) {
@@ -328,7 +408,7 @@ function collectConfiguredInvokeSrcs(stateConfig, stateName, out) {
328
408
  * walk into invoked child machines (their internal agent requests are opaque
329
409
  * to the parent-level source walk otherwise).
330
410
  */
331
- function isStateMachine(logic) {
411
+ function isStateMachineLogic(logic) {
332
412
  return !!logic && typeof logic === "object" && "config" in logic && "root" in logic && typeof logic.provide === "function" && typeof logic.sources === "object" && !!logic.sources?.actors;
333
413
  }
334
414
  /**
@@ -360,7 +440,7 @@ function assertMachineBindable(machine, effectiveSources, executors, ctx) {
360
440
  const where = ctx.isChild ? `child machine '${ctx.childPath}' state` : "state";
361
441
  for (const { stateName, src } of invokes) {
362
442
  if (typeof src !== "string") {
363
- if (isStateMachine(src)) {
443
+ if (isStateMachineLogic(src)) {
364
444
  assertChildMachineBindable(src, src, stateName, executors, ctx);
365
445
  continue;
366
446
  }
@@ -369,7 +449,7 @@ function assertMachineBindable(machine, effectiveSources, executors, ctx) {
369
449
  }
370
450
  const logic = effectiveSources[src];
371
451
  if (logic === void 0) throw new Error(`runAgent: ${where} '${stateName}' invokes unregistered actor source '${src}'. Provide it via machine.provide({ actors: { '${src}': ... } }) or runAgent(machine, { actors: { '${src}': ... } }).`);
372
- if (isStateMachine(logic)) {
452
+ if (isStateMachineLogic(logic)) {
373
453
  assertChildMachineBindable(logic, src, stateName, executors, ctx);
374
454
  continue;
375
455
  }
@@ -415,18 +495,16 @@ function unrebindableChildRequestError(childPath, stateName, requestSrc, kind) {
415
495
  return /* @__PURE__ */ new Error(`runAgent: child machine '${childPath}' (state '${stateName}') invokes ${kind} source '${requestSrc}', which has no host execution and is reached through a direct-object invoke src that runAgent cannot rebind. Requests reached through string-keyed actor sources inherit runAgent's generateText/streamText/decide executors automatically; a direct-object child machine does not. Either bind the request with its own executor (requestLogic.withExecutor(...)), or register the child as a string-keyed actor source (machine.provide({ actors: { <child>: childMachine } })) and invoke it by name.`);
416
496
  }
417
497
  /**
418
- * True when the snapshot's active states declare a transition for the reserved
419
- * `'@agent.usage'` type EXPLICITLY. A catch-all `on: { '*': … }` deliberately
420
- * does not count: a wildcard is a machine's own event vocabulary, not an
421
- * opt-in to a library-reserved event, and `snapshot.can(event)` alone cannot
422
- * tell the two apart (it answers "would this event be taken?", which a
423
- * wildcard makes true for everything). Gating delivery on the explicit
424
- * declaration is what keeps `@agent.usage` opt-in by construction — and keeps
425
- * a wildcard machine's context and event log byte-identical to a run without
426
- * the feature. @internal
498
+ * True when the snapshot's active states declare a transition that would
499
+ * receive the reserved `'@agent.usage'` type an explicit `on: { '@agent.usage'
500
+ * }` OR a catch-all `on: { '*': }`. Plain XState semantics apply unmodified:
501
+ * a wildcard matches every event delivered to the machine, reserved ones
502
+ * included. (The MODEL-facing side stays closed: `getAcceptedEvents` drops
503
+ * `@agent.*` before any `allowedEvents` matching, so a wildcard never offers
504
+ * the reserved event as a decision candidate.) @internal
427
505
  */
428
506
  function declaresUsageTransition(snapshot) {
429
- return getNextTransitions(snapshot).some((transition) => transition.eventType === AGENT_USAGE_EVENT_TYPE);
507
+ return getNextTransitions(snapshot).some((transition) => transition.eventType === "@agent.usage" || transition.eventType === "*");
430
508
  }
431
509
  /**
432
510
  * Builds the ONE place a trace payload is emitted: it hands the payload to the
@@ -493,7 +571,7 @@ function bindTextLogic(logic, runCtx) {
493
571
  const self = selfArg;
494
572
  const { id, src } = selfIdAndSrc(self);
495
573
  const executor = logic.mode === "stream" ? runCtx.streamText : runCtx.generateText;
496
- if (!executor) throw new Error(`runAgent: no '${logic.mode === "stream" ? "streamText" : "generateText"}' executor provided.`);
574
+ if (!executor) throw new Error(`No '${logic.mode === "stream" ? "streamText" : "generateText"}' executor provided.`);
497
575
  const requestWithTools = {
498
576
  ...request,
499
577
  tools: request.tools ?? {}
@@ -565,17 +643,22 @@ function bindTextLogic(logic, runCtx) {
565
643
  });
566
644
  }
567
645
  function createCountingDecide(runCtx, self) {
568
- return async (attemptRequest) => {
646
+ return async (attemptRequest, info) => {
569
647
  runCtx.consumeModelCall();
570
648
  runCtx.emitTrace?.({
571
649
  type: "request.start",
572
650
  request: attemptRequest
573
651
  }, self);
574
652
  try {
653
+ const { id } = selfIdAndSrc(self);
575
654
  const result = await runCtx.decide(runCtx.runId !== void 0 ? {
576
655
  ...attemptRequest,
577
656
  runId: runCtx.runId
578
- } : attemptRequest);
657
+ } : attemptRequest, {
658
+ ...info,
659
+ ...runCtx.runId !== void 0 ? { runId: runCtx.runId } : {},
660
+ ...info?.requestId === void 0 && id !== "" ? { requestId: id } : {}
661
+ });
579
662
  const usage = getCallUsage(result);
580
663
  if (usage) {
581
664
  const { src } = selfIdAndSrc(self);
@@ -621,7 +704,7 @@ function createCountingDecide(runCtx, self) {
621
704
  */
622
705
  function bindDecisionLogic(logic, runCtx) {
623
706
  const decisionLogic = createAsyncLogic({ run: async ({ input, signal, self: selfArg }) => {
624
- if (!runCtx.decide) throw new Error("runAgent: no 'decide' executor provided.");
707
+ if (!runCtx.decide) throw new Error("No 'decide' executor provided.");
625
708
  const self = selfArg;
626
709
  const { id } = selfIdAndSrc(self);
627
710
  const declaredEventTypes = logic.allowedEventTypes?.(input);
@@ -635,7 +718,7 @@ function bindDecisionLogic(logic, runCtx) {
635
718
  ...logic.request(input),
636
719
  id,
637
720
  events
638
- }, createCountingDecide(runCtx, self), {
721
+ }, { decide: createCountingDecide(runCtx, self) }, {
639
722
  maxRetries: logic.maxRetries,
640
723
  signal,
641
724
  canTake: (event) => actorRef ? actorRef.getSnapshot().can(event) : true
@@ -680,7 +763,7 @@ function rootTraceState(root) {
680
763
  if (!state) {
681
764
  const logic = root.logic;
682
765
  const machineId = (logic?.config)?.id ?? logic?.id ?? "(machine)";
683
- const machineVersion = logic ? getMachineStructuralHash(logic) : "";
766
+ const machineVersion = logic ? resolveMachineVersion(logic) : "";
684
767
  state = {
685
768
  runId: `run_${nextProvideRunId++}`,
686
769
  seq: 0,
@@ -752,9 +835,9 @@ function provideBindContext(machine, executors, options) {
752
835
  * under a live `createActor` tree) on the `provideExecutors` path.
753
836
  *
754
837
  * Gating is identical on both: the target snapshot must be active, must declare
755
- * an `'@agent.usage'` transition EXPLICITLY (see {@link declaresUsageTransition}
756
- * — a catch-all `on: { '*' }` is not an opt-in), and must be able to take the
757
- * event. `onDropped` is the run path's straggler gate: it returns `true` for a
838
+ * an `'@agent.usage'` transition explicitly, or through a catch-all
839
+ * `on: { '*' }` (see {@link declaresUsageTransition}) and must be able to
840
+ * take the event. `onDropped` is the run path's straggler gate: it returns `true` for a
758
841
  * call that settled after the cycle resolved, which drops the event (traced as
759
842
  * `usage.dropped`) rather than delivering it. Uncontrolled mode has no cycle to
760
843
  * settle, so it passes no gate and has no dropped stragglers.
@@ -796,6 +879,19 @@ function bindDecisionForProvide(machine, logic, executors, options) {
796
879
  return bindDecisionLogic(logic, provideBindContext(machine, executors, options));
797
880
  }
798
881
  /**
882
+ * Recursively binds an invoked child state machine for {@link provideExecutors},
883
+ * with the same semantics `runAgent` applies ({@link rebindChildMachine}):
884
+ * string-keyed text/decision sources at any depth inherit the host executors,
885
+ * a source that carries its own executor is left alone, and a cycle is
886
+ * returned as-is. Each machine in the tree is bound with its own registered
887
+ * `setupAgent` schemas. Returns the original machine when nothing needed
888
+ * wrapping. @internal
889
+ */
890
+ function bindChildMachineForProvide(childMachine, executors, options, visited) {
891
+ const ctxFor = (target) => provideBindContext(target, executors, options);
892
+ return rebindChildMachine(childMachine, ctxFor(childMachine), visited, ctxFor);
893
+ }
894
+ /**
799
895
  * Validates `input` against the machine's registered input schema, returning
800
896
  * the schema's output — so defaults are filled and transforms applied before
801
897
  * the value reaches `createActor` or the replayable event log.
@@ -829,9 +925,10 @@ function resolveMachineInput(machine, input) {
829
925
  * (`executorBoundLogics`) is left as-is: explicit binding shadows inheritance.
830
926
  * Cycle-safe via `visited` (a machine that invokes itself is returned as-is).
831
927
  */
832
- function rebindChildMachine(childMachine, runCtx, visited) {
928
+ function rebindChildMachine(childMachine, runCtx, visited, ctxFor) {
833
929
  if (visited.has(childMachine)) return childMachine;
834
930
  const childVisited = new Set([...visited, childMachine]);
931
+ runCtx = ctxFor ? ctxFor(childMachine) : runCtx;
835
932
  const sources = childMachine.sources.actors;
836
933
  const wrapped = {};
837
934
  for (const [key, logic] of Object.entries(sources)) {
@@ -843,8 +940,8 @@ function rebindChildMachine(childMachine, runCtx, visited) {
843
940
  if (!executorBoundLogics.has(logic)) wrapped[key] = bindTextLogic(logic, runCtx);
844
941
  continue;
845
942
  }
846
- if (isStateMachine(logic)) {
847
- const rebound = rebindChildMachine(logic, runCtx, childVisited);
943
+ if (isStateMachineLogic(logic)) {
944
+ const rebound = rebindChildMachine(logic, runCtx, childVisited, ctxFor);
848
945
  if (rebound !== logic) wrapped[key] = rebound;
849
946
  continue;
850
947
  }
@@ -927,7 +1024,7 @@ function createAgentSession(machine, options, lifecycle) {
927
1024
  let traceSeq = 0;
928
1025
  const resolvedInput = resolveMachineInput(machine, options.input);
929
1026
  const machineId = machine.config.id ?? machine.id ?? "(machine)";
930
- const machineVersion = options.machineVersion ?? machine.version ?? getMachineStructuralHash(machine);
1027
+ const machineVersion = resolveMachineVersion(machine);
931
1028
  const agentMeta = {
932
1029
  machineId,
933
1030
  version: machineVersion
@@ -949,10 +1046,10 @@ function createAgentSession(machine, options, lifecycle) {
949
1046
  onTransition: options.onTransition
950
1047
  });
951
1048
  const consumeModelCall = () => {
952
- if (budgetExceeded) throw new AgentMaxModelCallsExceededError();
1049
+ if (budgetExceeded) throw new AgentMaxModelCallsExceededError(maxModelCalls);
953
1050
  if (modelCallCount + 1 > maxModelCalls) {
954
1051
  budgetExceeded = true;
955
- throw new AgentMaxModelCallsExceededError();
1052
+ throw new AgentMaxModelCallsExceededError(maxModelCalls);
956
1053
  }
957
1054
  modelCallCount += 1;
958
1055
  };
@@ -1026,19 +1123,20 @@ function createAgentSession(machine, options, lifecycle) {
1026
1123
  if (!executorBoundLogics.has(logic)) wrappedSources[key] = bindTextLogic(logic, runCtx);
1027
1124
  continue;
1028
1125
  }
1029
- if (isStateMachine(logic)) {
1126
+ if (isStateMachineLogic(logic)) {
1030
1127
  const rebound = rebindChildMachine(logic, runCtx, new Set([machine]));
1031
1128
  if (rebound !== logic) wrappedSources[key] = rebound;
1032
1129
  continue;
1033
1130
  }
1034
1131
  }
1035
1132
  const boundMachine = provided.provide({ actors: wrappedSources });
1036
- const declaredSuspensionPredicate = options.isSuspended ?? getMachineSuspensionPredicate(machine);
1037
- const isSuspended = declaredSuspensionPredicate ?? (() => false);
1133
+ const declaredIdlePredicate = options.isIdle ?? getMachineIdlePredicate(machine);
1134
+ const isIdle = declaredIdlePredicate ?? (() => false);
1135
+ const machineDeclaresMigrate = typeof machine.config.migrate === "function";
1038
1136
  let effectiveSnapshot = options.snapshot;
1039
1137
  if (effectiveSnapshot !== void 0) {
1040
1138
  const from = effectiveSnapshot.agentMeta?.version ?? effectiveSnapshot.version;
1041
- if (from !== void 0 && from !== machineVersion) {
1139
+ if (from !== void 0 && from !== machineVersion && !(machineDeclaresMigrate && !options.migrateSnapshot)) {
1042
1140
  const info = {
1043
1141
  from,
1044
1142
  to: machineVersion
@@ -1057,7 +1155,7 @@ function createAgentSession(machine, options, lifecycle) {
1057
1155
  effectiveSnapshot = machine.getPersistedSnapshot(replayedSnapshot);
1058
1156
  }
1059
1157
  const machineOwnVersion = machine.version;
1060
- if (effectiveSnapshot !== void 0 && effectiveSnapshot.version !== machineOwnVersion) {
1158
+ if (!machineDeclaresMigrate && effectiveSnapshot !== void 0 && effectiveSnapshot.version !== machineOwnVersion) {
1061
1159
  const aligned = Object.assign(Object.create(Object.getPrototypeOf(effectiveSnapshot)), effectiveSnapshot);
1062
1160
  if (machineOwnVersion === void 0) delete aligned.version;
1063
1161
  else aligned.version = machineOwnVersion;
@@ -1071,7 +1169,7 @@ function createAgentSession(machine, options, lifecycle) {
1071
1169
  if (!options.getRequests && !options.messages && messages.length === 0) return;
1072
1170
  snapshot.messages = [...messages];
1073
1171
  };
1074
- if (effectiveSnapshot !== void 0 && options.event !== void 0 && (options.onIllegalResumeEvent ?? "throw") === "throw") {
1172
+ if (effectiveSnapshot !== void 0 && options.event !== void 0) {
1075
1173
  const acceptedTypes = getAcceptedEvents(createActor(boundMachine, { snapshot: effectiveSnapshot }).getSnapshot(), { schemas: runCtx.schemas }).map((descriptor) => descriptor.type);
1076
1174
  const eventType = options.event.type;
1077
1175
  if (!acceptedTypes.includes(eventType)) throw new AgentIllegalResumeEventError(eventType, acceptedTypes);
@@ -1217,9 +1315,9 @@ function createAgentSession(machine, options, lifecycle) {
1217
1315
  const current = actor.getSnapshot();
1218
1316
  if (isIdleSnapshot(current, { ignoreUserInputChildren: userInputIsPlaceholder })) {
1219
1317
  if (!maybeInterpret(current)) {
1220
- if (!declaredSuspensionPredicate && current.status === "active" && !warnedHeuristicIdle && process.env.NODE_ENV !== "production") {
1318
+ if (!declaredIdlePredicate && current.status === "active" && !warnedHeuristicIdle && process.env.NODE_ENV !== "production") {
1221
1319
  warnedHeuristicIdle = true;
1222
- console.warn("[@statelyai/agent] runAgent settled idle via the timing heuristic (no suspension predicate declared). This is best-effort; for deterministic idle detection, declare setupAgent({ isSuspended }) or pass runAgent(machine, { isSuspended }), e.g. (s) => s.hasTag('waiting').");
1320
+ console.warn("[@statelyai/agent] runAgent settled idle via the timing heuristic (no idle predicate declared). This is best-effort; for deterministic idle detection, declare setupAgent({ isIdle }) or pass runAgent(machine, { isIdle }), e.g. (s) => s.hasTag('waiting').");
1223
1321
  }
1224
1322
  settleIdle(current);
1225
1323
  }
@@ -1274,11 +1372,11 @@ function createAgentSession(machine, options, lifecycle) {
1274
1372
  });
1275
1373
  return;
1276
1374
  }
1277
- if (!deliveringResumeEvent && isSuspended(snapshot) && isIdleSnapshot(snapshot, { ignoreUserInputChildren: userInputIsPlaceholder })) {
1375
+ if (!deliveringResumeEvent && isIdle(snapshot) && isIdleSnapshot(snapshot, { ignoreUserInputChildren: userInputIsPlaceholder })) {
1278
1376
  queueMicrotask(() => {
1279
1377
  if (settled) return;
1280
1378
  const current = actor.getSnapshot();
1281
- if (isSuspended(current) && isIdleSnapshot(current, { ignoreUserInputChildren: userInputIsPlaceholder })) {
1379
+ if (isIdle(current) && isIdleSnapshot(current, { ignoreUserInputChildren: userInputIsPlaceholder })) {
1282
1380
  if (!maybeInterpret(current)) settleIdle(current);
1283
1381
  } else scheduleIdleCheck();
1284
1382
  });
@@ -1575,9 +1673,12 @@ function createAgentRun(machine, options) {
1575
1673
  * Throws at bind time if a source needs an executor kind that `executors` does
1576
1674
  * not provide.
1577
1675
  *
1578
- * `provideExecutors` does not descend into invoked child state machines: a string-keyed child
1579
- * machine source is left untouched, so a child with its own agent invokes needs
1580
- * its own `provideExecutors(...)` (or `runAgent`, which does rebind children).
1676
+ * Executor inheritance is RECURSIVE, exactly as in `runAgent`: a string-keyed
1677
+ * invoked child machine is rebound too, so its own text/decision requests — at
1678
+ * any depth reach the same host executors. A direct-object invoke `src`
1679
+ * cannot be swapped via `.provide`, so nothing under one inherits; bind those
1680
+ * with `.withExecutor(...)` or register the child as a string-keyed source. A
1681
+ * source that already carries its own executor is never overwritten.
1581
1682
  */
1582
1683
  function provideExecutors(machine, executors, options = {}) {
1583
1684
  const bindOptions = {
@@ -1591,21 +1692,18 @@ function provideExecutors(machine, executors, options = {}) {
1591
1692
  const invokedSrcs = getConfiguredInvokeSrcs(provided);
1592
1693
  for (const [key, logic] of Object.entries(effectiveSources)) {
1593
1694
  if (key === "agent.userInput") continue;
1594
- let binding;
1595
- if (isDecisionLogic(logic)) binding = {
1596
- executorKey: "decide",
1597
- kind: "decision",
1598
- bind: () => bindDecisionForProvide(provided, logic, executors, bindOptions)
1599
- };
1600
- else if (isTextLogic(logic)) {
1601
- const streaming = logic.mode === "stream";
1602
- binding = {
1603
- executorKey: streaming ? "streamText" : "generateText",
1604
- kind: streaming ? "streaming text" : "text",
1605
- bind: () => bindTextForProvide(provided, logic, executors, bindOptions)
1606
- };
1695
+ if (isStateMachineLogic(logic)) {
1696
+ if (invokedSrcs.has(key)) assertChildBindable(logic, executors, key, new Set([provided]));
1697
+ const rebound = bindChildMachineForProvide(logic, executors, bindOptions, new Set([provided]));
1698
+ if (rebound !== logic) wrappedSources[key] = rebound;
1699
+ continue;
1607
1700
  }
1608
- if (!binding) continue;
1701
+ const requirement = executorRequirementOf(logic);
1702
+ if (!requirement) continue;
1703
+ const binding = {
1704
+ ...requirement,
1705
+ bind: () => isDecisionLogic(logic) ? bindDecisionForProvide(provided, logic, executors, bindOptions) : bindTextForProvide(provided, logic, executors, bindOptions)
1706
+ };
1609
1707
  if (executorBoundLogics.has(logic)) continue;
1610
1708
  if (!executors[binding.executorKey]) {
1611
1709
  if (invokedSrcs.has(key)) throw missingExecutorError(key, binding.kind, binding.executorKey);
@@ -1615,12 +1713,50 @@ function provideExecutors(machine, executors, options = {}) {
1615
1713
  }
1616
1714
  return withActors(provided, wrappedSources);
1617
1715
  }
1716
+ /** The executor slot + label an agent logic needs, or `undefined` for a non-agent actor. */
1717
+ function executorRequirementOf(logic) {
1718
+ if (isDecisionLogic(logic)) return {
1719
+ executorKey: "decide",
1720
+ kind: "decision"
1721
+ };
1722
+ if (isTextLogic(logic)) return logic.mode === "stream" ? {
1723
+ executorKey: "streamText",
1724
+ kind: "streaming text"
1725
+ } : {
1726
+ executorKey: "generateText",
1727
+ kind: "text"
1728
+ };
1729
+ }
1730
+ /**
1731
+ * Walks an invoked child machine's own invoked sources (recursively, at any
1732
+ * depth) and throws the same missing-executor error `provideExecutors` throws
1733
+ * for the top-level machine — before any actor starts. Mirrors runAgent's
1734
+ * `assertBindable` for the uncontrolled path. Cycle-safe via `visited`.
1735
+ */
1736
+ function assertChildBindable(childMachine, executors, path, visited) {
1737
+ if (visited.has(childMachine)) return;
1738
+ const nextVisited = new Set([...visited, childMachine]);
1739
+ const sources = childMachine.sources.actors;
1740
+ for (const src of getConfiguredInvokeSrcs(childMachine)) {
1741
+ if (src === "agent.userInput") continue;
1742
+ const logic = sources[src];
1743
+ if (!logic) continue;
1744
+ if (isStateMachineLogic(logic)) {
1745
+ assertChildBindable(logic, executors, `${path} > ${src}`, nextVisited);
1746
+ continue;
1747
+ }
1748
+ const requirement = executorRequirementOf(logic);
1749
+ if (!requirement || executorBoundLogics.has(logic)) continue;
1750
+ if (!executors[requirement.executorKey]) throw missingExecutorError(`${path} > ${src}`, requirement.kind, requirement.executorKey);
1751
+ }
1752
+ }
1618
1753
  function missingExecutorError(src, kind, executor) {
1619
1754
  return /* @__PURE__ */ new Error(`provideExecutors: actor source '${src}' is a ${kind} source but no '${executor}' executor was provided. Add it to the executors object, or bind the source with its own executor (logic.withExecutor(...)) before calling provideExecutors.`);
1620
1755
  }
1621
1756
  //#endregion
1622
1757
  //#region src/verify.ts
1623
1758
  const DECIDE_SRC = "agent.decide";
1759
+ const USER_INPUT_SRC = "agent.userInput";
1624
1760
  function normalizeInvokes(invoke) {
1625
1761
  if (invoke === void 0 || invoke === null) return [];
1626
1762
  return Array.isArray(invoke) ? invoke : [invoke];
@@ -1907,11 +2043,23 @@ const LINT_CHECKS = [
1907
2043
  * call this to catch dead states, undeliverable decisions, un-rebindable
1908
2044
  * invoke srcs, and output-contract gaps before ever running it.
1909
2045
  *
2046
+ * Pass `{ throw: true }` for the one-liner form used in tests and generation
2047
+ * loops: it returns silently when the machine is clean and throws
2048
+ * {@link AgentLintError} (findings on `.diagnostics`) on error-severity
2049
+ * findings, or on warnings too with `{ throw: true, warnings: true }`.
2050
+ *
1910
2051
  * @example
1911
2052
  * ```ts
1912
2053
  * const errors = lintAgentMachine(machine).filter((d) => d.severity === 'error');
1913
2054
  * if (errors.length) throw new Error(errors.map((e) => `${e.path}: ${e.message}`).join('\n'));
1914
2055
  * ```
2056
+ *
2057
+ * @example Throwing form
2058
+ * ```ts
2059
+ * test('agent machine is structurally sound', () => {
2060
+ * lintAgentMachine(machine, { throw: true });
2061
+ * });
2062
+ * ```
1915
2063
  */
1916
2064
  function lintAgentMachine(machine, options = {}) {
1917
2065
  const config = machine.config ?? {};
@@ -1926,10 +2074,16 @@ function lintAgentMachine(machine, options = {}) {
1926
2074
  actors: registered.actors ?? machine.sources?.actors ?? {}
1927
2075
  };
1928
2076
  const disabled = new Set(options.disable ?? []);
1929
- return LINT_CHECKS.flatMap((check) => check(ctx)).filter((d) => !disabled.has(d.code));
2077
+ const diagnostics = LINT_CHECKS.flatMap((check) => check(ctx)).filter((d) => !disabled.has(d.code));
2078
+ if (options.throw) {
2079
+ const failing = options.warnings ? diagnostics : diagnostics.filter((d) => d.severity === "error");
2080
+ if (failing.length > 0) throw new AgentLintError(machine.id ?? "(machine)", failing);
2081
+ }
2082
+ return diagnostics;
1930
2083
  }
1931
2084
  /**
1932
- * Thrown by {@link assertAgentMachine} when lint finds failing diagnostics.
2085
+ * Thrown by `lintAgentMachine(machine, { throw: true })` when lint finds
2086
+ * failing diagnostics.
1933
2087
  * `diagnostics` holds the findings; the message lists them one per finding,
1934
2088
  * so a test runner's failure output reads like the CLI's lint report.
1935
2089
  */
@@ -1942,24 +2096,6 @@ var AgentLintError = class extends AgentError {
1942
2096
  this.diagnostics = diagnostics;
1943
2097
  }
1944
2098
  };
1945
- /**
1946
- * Asserts a machine passes {@link lintAgentMachine}: returns silently when
1947
- * clean, throws {@link AgentLintError} (with the findings on `.diagnostics`)
1948
- * otherwise. Fails on error-severity findings; set `warnings: true` to fail on
1949
- * warnings too. The one-liner for tests and generation loops:
1950
- *
1951
- * @example
1952
- * ```ts
1953
- * test('agent machine is structurally sound', () => {
1954
- * assertAgentMachine(machine);
1955
- * });
1956
- * ```
1957
- */
1958
- function assertAgentMachine(machine, options = {}) {
1959
- const diagnostics = lintAgentMachine(machine, options);
1960
- const failing = options.warnings ? diagnostics : diagnostics.filter((d) => d.severity === "error");
1961
- if (failing.length > 0) throw new AgentLintError(machine.id ?? "(machine)", failing);
1962
- }
1963
2099
  function pendingInvokes(step) {
1964
2100
  const out = [];
1965
2101
  for (const action of step.actions) {
@@ -2005,6 +2141,7 @@ async function simulateAgent(machine, options) {
2005
2141
  decisions: mapValues(options.script.decisions ?? {}, (arr) => [...arr]),
2006
2142
  invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr])
2007
2143
  };
2144
+ if (options.script.userInput?.length) script.invokes[USER_INPUT_SRC] = [...options.script.userInput, ...script.invokes[USER_INPUT_SRC] ?? []];
2008
2145
  let step = initialAgentStep(machine, options.input);
2009
2146
  const trail = [];
2010
2147
  for (let i = 0; i < maxSteps; i++) {
@@ -2071,13 +2208,16 @@ function mapValues(obj, fn) {
2071
2208
  }
2072
2209
  function scriptDryError(kind, src, id, request) {
2073
2210
  const events = request?.kind === "decision" ? ` Candidate events: ${request.events.map((e) => e.type).join(", ") || "(none)"}.` : "";
2074
- return /* @__PURE__ */ new Error(`simulateAgent: script ran dry on a pending ${kind} request for src '${src}' (id '${id}'). Add a '${kind}' entry for '${src}' to the script.${events}`);
2211
+ const key = kind === "text" ? `text['${src}']` : kind === "decision" ? `decisions['${src}']` : src === USER_INPUT_SRC ? "userInput" : `invokes['${src}']`;
2212
+ 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}`);
2075
2213
  }
2076
2214
  const MAX_ADVANCE_STEPS = 1e3;
2077
2215
  async function explore(machine, options, stopWhen) {
2078
2216
  const maxDepth = options.maxDepth ?? 8;
2079
2217
  const maxPaths = options.maxPaths ?? 200;
2080
- const textOutputs = options.textOutputs ?? {};
2218
+ const textScript = options.text ?? {};
2219
+ const invokeOutputs = { ...options.invokes ?? {} };
2220
+ if ("userInput" in options) invokeOutputs[USER_INPUT_SRC] = options.userInput;
2081
2221
  const reachedStates = /* @__PURE__ */ new Set();
2082
2222
  const reachedValues = [];
2083
2223
  const terminals = [];
@@ -2102,22 +2242,22 @@ async function explore(machine, options, stopWhen) {
2102
2242
  if (current.done) return { step: current };
2103
2243
  const request = current.requests[0];
2104
2244
  if (request && request.kind === "text") {
2105
- if (!(request.src in textOutputs)) return {
2245
+ if (!(request.src in textScript)) return {
2106
2246
  step: current,
2107
2247
  blockedSrc: request.src
2108
2248
  };
2109
- current = resolveAgentStep(machine, current, request, textOutputs[request.src]);
2249
+ current = resolveAgentStep(machine, current, request, textScript[request.src]);
2110
2250
  recordState(current.snapshot);
2111
2251
  continue;
2112
2252
  }
2113
2253
  if (request && request.kind === "decision") return { step: current };
2114
2254
  const [invoke] = pendingInvokes(current);
2115
2255
  if (invoke) {
2116
- if (!(invoke.src in textOutputs)) return {
2256
+ if (!(invoke.src in invokeOutputs)) return {
2117
2257
  step: current,
2118
2258
  blockedSrc: invoke.src
2119
2259
  };
2120
- current = resolveAgentStep(machine, current, invoke.id, textOutputs[invoke.src]);
2260
+ current = resolveAgentStep(machine, current, invoke.id, invokeOutputs[invoke.src]);
2121
2261
  recordState(current.snapshot);
2122
2262
  continue;
2123
2263
  }
@@ -2209,9 +2349,10 @@ async function explore(machine, options, stopWhen) {
2209
2349
  * depth, model-free, and reports which states are reached and how each path
2210
2350
  * terminates. At each decision request it forks one branch per candidate event
2211
2351
  * (guard-rejected candidates are counted in `prunedByGuard`, not explored); at
2212
- * an idle wait it forks per externally-accepted event. Text/`userInput` invokes
2213
- * are resolved from `textOutputs` (a by-src canned-output map) — a missing src
2214
- * halts that branch with a `needs-output` terminal rather than throwing.
2352
+ * an idle wait it forks per externally-accepted event. Text requests resolve
2353
+ * from `text`, other invokes from `invokes` (or `userInput` for
2354
+ * `agent.userInput`) all by-src canned-output maps, and a missing src halts
2355
+ * that branch with a `needs-output` terminal rather than throwing.
2215
2356
  *
2216
2357
  * Combinatorics are bounded by `maxDepth` (default 8) and `maxPaths` (default
2217
2358
  * 200, reported via `hitPathCap`).
@@ -2227,13 +2368,14 @@ async function explorePaths(machine, options = {}) {
2227
2368
  }
2228
2369
  /**
2229
2370
  * Answers "can the machine reach `statePath`?" by exploring its branches (a
2230
- * thin wrapper over {@link explorePaths}). Returns `{ canReach: true, witness }`
2231
- * with the event sequence that reaches it, or `{ canReach: false }`.
2371
+ * thin wrapper over {@link explorePaths}). Returns
2372
+ * `{ reachable: true, witness }` with the event sequence that reaches it, or
2373
+ * `{ reachable: false }`.
2232
2374
  *
2233
2375
  * @example
2234
2376
  * ```ts
2235
- * const { canReach, witness } = await canReach(refundMachine, 'denied', { input: { request: 'x', amount: 5000 } });
2236
- * // canReach → true; witness → [{ type: 'NEEDS_REVIEW' }, { type: 'DENY' }]
2377
+ * const { reachable, witness } = await canReach(refundMachine, 'denied', { input: { request: 'x', amount: 5000 } });
2378
+ * // reachable → true; witness → [{ type: 'NEEDS_REVIEW' }, { type: 'DENY' }]
2237
2379
  * ```
2238
2380
  */
2239
2381
  async function canReach(machine, statePath, options = {}) {
@@ -2245,9 +2387,9 @@ async function canReach(machine, statePath, options = {}) {
2245
2387
  }
2246
2388
  });
2247
2389
  return witness !== void 0 ? {
2248
- canReach: true,
2390
+ reachable: true,
2249
2391
  witness
2250
- } : { canReach: false };
2392
+ } : { reachable: false };
2251
2393
  }
2252
2394
  //#endregion
2253
2395
  //#region src/internal/is-record.ts
@@ -2408,7 +2550,8 @@ function matchesTrajectory(actual, expected, options = {}) {
2408
2550
  * Scripted executors — a keyless, deterministic stand-in for a model host.
2409
2551
  *
2410
2552
  * `createScriptedExecutors` builds a full `{ generateText, streamText, decide }`
2411
- * set that plays back canned answers from FIFO queues, so `runAgent` (or
2553
+ * set (plus a `userInput` handler) that plays back canned answers from FIFO
2554
+ * queues, so `runAgent` (or
2412
2555
  * `provideExecutors`, or a bare `TextLogic.execute`) runs with no API key and no
2413
2556
  * network. It is the fastest way to see a machine run, and the least ceremonial
2414
2557
  * way to test one: same machine, same executor contract, scripted answers.
@@ -2484,15 +2627,27 @@ function emitScriptedChunk(result, info) {
2484
2627
  * decisions: [(request) => ({ type: request.events[0]!.type })],
2485
2628
  * });
2486
2629
  * ```
2630
+ *
2631
+ * @example Scripted human input
2632
+ * ```ts
2633
+ * const scripted = createScriptedExecutors({ userInput: ['ship it'] });
2634
+ * await runAgent(machine, { executors: scripted, userInput: scripted.userInput });
2635
+ * ```
2487
2636
  */
2488
2637
  function createScriptedExecutors(script = {}) {
2489
2638
  const decisions = [...script.decisions ?? []];
2490
2639
  const text = [...script.text ?? []];
2640
+ const userInput = [...script.userInput ?? []];
2491
2641
  const nextText = async (request, info) => {
2492
2642
  if (text.length === 0) throw new AgentError("scripted-executors-exhausted", `createScriptedExecutors: script ran dry on a pending text request ${describeText(request)}. Add another entry to the script's \`text\` queue.`);
2493
2643
  return resolveScriptedTextEntry(text.shift(), request, info);
2494
2644
  };
2495
2645
  return {
2646
+ userInput: async (input) => {
2647
+ if (userInput.length === 0) throw new AgentError("scripted-executors-exhausted", `createScriptedExecutors: script ran dry on a pending userInput request (prompt: ${input.prompt ? `'${input.prompt}'` : "(none)"}). Add another entry to the script's \`userInput\` queue.`);
2648
+ const entry = userInput.shift();
2649
+ return typeof entry === "function" ? await entry(input) : entry;
2650
+ },
2496
2651
  generateText: nextText,
2497
2652
  streamText: async (request, info) => {
2498
2653
  const result = await nextText(request, info);
@@ -2566,22 +2721,22 @@ async function runSeam(machine, options) {
2566
2721
  const queueKeyOf = (request) => request.name !== void 0 && queues.has(request.name) ? request.name : request.model;
2567
2722
  /**
2568
2723
  * Consumes this request's slot in the call plan, or resolves `undefined` when
2569
- * its queue is dry. The LAST entry repeats: a live seam that branches further
2570
- * still finds an answer instead of running dry.
2724
+ * its queue is dry. With `repeatLast`, the last entry is replayed instead of
2725
+ * running dry.
2571
2726
  */
2572
2727
  const takeScriptedSlot = async (request, info) => {
2573
2728
  const queue = queues.get(queueKeyOf(request));
2574
2729
  if (!queue?.length) return;
2575
- return resolveScriptedTextEntry(queue.length === 1 ? queue[0] : queue.shift(), request, info);
2730
+ return resolveScriptedTextEntry(options.repeatLast && queue.length === 1 ? queue[0] : queue.shift(), request, info);
2576
2731
  };
2577
2732
  const scriptedAnswer = async (request, info) => {
2578
2733
  const scripted = await takeScriptedSlot(request, info);
2579
- if (!scripted) throw new AgentError("seam-script-exhausted", `runSeam: no scripted answer left for request ${describeText(request)}. Add an entry to \`scripts.${queueKeyOf(request)}\` its last entry repeats, so one extra answer covers a longer branch.`);
2734
+ if (!scripted) throw new 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.`);
2580
2735
  return scripted;
2581
2736
  };
2582
2737
  const route = async (request, info) => {
2583
2738
  const callIndex = calls++;
2584
- const isSeam = (seam.request !== void 0 ? request.name === seam.request : request.model === seam.model) && seamMatches++ === (seam.occurrence ?? 0);
2739
+ const isSeam = request.name === seam.request && seamMatches++ === (seam.occurrence ?? 0);
2585
2740
  if (isSeam && candidate) {
2586
2741
  await takeScriptedSlot(request, info);
2587
2742
  seamReached = true;
@@ -2619,7 +2774,7 @@ async function runSeam(machine, options) {
2619
2774
  result = await runAgent(machine, {
2620
2775
  ...snapshot ? { snapshot } : { input: options.input },
2621
2776
  ...event ? { event } : {},
2622
- ...options.isSuspended ? { isSuspended: options.isSuspended } : {},
2777
+ ...options.isIdle ? { isIdle: options.isIdle } : {},
2623
2778
  ...options.actors ? { actors: options.actors } : {},
2624
2779
  events,
2625
2780
  executors,
@@ -2672,4 +2827,4 @@ async function runSeam(machine, options) {
2672
2827
  };
2673
2828
  }
2674
2829
  //#endregion
2675
- export { AGENT_EVENT_SCHEMA_VERSION, AGENT_INIT_EVENT_TYPE, AGENT_TRACE_SCHEMA_VERSION, AGENT_USAGE_EVENT_TYPE, AgentDecisionExhaustedError, AgentError, AgentEventLogConflictError, AgentIdleError, AgentIllegalResumeEventError, AgentLintError, AgentReplayDivergenceError, AgentReplayMachineMismatchError, AgentSnapshotVersionMismatchError, NonSerializableAgentEventError, appendMessages, assertAgentLogEntry, assertAgentMachine, assertEventLogStoreConformance, assertJsonSerializable, assistantMessage, bindRequestExecutor, buildEnvelopeSchema, canReach, createAgentActor, createAgentRun, createAgentSchemas, createInMemoryEventLogStore, createReplayEntry, createScriptedExecutors, createTextLogic, diffEventLogs, executeAgentRequest, explorePaths, generateResult, getAcceptedEvents, getAgentEffects, getAgentMessages, getAgentOutputMode, getAgentSchemas, getCallUsage, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, getStateMeta, initEntry, inspectTransitions, isStandardSchema, lintAgentMachine, matchesTrajectory, messagesSchema, parseAgentEvent, parseModelRef, parseOutput, parseStructuredEnvelope, persistSnapshot, provideExecutors, renderDecisionAttempts, replay, resolveDecision, runAgent, runSeam, serializeTraceEvent, setupAgent, simulateAgent, systemMessage, toolMessage, traceTransitions, userMessage, verifyReplay };
2830
+ export { AGENT_EVENT_SCHEMA_VERSION, AGENT_INIT_EVENT_TYPE, AGENT_TRACE_SCHEMA_VERSION, AGENT_USAGE_EVENT_TYPE, AgentDecisionExhaustedError, AgentError, AgentEventLogConflictError, AgentIdleError, AgentIllegalResumeEventError, AgentLintError, AgentMaxModelCallsExceededError, AgentReplayDivergenceError, AgentReplayMachineMismatchError, AgentSnapshotVersionMismatchError, NonSerializableAgentEventError, appendMessages, assertAgentLogEntry, assertEventLogStoreConformance, assertJsonSerializable, assistantMessage, bindRequestExecutor, buildEnvelopeSchema, canReach, createAgentActor, createAgentRun, createAgentSchemas, createInMemoryEventLogStore, createReplayEntry, createScriptedExecutors, createTextLogic, diffEventLogs, executeAgentRequest, explorePaths, generateResult, getAcceptedEvents, getAgentEffects, getAgentMessages, getAgentOutputMode, getAgentSchemas, getCallUsage, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, getSnapshotNodes, getSnapshotRequests, getStateMeta, initEntry, inspectTransitions, isStandardSchema, lintAgentMachine, matchesTrajectory, messagesSchema, parseAgentEvent, parseModelRef, parseOutput, parseStructuredEnvelope, provideExecutors, renderDecisionAttempts, replay, resolveDecision, runAgent, runSeam, serializeTraceEvent, setupAgent, simulateAgent, systemMessage, toolMessage, traceTransitions, userMessage };