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

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 (54) 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 +276 -27
  38. package/dist/testing.d.cts +17 -15
  39. package/dist/testing.d.mts +17 -15
  40. package/dist/testing.mjs +260 -12
  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-C2DPqomn.d.mts → utils-_q7F-AZu.d.mts} +2 -2
  48. package/dist/{utils-ceStNCOh.d.cts → utils-o4YRdHJX.d.cts} +2 -2
  49. package/dist/validate.cjs +0 -8
  50. package/dist/validate.mjs +0 -8
  51. package/package.json +3 -3
  52. package/readme.md +26 -18
  53. package/schemas/agent-workflow.json +0 -8
  54. package/skills/generate-machine/SKILL.md +6 -8
@@ -1,283 +1,12 @@
1
- import { C as isUnboundPlaceholder, H as AgentError, I as isStandardSchema, L as resolveMachineVersion, S as getRegisteredAgentExecutionOptions, V as validateSchemaSync, c as executeAgentTextRequest, d as getCallUsage, f as isTextLogic, k as findNonSerializableContextPaths, p as normalizeGeneratorResult, u as getCallFinishReason, v as responseMessagesOf, x as getMachineIdlePredicate, y as executorBoundLogics } from "./text-logic-Bl_ZRxDi.mjs";
2
- import { a as isDecisionLogic, l as getAcceptedEvents, s as resolveDecision, t as AgentDecisionExhaustedError } from "./decision-DzSv1NpD.mjs";
1
+ import { E as findNonSerializableContextPaths, N as isStandardSchema, P as resolveMachineVersion, R as validateSchemaSync, _ as responseMessagesOf, b as getRegisteredAgentExecutionOptions, d as isTextLogic, f as normalizeGeneratorResult, l as getCallFinishReason, u as getCallUsage, v as executorBoundLogics, x as isUnboundPlaceholder, z as AgentError } from "./text-logic-CIqHAmBi.mjs";
2
+ import { a as isDecisionLogic, l as getAcceptedEvents, s as resolveDecision, t as AgentDecisionExhaustedError } from "./decision-RQ0iv0oZ.mjs";
3
3
  import { t as AGENT_USAGE_EVENT_TYPE } from "./usage-BdEjFuIi.mjs";
4
- import { _ as getUsageFromEvents, b as replay, f as canonicalEventJson, g as getSnapshotStateHash, h as getLogExecutionId, l as agentCallOccurrence, o as AgentMachineVersionMismatchError, p as createReplayEntry, t as AgentEventLogConflictError, v as initEntry, x as validateReplayEntries } from "./event-log-store-Ba2fsc4E.mjs";
5
- import { createActor, createAsyncLogic, getNextTransitions, initialTransition, isMachineSnapshot, transition } from "xstate";
6
- //#region src/steps.ts
7
- /**
8
- * The step API: the pure `(state, event) => (nextState, requests)` view of an
9
- * agent machine. An {@link AgentStep} is a snapshot plus the model requests
10
- * the machine is now waiting on; nothing here executes anything. A host
11
- * resolves each request however it likes ({@link executeAgentRequest},
12
- * `resolveDecision`, a queue, a durable workflow, a replayed log) and feeds
13
- * the result back with {@link resolveAgentStep} or {@link rejectAgentStep}.
14
- * `runAgent` is the in-process host built for the common case.
15
- * @module
16
- */
17
- /** @internal Normalizes current and legacy XState invoke effect shapes. */
18
- function getInvokeEffectMetadata(action) {
19
- if (action.type === "@xstate.spawn") return action;
20
- if (action.type === "xstate.spawnChild") {
21
- const params = action.params;
22
- return params ? {
23
- ...params,
24
- logic: action.logic
25
- } : void 0;
26
- }
27
- if (action.type === "@xstate.start" && typeof action.src === "string") return action;
28
- }
29
- /**
30
- * The invokes a snapshot is still waiting on, lowered to the same spawn-effect
31
- * shape {@link getAgentRequests} reads. Derived from the snapshot's live
32
- * children rather than the last transition's actions, so a state that invokes
33
- * several actors at once (an `invoke: [...]` array, or parallel regions each
34
- * invoking) keeps reporting the siblings after the first one settles —
35
- * including when the settled invoke's `onDone` is targetless and starts
36
- * nothing new.
37
- *
38
- * Entries are ordered by invoke id, so a walker that resolves one invoke per
39
- * step is deterministic.
40
- *
41
- * @internal
42
- */
43
- function getPendingInvokes(snapshot) {
44
- const children = snapshot.children ?? {};
45
- return Object.keys(children).sort().flatMap((id) => {
46
- const child = children[id];
47
- const childSnapshot = child?.getSnapshot?.();
48
- if (!child || childSnapshot?.status !== "active") return [];
49
- return [{
50
- type: "@xstate.spawn",
51
- id,
52
- src: child.src,
53
- input: childSnapshot.input,
54
- logic: child.logic
55
- }];
56
- });
57
- }
58
- /**
59
- * Scans a set of executable actions (as returned by xstate's `transition`/
60
- * `initialTransition`) for spawned `TextLogic`/`DecisionLogic` invokes and
61
- * lowers each into an {@link AgentStepRequest}. Pass `options.machine` to
62
- * pre-fill `schemas`/`actors` from the machine's registered `setupAgent`
63
- * options instead of passing them by hand.
64
- * `options.snapshot` is required to resolve a decision's candidate events
65
- * (intersecting declared `allowedEvents` with what's currently legal) — omit
66
- * it and decision requests report an empty `events` list.
67
- *
68
- * @internal
69
- */
70
- function getAgentRequests(actions, { machine, ...options } = {}) {
71
- if (machine) options = {
72
- ...getRegisteredAgentExecutionOptions(machine),
73
- ...options
74
- };
75
- return actions.flatMap((action) => {
76
- const params = getInvokeEffectMetadata(action);
77
- if (!params || typeof params.src !== "string") return [];
78
- if (typeof params.id !== "string" || params.id.length === 0) throw new Error(`Agent invoke '${params.src}' must define a durable string id.`);
79
- const registeredLogic = isTextLogic(params.logic) || isDecisionLogic(params.logic) ? params.logic : options.actors?.[params.src];
80
- if (isDecisionLogic(registeredLogic)) {
81
- const decisionRequest = registeredLogic.request(params.input);
82
- const allowedEventTypes = registeredLogic.allowedEventTypes?.(params.input);
83
- const events = options.snapshot ? getAcceptedEvents(options.snapshot, {
84
- events: options.events,
85
- schemas: options.schemas,
86
- eventTypes: allowedEventTypes
87
- }) : [];
88
- return [{
89
- ...decisionRequest,
90
- id: params.id,
91
- events
92
- }];
93
- }
94
- const textLogic = isTextLogic(registeredLogic) ? registeredLogic : void 0;
95
- const input = textLogic ? textLogic.request(params.input) : void 0;
96
- if (!input) return [];
97
- return [{
98
- kind: "text",
99
- id: params.id,
100
- src: params.src,
101
- ...textLogic ? { mode: textLogic.mode } : {},
102
- input,
103
- tools: input.tools ?? {},
104
- events: []
105
- }];
106
- });
107
- }
108
- /**
109
- * Builds the canonical `xstate.done.actor` event xstate's `transition()`
110
- * expects to resolve a spawned invoke — including the child session identity
111
- * that rejects a stale completion from an earlier incarnation.
112
- *
113
- * @internal
114
- */
115
- function doneEvent(snapshot, request, output) {
116
- const id = typeof request === "string" ? request : request.id;
117
- const child = snapshot.children[id];
118
- return {
119
- type: "xstate.done.actor",
120
- actorId: id,
121
- ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
122
- output
123
- };
124
- }
125
- /**
126
- * The `xstate.error.actor` counterpart of {@link doneEvent} — the event a
127
- * failed invoke delivers, which `onError` transitions observe. Carries the
128
- * same child session identity, so xstate does not discard it as stale.
129
- *
130
- * @internal
131
- */
132
- function errorEvent(snapshot, request, error) {
133
- const id = typeof request === "string" ? request : request.id;
134
- const child = snapshot.children[id];
135
- return {
136
- type: "xstate.error.actor",
137
- actorId: id,
138
- ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
139
- error
140
- };
141
- }
142
- /**
143
- * Applies a request's `output` as a done event (or, with `outcome: 'error'`,
144
- * its `output` as the invoke's error) via `transition(...)`, returning the raw
145
- * `[snapshot, actions]` tuple. Lower-level than {@link resolveAgentStep} /
146
- * {@link rejectAgentStep} — those helpers wrap this and also run
147
- * {@link getAgentRequests} to produce the next {@link AgentStep}.
148
- *
149
- * @internal
150
- */
151
- function transitionResult(logic, snapshot, request, output, outcome = "output") {
152
- const event = outcome === "error" ? errorEvent(snapshot, request, output) : doneEvent(snapshot, request, output);
153
- const result = transition(logic, snapshot, event);
154
- applyFinalStateOutput(logic, result[0], event);
155
- return result;
156
- }
157
- /**
158
- * Starts a machine and returns its first {@link AgentStep} — the step-path
159
- * equivalent of `initialTransition` plus request discovery. Begins the
160
- * durable/per-model-call-checkpoint loop: resolve each `step.requests` entry
161
- * (via {@link executeAgentRequest} for `kind: 'text'`, or
162
- * {@link resolveDecision} for `kind: 'decision'`), then advance with
163
- * {@link resolveAgentStep} or {@link transitionAgentStep}.
164
- */
165
- function initialAgentStep(machine, input, options) {
166
- const [snapshot, actions] = initialTransition(machine, input);
167
- return createAgentStep(machine, snapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
168
- }
169
- /**
170
- * Applies an externally-sent event (e.g. a decision's chosen event, or a
171
- * human's reply) and returns the next {@link AgentStep}. Accepts **either**
172
- * a raw snapshot **or** a prior `AgentStep` as the second argument —
173
- * `.snapshot` is unwrapped automatically, so callers can thread the whole
174
- * step object through without manually plucking the snapshot out.
175
- */
176
- function transitionAgentStep(machine, snapshotOrStep, event, options) {
177
- const [nextSnapshot, actions] = transition(machine, isAgentStep(snapshotOrStep) ? snapshotOrStep.snapshot : snapshotOrStep, event);
178
- return createAgentStep(machine, nextSnapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
179
- }
180
- /**
181
- * Applies a resolved text request's output (a `kind: 'text'`
182
- * {@link AgentRequest} — not a decision) as a done event and returns the
183
- * next {@link AgentStep}. For decisions, resolve with `resolveDecision`
184
- * (which returns a {@link ChosenEvent}) and apply it with
185
- * {@link transitionAgentStep} instead — a decision has no output value of
186
- * its own to feed here.
187
- */
188
- function resolveAgentStep(machine, step, request, output, options) {
189
- const [snapshot, actions] = transitionResult(machine, step.snapshot, request, output);
190
- return createAgentStep(machine, snapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
191
- }
192
- /**
193
- * Rejects a pending invoke — the failure counterpart of
194
- * {@link resolveAgentStep}. Delivers `error` as the invoke's
195
- * `xstate.error.actor` event, so the machine takes that invoke's `onError`
196
- * transition and returns the next {@link AgentStep}. With no `onError` in
197
- * scope the returned step's snapshot has `status: 'error'`, exactly as a live
198
- * run's failed invoke does.
199
- *
200
- * A durable host replays a failed call with this: the log says the call
201
- * failed, so the replay rejects the invoke instead of resolving it. Scripted
202
- * playthroughs use it too — see `simulateAgent`'s `script.errors`.
203
- *
204
- * `error` can be any value. An `Error` instance is the usual one, but a plain
205
- * object (`{ code: 'truncated' }`) reaches the `onError` transition as
206
- * `event.error` untouched, so a machine that branches on a failure code is
207
- * testable without constructing a provider's error class.
208
- */
209
- function rejectAgentStep(machine, step, request, error, options) {
210
- const [snapshot, actions] = transitionResult(machine, step.snapshot, request, error, "error");
211
- return createAgentStep(machine, snapshot, actions, getRegisteredAgentExecutionOptions(machine, options));
212
- }
213
- /**
214
- * Resolves one **text** request ({@link AgentRequest}, from
215
- * {@link AgentStep.requests}) against a host's {@link AgentRequestExecutors}
216
- * — merges the request's tools, dispatches to `generateText`/`streamText`
217
- * per `mode`, and validates the result against the request's
218
- * `outputSchema` if present. **Text-only**: passing a `kind: 'decision'`
219
- * request throws, directing the caller to `resolveDecision(request,
220
- * executors, ...)` instead. Returns the invoke's output — `{ result,
221
- * messages }`, ready for {@link resolveAgentStep} — plus the `raw` executor
222
- * result (tool calls, usage, finish reason).
223
- */
224
- async function executeAgentRequest(request, executors) {
225
- if (request.kind === "decision") throw new Error("executeAgentRequest(...) is text-only. Resolve a 'decision' request with resolveDecision(request, executors, ...) instead.");
226
- assertTextExecutor(request, executors);
227
- const { result, raw } = await executeAgentTextRequest(request.mode ?? "generate", request.id, request.input, executors, request.tools);
228
- return {
229
- result: request.input.outputSchema ? validateSchemaSync(request.input.outputSchema, result) : result,
230
- messages: responseMessagesOf(raw),
231
- raw
232
- };
233
- }
234
- function assertTextExecutor(request, executors) {
235
- const mode = request.mode ?? "generate";
236
- const kind = mode === "stream" ? "streamText" : "generateText";
237
- if (!(mode === "stream" ? executors.streamText : executors.generateText)) throw new Error(`this step's text request '${request.src}' needs a '${kind}' executor but none was provided.`);
238
- }
239
- function createAgentStep(machine, snapshot, actions, options) {
240
- applyFinalStateOutput(machine, snapshot);
241
- return {
242
- snapshot,
243
- actions,
244
- requests: getAgentRequests(getPendingInvokes(snapshot), {
245
- ...options,
246
- snapshot
247
- }),
248
- done: snapshot.status === "done"
249
- };
250
- }
251
- function resolveStateValueConfig(config, value) {
252
- if (typeof value === "string") return config.states?.[value];
253
- if (!value || typeof value !== "object") return;
254
- for (const [key, childValue] of Object.entries(value)) {
255
- const childConfig = config.states?.[key];
256
- if (!childConfig) continue;
257
- if (childConfig.type === "final") return childConfig;
258
- const nested = resolveStateValueConfig(childConfig, childValue);
259
- if (nested) return nested;
260
- }
261
- }
262
- function applyFinalStateOutput(logic, snapshot, event) {
263
- const machineSnapshot = snapshot;
264
- if (machineSnapshot.status !== "done" || machineSnapshot.output !== void 0 || !("config" in logic)) return;
265
- const config = logic.config;
266
- if (!config) return;
267
- const output = resolveStateValueConfig(config, machineSnapshot.value)?.output;
268
- if (output === void 0) return;
269
- machineSnapshot.output = typeof output === "function" ? output({
270
- context: machineSnapshot.context,
271
- event
272
- }) : output;
273
- }
274
- function isAgentStep(value) {
275
- return !!value && typeof value === "object" && "snapshot" in value && "actions" in value && "requests" in value;
276
- }
277
- //#endregion
4
+ import { _ as getUsageFromEvents, b as replay, f as canonicalEventJson, g as getSnapshotStateHash, h as getLogExecutionId, l as agentCallOccurrence, o as AgentMachineVersionMismatchError, p as createReplayEntry, t as AgentEventLogConflictError, v as initEntry, x as validateReplayEntries } from "./event-log-store-2pAFiWuc.mjs";
5
+ import { createAsyncLogic, getNextTransitions, isMachineSnapshot, stopActor } from "xstate";
6
+ import { createDurable } from "xstate/durable";
278
7
  //#region src/run-agent.ts
279
8
  /**
280
- * Thrown by {@link runAgent} when a resume is given BOTH an `events` log and a
9
+ * Thrown by {@link AgentRuntime.start} when a resume is given BOTH an `events` log and a
281
10
  * `snapshot` that claims a position in that log (`agentMeta.logIndex`), and the
282
11
  * two disagree about the state at that position. The log is the source of
283
12
  * truth, so this is a host bug (a snapshot cached from a different lineage, or
@@ -286,7 +15,7 @@ function isAgentStep(value) {
286
15
  */
287
16
  var AgentSnapshotDivergedError = class extends AgentError {
288
17
  constructor(expected, actual, logIndex) {
289
- super("snapshot-diverged", `runAgent: the resume snapshot disagrees with the event log at index ${logIndex}: the log replays to state hash '${expected}', the snapshot hashes '${actual}'.`);
18
+ super("snapshot-diverged", `createAgentRuntime: the resume snapshot disagrees with the event log at index ${logIndex}: the log replays to state hash '${expected}', the snapshot hashes '${actual}'.`);
290
19
  this.expected = expected;
291
20
  this.actual = actual;
292
21
  this.logIndex = logIndex;
@@ -309,7 +38,7 @@ function readAgentMeta(snapshot) {
309
38
  * The version of the {@link AgentTraceEvent} envelope every trace event carries
310
39
  * as `schemaVersion`. Bumped only on a breaking change to the envelope or any
311
40
  * payload shape, so a consumer can gate on it. Emitted identically by
312
- * {@link runAgent}, {@link provideExecutors}' `onTrace`, and
41
+ * {@link createAgentRuntime}, {@link provideExecutors}' `onTrace`, and
313
42
  * {@link traceTransitions}.
314
43
  */
315
44
  const AGENT_TRACE_SCHEMA_VERSION = 1;
@@ -403,7 +132,7 @@ function serializeTraceEvent(event, options = {}) {
403
132
  let nextRunAgentTraceId = 1;
404
133
  /**
405
134
  * Thrown into the invoke that would have made the call once
406
- * {@link RunAgentOptions.maxModelCalls} is spent. It reaches the machine
135
+ * {@link AgentRuntimeOptions.maxModelCalls} is spent. It reaches the machine
407
136
  * through the normal error channel, so an invoke's `onError` can branch on it
408
137
  * (`error.code === 'max-model-calls'`, the same string the settled result's
409
138
  * `cause` uses) and route to a degraded/finish state instead of failing the
@@ -420,7 +149,7 @@ var AgentMaxModelCallsExceededError = class extends AgentError {
420
149
  /** The budget that was exceeded (`options.maxModelCalls`). */
421
150
  maxModelCalls;
422
151
  constructor(maxModelCalls) {
423
- super("max-model-calls", `runAgent exceeded maxModelCalls (${maxModelCalls}). Raise the budget, or handle it in the invoke's onError (error.code === 'max-model-calls').`);
152
+ super("max-model-calls", `The run exceeded maxModelCalls (${maxModelCalls}). Raise the budget, or handle it in the invoke's onError (error.code === 'max-model-calls').`);
424
153
  this.name = "AgentMaxModelCallsExceededError";
425
154
  this.maxModelCalls = maxModelCalls;
426
155
  }
@@ -467,12 +196,12 @@ function isStateMachineLogic(logic) {
467
196
  }
468
197
  /**
469
198
  * Fails fast (throws) at bind time — before any actor runs — when the
470
- * machine invokes an agent actor `runAgent` cannot execute. See §3.2 point 2.
199
+ * machine invokes an agent actor the runtime cannot execute. See §3.2 point 2.
471
200
  *
472
201
  * Recurses into invoked child state machines (arbitrarily deep). A child
473
202
  * machine's agent requests reached through string-keyed invoke srcs DO inherit
474
- * the parent runAgent's `generateText`/`streamText`/`decide` executors —
475
- * runAgent rebinds them with the same host-backed wrappers (see
203
+ * the parent run's `generateText`/`streamText`/`decide` executors —
204
+ * the runtime rebinds them with the same host-backed wrappers (see
476
205
  * {@link rebindChildMachine}) — so the only remaining bind-time errors are: a
477
206
  * required executor kind missing entirely (naming the invoke chain and src),
478
207
  * and an unbound request reached through a direct-object invoke src that can't
@@ -498,11 +227,11 @@ function assertMachineBindable(machine, effectiveSources, executors, ctx) {
498
227
  assertChildMachineBindable(src, src, stateName, executors, ctx);
499
228
  continue;
500
229
  }
501
- if ((isTextLogic(src) || isDecisionLogic(src)) && !executorBoundLogics.has(src)) throw new Error(`runAgent: ${where} '${stateName}' invokes a direct-object actor logic (kind: '${src.kind}'). Direct-object invoke srcs cannot be rebound by runAgent — either call '.withExecutor(...)' on the logic before invoking it, or register it as a string-keyed actor source instead (machine.provide({ actors: { name: logic } })) and invoke it by name.`);
230
+ if ((isTextLogic(src) || isDecisionLogic(src)) && !executorBoundLogics.has(src)) throw new Error(`createAgentRuntime: ${where} '${stateName}' invokes a direct-object actor logic (kind: '${src.kind}'). Direct-object invoke srcs cannot be rebound by the agent runtime — either call '.withExecutor(...)' on the logic before invoking it, or register it as a string-keyed actor source instead (machine.provide({ actors: { name: logic } })) and invoke it by name.`);
502
231
  continue;
503
232
  }
504
233
  const logic = effectiveSources[src];
505
- 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}': ... } }).`);
234
+ if (logic === void 0) throw new Error(`createAgentRuntime: ${where} '${stateName}' invokes unregistered actor source '${src}'. Provide it via machine.provide({ actors: { '${src}': ... } }) or createAgentRuntime(machine, { actors: { '${src}': ... } }).`);
506
235
  if (isStateMachineLogic(logic)) {
507
236
  assertChildMachineBindable(logic, src, stateName, executors, ctx);
508
237
  continue;
@@ -510,17 +239,17 @@ function assertMachineBindable(machine, effectiveSources, executors, ctx) {
510
239
  if (isDecisionLogic(logic)) {
511
240
  if (executorBoundLogics.has(logic)) continue;
512
241
  if (!ctx.rebindable) throw unrebindableChildRequestError(ctx.childPath, stateName, src, "decision");
513
- if (!executors.decide) throw new Error(`runAgent: ${where} '${stateName}' invokes decision source '${src}' but no 'decide' executor was provided to runAgent(...).`);
242
+ if (!executors.decide) throw new Error(`createAgentRuntime: ${where} '${stateName}' invokes decision source '${src}' but no 'decide' executor was provided to createAgentRuntime(...).`);
514
243
  continue;
515
244
  }
516
245
  if (isTextLogic(logic)) {
517
246
  if (executorBoundLogics.has(logic)) continue;
518
247
  if (!ctx.rebindable) throw unrebindableChildRequestError(ctx.childPath, stateName, src, logic.mode === "stream" ? "streaming text" : "text");
519
- if (logic.mode === "stream" && !executors.streamText) throw new Error(`runAgent: ${where} '${stateName}' invokes streaming text source '${src}' but no 'streamText' executor was provided to runAgent(...).`);
520
- if (logic.mode !== "stream" && !executors.generateText) throw new Error(`runAgent: ${where} '${stateName}' invokes text source '${src}' but no 'generateText' executor was provided to runAgent(...).`);
248
+ if (logic.mode === "stream" && !executors.streamText) throw new Error(`createAgentRuntime: ${where} '${stateName}' invokes streaming text source '${src}' but no 'streamText' executor was provided to createAgentRuntime(...).`);
249
+ if (logic.mode !== "stream" && !executors.generateText) throw new Error(`createAgentRuntime: ${where} '${stateName}' invokes text source '${src}' but no 'generateText' executor was provided to createAgentRuntime(...).`);
521
250
  continue;
522
251
  }
523
- if (isUnboundPlaceholder(logic)) throw new Error(`runAgent: ${where} '${stateName}' invokes actor source '${src}', which has no host execution. Provide it via machine.provide({ actors: { '${src}': ... } }) or runAgent(machine, { actors: { '${src}': ... } }).`);
252
+ if (isUnboundPlaceholder(logic)) throw new Error(`createAgentRuntime: ${where} '${stateName}' invokes actor source '${src}', which has no host execution. Provide it via machine.provide({ actors: { '${src}': ... } }) or createAgentRuntime(machine, { actors: { '${src}': ... } }).`);
524
253
  }
525
254
  }
526
255
  /** Descends the bind-time walk into an invoked child state machine, guarding
@@ -538,14 +267,14 @@ function assertChildMachineBindable(childMachine, childSrc, stateName, executors
538
267
  });
539
268
  }
540
269
  /** The loud bind-time error for an unbound agent request reached under a
541
- * direct-object invoke src, which runAgent cannot rebind (only string-keyed
270
+ * direct-object invoke src, which the runtime cannot rebind (only string-keyed
542
271
  * sources can be swapped via `.provide`). Names the invoke chain AND the
543
272
  * request src, and spells out the `.withExecutor`/string-keyed remedy. Note:
544
273
  * requests reachable through string-keyed srcs at any depth DO inherit
545
- * runAgent's executors — this error is only for the unrebindable direct-object
274
+ * the runtime's executors — this error is only for the unrebindable direct-object
546
275
  * case. */
547
276
  function unrebindableChildRequestError(childPath, stateName, requestSrc, kind) {
548
- 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.`);
277
+ return /* @__PURE__ */ new Error(`createAgentRuntime: child machine '${childPath}' (state '${stateName}') invokes ${kind} source '${requestSrc}', which has no host execution and is reached through a direct-object invoke src that the agent runtime cannot rebind. Requests reached through string-keyed actor sources inherit the agent runtime'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.`);
549
278
  }
550
279
  /**
551
280
  * True when the snapshot's active states declare a transition that would
@@ -562,8 +291,8 @@ function declaresUsageTransition(snapshot) {
562
291
  /**
563
292
  * Builds the ONE place a trace payload is emitted: it hands the payload to the
564
293
  * envelope-stamping trace sink and, from that same payload, invokes the sugar
565
- * callbacks that are projections of it — {@link RunAgentOptions.onChunk},
566
- * {@link RunAgentOptions.onResult}, {@link RunAgentOptions.onTransition}. Each
294
+ * callbacks that are projections of it — {@link AgentRuntimeOptions.onChunk},
295
+ * {@link AgentRuntimeOptions.onResult}, {@link AgentRuntimeOptions.onTransition}. Each
567
296
  * keeps its historical position relative to the trace: `onResult` fires just
568
297
  * BEFORE its `request.end`, `onChunk`/`onTransition` just AFTER their
569
298
  * `stream.chunk`/`machine.transition`. Sugar dispatch never depends on whether
@@ -610,13 +339,15 @@ function selfIdAndSrc(self) {
610
339
  * root actor as a fallback.
611
340
  */
612
341
  function invokingActorOf(self, runCtx) {
613
- return self?._parent ?? runCtx.actorHolder.actorRef;
342
+ const parent = self?._parent;
343
+ if (parent !== void 0 && parent._parent !== void 0) return parent;
344
+ return runCtx.actorHolder.actorRef ?? parent;
614
345
  }
615
346
  /**
616
347
  * The shared text/stream emission helper: binds a {@link TextLogic} to
617
348
  * `runCtx`'s executor and constructs the `request.start` / `stream.chunk` /
618
349
  * `request.end` (incl. the lifted `reasoning`) / `request.error` trace payloads.
619
- * Used by both `runAgent` and `provideExecutors` so the two paths produce
350
+ * Used by both `createAgentRuntime` and `provideExecutors` so the two paths produce
620
351
  * identical event shapes by construction. @internal
621
352
  */
622
353
  function bindTextLogic(logic, runCtx) {
@@ -750,11 +481,11 @@ function createCountingDecide(runCtx, self) {
750
481
  };
751
482
  }
752
483
  /**
753
- * Builds the decision actor logic runAgent installs in place of a
484
+ * Builds the decision actor logic the runtime installs in place of a
754
485
  * `DecisionLogic`/`agent.decide` source. `DecisionLogic.withExecutor(...)`
755
486
  * can only swap the innermost per-attempt executor — the `resolveDecision(...)`
756
487
  * call (and its `canTake`) is hardwired inside the original logic's `run`.
757
- * To supply `canTake` (mode-3, §2.6), runAgent instead builds a fresh async
488
+ * To supply `canTake` (mode-3, §2.6), the runtime instead builds a fresh async
758
489
  * logic here that calls `resolveDecision` itself, reusing `logic.request(...)`
759
490
  * to build the request the same way the original logic would have.
760
491
  *
@@ -822,7 +553,7 @@ function rootActorOf(self) {
822
553
  while (ref?._parent) ref = ref._parent;
823
554
  return ref;
824
555
  }
825
- /** The per-root envelope state, minted on first use (runId `run_<n>`, matching runAgent). */
556
+ /** The per-root envelope state, minted on first use (runId `run_<n>`, matching the runtime). */
826
557
  function rootTraceState(root) {
827
558
  let state = rootTraceRegistry.get(root);
828
559
  if (!state) {
@@ -862,7 +593,7 @@ function provideTraceSink(onTrace) {
862
593
  }
863
594
  /**
864
595
  * A minimal {@link RunAgentBindContext} for `provideExecutors` (uncontrolled
865
- * `createActor`): the same wrappers runAgent installs, MINUS the run-scoped
596
+ * `createActor`): the same wrappers the runtime installs, MINUS the run-scoped
866
597
  * model-call counter. `consumeModelCall` is a no-op (no budget), and
867
598
  * `actorHolder.actorRef` is left undefined — the wrappers read the invoking
868
599
  * actor off `self._parent`, always present under a live `createActor` tree.
@@ -871,7 +602,7 @@ function provideTraceSink(onTrace) {
871
602
  *
872
603
  * `recordUsage` has no run-level aggregate to fold into here (there is no
873
604
  * run), so it does one thing: deliver the reserved `@agent.usage` event, gated
874
- * exactly like runAgent's — see {@link deliverUsageEvent}.
605
+ * exactly like the runtime's — see {@link deliverUsageEvent}.
875
606
  */
876
607
  function provideBindContext(machine, executors, options) {
877
608
  const traceSink = provideTraceSink(options.onTrace);
@@ -899,7 +630,7 @@ function provideBindContext(machine, executors, options) {
899
630
  /**
900
631
  * The single reserved-`@agent.usage` DELIVERY seam, shared by both bind paths:
901
632
  * after a bound call settles with reported usage, send the prebuilt `event` to
902
- * the machine actor `resolveActorRef` names — the run's root actor on the `runAgent` path,
633
+ * the machine actor `resolveActorRef` names — the run's root actor on the `createAgentRuntime` path,
903
634
  * the settling request actor's `self._parent` (always the invoking machine
904
635
  * under a live `createActor` tree) on the `provideExecutors` path.
905
636
  *
@@ -929,7 +660,7 @@ function deliverUsageEvent(event, resolveActorRef, onDropped) {
929
660
  }
930
661
  /**
931
662
  * Host-binds one text/stream source for {@link provideExecutors} using the SAME
932
- * emission helper as `runAgent` ({@link bindTextLogic}), so a bound
663
+ * emission helper as `createAgentRuntime` ({@link bindTextLogic}), so a bound
933
664
  * text request emits request.start/stream.chunk/request.end/request.error with
934
665
  * identical shapes. @internal
935
666
  */
@@ -938,16 +669,16 @@ function bindTextForProvide(machine, logic, executors, options) {
938
669
  }
939
670
  /**
940
671
  * Host-binds one `DecisionLogic`/`agent.decide` source for
941
- * {@link provideExecutors}: runAgent's decision wrapper (snapshot-driven
672
+ * {@link provideExecutors}: the runtime's decision wrapper (snapshot-driven
942
673
  * candidate events, `canTake`, auto-delivery of the chosen event) with the same
943
- * request-level tracing runAgent emits, minus run-scoped counting. @internal
674
+ * request-level tracing the runtime emits, minus run-scoped counting. @internal
944
675
  */
945
676
  function bindDecisionForProvide(machine, logic, executors, options) {
946
677
  return bindDecisionLogic(logic, provideBindContext(machine, executors, options));
947
678
  }
948
679
  /**
949
680
  * Recursively binds an invoked child state machine for {@link provideExecutors},
950
- * with the same semantics `runAgent` applies ({@link rebindChildMachine}):
681
+ * with the same semantics `createAgentRuntime` applies ({@link rebindChildMachine}):
951
682
  * string-keyed text/decision sources at any depth inherit the host executors,
952
683
  * a source that carries its own executor is left alone, and a cycle is
953
684
  * returned as-is. Each machine in the tree is bound with its own registered
@@ -975,13 +706,13 @@ function resolveMachineInput(machine, input) {
975
706
  try {
976
707
  return validateSchemaSync(schema, input);
977
708
  } catch (error) {
978
- throw new AgentError("invalid-machine-input", `runAgent: machine input failed validation against the declared input schema: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
709
+ throw new AgentError("invalid-machine-input", `createAgentRuntime: machine input failed validation against the declared input schema: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
979
710
  }
980
711
  }
981
712
  /**
982
713
  * Recursively rebinds an invoked child machine's own agent sources with the
983
- * SAME host-backed wrappers runAgent applies to the top-level machine, so a
984
- * child's text/stream/decision requests inherit runAgent's executors and
714
+ * SAME host-backed wrappers the runtime applies to the top-level machine, so a
715
+ * child's text/stream/decision requests inherit the runtime's executors and
985
716
  * participate in maxModelCalls counting, onTrace/onChunk/onResult exactly like
986
717
  * parent requests. Returns the child machine to invoke: a `.provide`-rebound
987
718
  * copy when any inner source needed wrapping, else the original untouched.
@@ -1016,31 +747,66 @@ function rebindChildMachine(childMachine, runCtx, visited, ctxFor) {
1016
747
  return Object.keys(wrapped).length > 0 ? childMachine.provide({ actors: wrapped }) : childMachine;
1017
748
  }
1018
749
  /**
1019
- * Runs an agent machine to completion or idle: a `createActor` host that
1020
- * binds `options`' host executors onto the machine's `agent.*`/`TextLogic`/
1021
- * `DecisionLogic` actor sources, starts (or resumes) the actor, and drives
1022
- * it until it settles — {@link RunAgentResult} `done | idle | error`. Unlike
1023
- * the step helpers ({@link initialAgentStep} etc — a pure
1024
- * transition-at-a-time path for durable hosts), `runAgent` owns a live actor
1025
- * internally; there is no continuation callback, so **idle always settles**
1026
- * and the caller resumes explicitly by passing the settled `{ snapshot,
1027
- * event }` back in. The actor is stopped on every settle path (`done`,
1028
- * `idle`, and `error` alike) — resume is always by snapshot, never by
1029
- * holding a reference to a live actor.
750
+ * The blocking host: opens the run, delivers `event` if given, then applies
751
+ * every event that arrives until the run is quiescent, and returns the
752
+ * outcome. A request handler or durable engine writes the same loop with its
753
+ * own stop policy; this one is for scripts and tests.
754
+ */
755
+ async function runToQuiescence(runtime, init = {}) {
756
+ let [state, effects] = await runtime.start(init);
757
+ await runtime.execute(effects);
758
+ if (init.event !== void 0 && state.status === "active") {
759
+ [state, effects] = runtime.transition(state, init.event);
760
+ await runtime.execute(effects);
761
+ }
762
+ for (let event; event = await runtime.nextEvent();) {
763
+ [state, effects] = runtime.transition(state, event);
764
+ await runtime.execute(effects);
765
+ }
766
+ return runtime.finish();
767
+ }
768
+ /**
769
+ * Entry-by-entry check that the caller's `events` IS the store's thread. Equal
770
+ * lengths prove nothing: a fork, a rolled-back replay, or a concurrent writer
771
+ * that appended and truncated all produce a divergent log of the same size,
772
+ * and appending this run's entries onto it would splice two lineages together.
773
+ */
774
+ function assertThreadMatchesEvents(threadId, stored, events) {
775
+ for (let index = 0; index < stored.length; index++) {
776
+ const storedEntry = stored[index];
777
+ const givenEntry = events[index];
778
+ const storedHash = storedEntry.verification?.stateHash;
779
+ const givenHash = givenEntry.verification?.stateHash;
780
+ if (!(storedEntry.id === givenEntry.id && storedEntry.index === givenEntry.index && canonicalEventJson(storedEntry.event) === canonicalEventJson(givenEntry.event) && (storedHash === void 0 || givenHash === void 0 || storedHash === givenHash))) throw new AgentError("event-log-conflict", `createAgentRuntime: the given \`events\` diverge from thread "${threadId}" at index ${index} (stored entry '${storedEntry.id}', given '${givenEntry.id}') — the log passed in is not this thread's log.`);
781
+ }
782
+ }
783
+ const QUIESCENT = Symbol("quiescent");
784
+ /**
785
+ * Creates the helpers for one run of `machine`: executors bound to its
786
+ * requests, the write-ahead log, traces, the budget, and a mailbox over
787
+ * XState's durable transition loop (`xstate/durable`). See
788
+ * {@link AgentRuntime} for the loop a host writes around it, and
789
+ * {@link runToQuiescence} for the blocking host. {@link AgentRuntime.finish}
790
+ * stops whatever is still running on every settle path (`done`, `idle`, and
791
+ * `error` alike) — resume is always by snapshot (or log), never by holding a
792
+ * reference to a live actor.
1030
793
  *
1031
- * Binding happens **before** the actor starts: every invoke the machine
794
+ * Binding happens **before** any actor starts: every invoke the machine
1032
795
  * could reach is walked and checked against the effective actor sources
1033
796
  * (`options.actors` merged onto the machine), so a missing
1034
797
  * `streamText`/`decide` executor or any other unbound actor source throws
1035
- * immediately — a bind-time error, not a mid-run failure.
798
+ * synchronously from this call — a bind-time error, not a mid-run failure.
1036
799
  *
1037
800
  * @example
1038
801
  * ```ts
1039
802
  * const executors = createAiSdkExecutors({ models });
1040
- * let r = await runAgent(machine, { input, executors });
803
+ * let r = await runToQuiescence(createAgentRuntime(machine, { executors }), { input });
1041
804
  * while (r.status === 'idle') {
1042
805
  * const event = await promptUser(getAcceptedEvents(r.snapshot));
1043
- * r = await runAgent(machine, { snapshot: r.snapshot, event, executors });
806
+ * r = await runToQuiescence(createAgentRuntime(machine, { executors }), {
807
+ * snapshot: r.snapshot,
808
+ * event,
809
+ * });
1044
810
  * }
1045
811
  * if (r.status !== 'done') throw new Error(`Run did not complete: ${r.status}`);
1046
812
  * console.log(r.output);
@@ -1050,46 +816,13 @@ function rebindChildMachine(childMachine, runCtx, visited, ctxFor) {
1050
816
  * an adapter's set: `createAiSdkExecutors` from '@statelyai/agent/ai-sdk' or
1051
817
  * `createOpenAiExecutors` from '@statelyai/agent/openai' supply all three.
1052
818
  */
1053
- async function runAgent(machine, options) {
1054
- const store = options.store;
1055
- if (store === void 0) return createAgentSession(machine, options).settled();
1056
- const threadId = options.threadId;
1057
- if (threadId === void 0 || threadId === "") throw new AgentError("missing-thread-id", "runAgent: `threadId` is required when `store` is given — it names the log thread to read and append to.");
1058
- if (options.events !== void 0) {
1059
- const storedThread = await store.read(threadId);
1060
- if (storedThread.length !== options.events.length) throw new AgentEventLogConflictError(threadId, options.events.length, storedThread.length);
1061
- assertThreadMatchesEvents(threadId, storedThread, options.events);
1062
- return createAgentSession(machine, options).settled();
1063
- }
1064
- const stored = await store.read(threadId);
1065
- return createAgentSession(machine, stored.length > 0 ? {
1066
- ...options,
1067
- events: stored
1068
- } : options).settled();
1069
- }
1070
- /**
1071
- * Entry-by-entry check that the caller's `events` IS the store's thread. Equal
1072
- * lengths prove nothing: a fork, a rolled-back replay, or a concurrent writer
1073
- * that appended and truncated all produce a divergent log of the same size,
1074
- * and appending this run's entries onto it would splice two lineages together.
1075
- */
1076
- function assertThreadMatchesEvents(threadId, stored, events) {
1077
- for (let index = 0; index < stored.length; index++) {
1078
- const storedEntry = stored[index];
1079
- const givenEntry = events[index];
1080
- const storedHash = storedEntry.verification?.stateHash;
1081
- const givenHash = givenEntry.verification?.stateHash;
1082
- if (!(storedEntry.id === givenEntry.id && storedEntry.index === givenEntry.index && canonicalEventJson(storedEntry.event) === canonicalEventJson(givenEntry.event) && (storedHash === void 0 || givenHash === void 0 || storedHash === givenHash))) throw new AgentError("event-log-conflict", `runAgent: the given \`events\` diverge from thread "${threadId}" at index ${index} (stored entry '${storedEntry.id}', given '${givenEntry.id}') — the log passed in is not this thread's log.`);
1083
- }
1084
- }
1085
- function createAgentSession(machine, options) {
819
+ function createAgentRuntime(machine, options = {}) {
1086
820
  const maxModelCalls = options.maxModelCalls ?? 100;
1087
821
  let modelCallCount = 0;
1088
822
  let budgetExceeded = false;
1089
823
  let warnedNonSerializable = false;
1090
824
  const runId = `run_${nextRunAgentTraceId++}`;
1091
825
  let traceSeq = 0;
1092
- const resolvedInput = resolveMachineInput(machine, options.input);
1093
826
  const machineId = machine.config.id ?? machine.id ?? "(machine)";
1094
827
  const machineVersion = resolveMachineVersion(machine);
1095
828
  const onTrace = createTraceDispatch({
@@ -1116,11 +849,10 @@ function createAgentSession(machine, options) {
1116
849
  }
1117
850
  modelCallCount += 1;
1118
851
  };
1119
- const cycleGate = { isResolved: () => false };
1120
852
  const logEntries = [];
1121
853
  const store = options.store;
1122
854
  const threadId = options.threadId;
1123
- if (store !== void 0 && (threadId === void 0 || threadId === "")) throw new AgentError("missing-thread-id", "runAgent: `threadId` is required when `store` is given — it names the log thread to read and append to.");
855
+ if (store !== void 0 && (threadId === void 0 || threadId === "")) throw new AgentError("missing-thread-id", "createAgentRuntime: `threadId` is required when `store` is given — it names the log thread to read and append to.");
1124
856
  let journalChain = Promise.resolve();
1125
857
  let journalPending = 0;
1126
858
  let journalError;
@@ -1136,7 +868,10 @@ function createAgentSession(machine, options) {
1136
868
  });
1137
869
  }).catch((error) => {
1138
870
  journalError ??= error;
1139
- failRunOnJournalError(error);
871
+ stopRun({
872
+ cause: "journal",
873
+ error
874
+ });
1140
875
  }).finally(() => {
1141
876
  journalPending--;
1142
877
  });
@@ -1162,19 +897,10 @@ function createAgentSession(machine, options) {
1162
897
  while (journalPending > 0) await journalChain;
1163
898
  if (journalError !== void 0) throw journalError;
1164
899
  };
1165
- const failRunOnJournalError = (error) => {
1166
- if (settled) return;
1167
- settle({
1168
- status: "error",
1169
- cause: "journal",
1170
- error,
1171
- snapshot: actor.getSnapshot()
1172
- });
1173
- };
1174
900
  let resumedLogLength = 0;
1175
901
  let logExecutionId;
1176
902
  const verificationEnabled = options.verification !== false;
1177
- let lastRootSnapshot;
903
+ let current;
1178
904
  let logMachine = machine;
1179
905
  let loggingEnabled = true;
1180
906
  const appendLogEntry = (event, snapshot) => {
@@ -1190,7 +916,6 @@ function createAgentSession(machine, options) {
1190
916
  return entry;
1191
917
  };
1192
918
  const usageEntrySnapshot = () => {
1193
- const current = lastRootSnapshot;
1194
919
  if (current && (current.status !== "active" || !declaresUsageTransition(current))) return current;
1195
920
  };
1196
921
  const recordUsage = (usage, source = {}) => {
@@ -1199,24 +924,18 @@ function createAgentSession(machine, options) {
1199
924
  ...source,
1200
925
  usage
1201
926
  };
1202
- const journal = { event };
1203
- pendingUsageJournal = journal;
1204
- try {
1205
- deliverUsageEvent(event, () => actorHolder.actorRef, (dropped) => {
1206
- if (!cycleGate.isResolved()) return false;
1207
- onTrace({
1208
- type: "usage.dropped",
1209
- event: dropped,
1210
- reason: "settled"
1211
- });
1212
- return true;
927
+ let delivered = false;
928
+ deliverUsageEvent(event, () => rootFacade(() => delivered = true), (dropped) => {
929
+ if (!finished) return false;
930
+ onTrace({
931
+ type: "usage.dropped",
932
+ event: dropped,
933
+ reason: "settled"
1213
934
  });
1214
- } finally {
1215
- pendingUsageJournal = void 0;
1216
- }
1217
- if (journal.entry === void 0) appendLogEntry(event, usageEntrySnapshot());
935
+ return true;
936
+ });
937
+ if (!delivered) appendLogEntry(event, usageEntrySnapshot());
1218
938
  };
1219
- let pendingUsageJournal;
1220
939
  const runUsage = () => ({
1221
940
  ...getUsageFromEvents(logEntries.slice(resumedLogLength)),
1222
941
  modelCalls: modelCallCount
@@ -1231,14 +950,21 @@ function createAgentSession(machine, options) {
1231
950
  }
1232
951
  if (offending.length === 0) return;
1233
952
  warnedNonSerializable = true;
1234
- console.warn(`runAgent: context holds value(s) that will not survive snapshot persist/resume (JSON round-trip): ${offending.join(", ")}. Persist only JSON-serializable context, or convert these before the run settles.`);
953
+ console.warn(`createAgentRuntime: context holds value(s) that will not survive snapshot persist/resume (JSON round-trip): ${offending.join(", ")}. Persist only JSON-serializable context, or convert these before the run settles.`);
1235
954
  };
1236
955
  const provided = machine.provide({ actors: options.actors });
1237
956
  logMachine = provided;
1238
957
  const effectiveSources = provided.sources.actors;
1239
958
  const executors = { ...options.executors };
1240
959
  assertBindable(provided, effectiveSources, executors);
1241
- const actorHolder = { actorRef: void 0 };
960
+ const rootFacade = (onSend) => ({
961
+ getSnapshot: () => current,
962
+ send: (event) => {
963
+ onSend?.();
964
+ enqueue(event);
965
+ }
966
+ });
967
+ const actorHolder = { actorRef: rootFacade() };
1242
968
  const runCtx = {
1243
969
  generateText: executors.generateText,
1244
970
  streamText: executors.streamText,
@@ -1268,328 +994,446 @@ function createAgentSession(machine, options) {
1268
994
  }
1269
995
  }
1270
996
  const boundMachine = provided.provide({ actors: wrappedSources });
1271
- const isIdle = getMachineIdlePredicate(machine) ?? isAgentIdle;
1272
- const isIntentionalIdle = (snapshot) => isIdle(snapshot);
1273
- const resumeEvents = options.events;
1274
- let effectiveSnapshot = options.snapshot;
1275
- /** Deferred until after the illegal-resume check, so a rejected resume emits no entries. */
1276
- let seedInitEntry;
1277
- const restoredPersistedSnapshot = () => {
1278
- try {
1279
- const restored = logMachine.restoreSnapshot(options.snapshot);
1280
- return logMachine.getPersistedSnapshot(restored);
1281
- } catch {
1282
- return;
1283
- }
997
+ const mailbox = [];
998
+ let wake;
999
+ const fromMailbox = /* @__PURE__ */ new WeakSet();
1000
+ let stopReason;
1001
+ let started = false;
1002
+ let finished = false;
1003
+ let ignoredEvent;
1004
+ const enqueue = (event) => {
1005
+ if (finished) return;
1006
+ fromMailbox.add(event);
1007
+ const waiting = wake;
1008
+ if (waiting) {
1009
+ wake = void 0;
1010
+ waiting(event);
1011
+ } else mailbox.push(event);
1284
1012
  };
1285
- /**
1286
- * Appends the log's reserved first entry, trying each candidate `init` in
1287
- * turn and, for each, an unverified entry if the hash cannot be computed.
1288
- * A machine or snapshot that defeats every attempt leaves the run WITHOUT a
1289
- * log rather than failing it: `runAgent` predates the log, and an exotic
1290
- * machine that cannot be folded must still run.
1291
- */
1292
- const appendInitEntry = (candidates, metadata) => {
1293
- for (const init of candidates) for (const verification of verificationEnabled ? [true, false] : [false]) try {
1294
- const entry = initEntry(logMachine, init, {
1295
- machineVersion,
1296
- verification,
1297
- metadata
1013
+ const inProcessTimers = options.timers === void 0 || options.timers === "in-process";
1014
+ const timerHandles = /* @__PURE__ */ new Map();
1015
+ const timerKey = (source, id) => `${source?.address ?? "(root)"}::${id}`;
1016
+ const armTimer = (source, isRoot, id, delay) => {
1017
+ if (isRoot && !inProcessTimers) {
1018
+ options.timers.schedule({
1019
+ id,
1020
+ delay
1298
1021
  });
1299
- logEntries.push(entry);
1300
- journalEntry(entry);
1301
- options.onEvent?.(entry);
1302
1022
  return;
1023
+ }
1024
+ const key = timerKey(isRoot ? void 0 : source, id);
1025
+ clearTimeout(timerHandles.get(key));
1026
+ timerHandles.set(key, setTimeout(() => {
1027
+ timerHandles.delete(key);
1028
+ const timerEvent = {
1029
+ type: "xstate.timer",
1030
+ id
1031
+ };
1032
+ if (isRoot) enqueue(timerEvent);
1033
+ else source?.send(timerEvent);
1034
+ recheck();
1035
+ }, delay));
1036
+ };
1037
+ const childrenBusy = (snapshot) => Object.values(snapshot?.children ?? {}).some((child) => {
1038
+ const childSnapshot = child?.getSnapshot?.();
1039
+ if (childSnapshot?.status !== "active") return false;
1040
+ return isMachineSnapshot(childSnapshot) ? childrenBusy(childSnapshot) : true;
1041
+ });
1042
+ const workInFlight = () => childrenBusy(current) || inProcessTimers && timerHandles.size > 0;
1043
+ let recheckScheduled = false;
1044
+ const recheck = () => {
1045
+ if (wake === void 0 || recheckScheduled) return;
1046
+ if (stopReason === void 0 && workInFlight()) return;
1047
+ recheckScheduled = true;
1048
+ setTimeout(() => {
1049
+ recheckScheduled = false;
1050
+ if (wake !== void 0 && (stopReason !== void 0 || !workInFlight())) {
1051
+ const waiting = wake;
1052
+ wake = void 0;
1053
+ waiting(QUIESCENT);
1054
+ }
1055
+ }, 0);
1056
+ };
1057
+ const stopChildren = (snapshot) => {
1058
+ for (const child of Object.values(snapshot?.children ?? {})) try {
1059
+ if (child) stopActor(child);
1303
1060
  } catch {}
1304
- loggingEnabled = false;
1061
+ for (const handle of timerHandles.values()) clearTimeout(handle);
1062
+ timerHandles.clear();
1305
1063
  };
1306
- /** Init forms for a snapshot resume, most faithful first. */
1307
- const snapshotInitCandidates = () => {
1308
- const restored = restoredPersistedSnapshot();
1309
- return [...restored !== void 0 ? [{ snapshot: restored }] : [], { snapshot: options.snapshot }];
1064
+ let persistedAtStop;
1065
+ const stopRun = (reason) => {
1066
+ if (finished || stopReason !== void 0) return;
1067
+ stopReason = reason;
1068
+ try {
1069
+ persistedAtStop = current && boundMachine.getPersistedSnapshot(current);
1070
+ } catch {}
1071
+ stopChildren(current);
1072
+ recheck();
1310
1073
  };
1311
- if (resumeEvents !== void 0 && resumeEvents.length > 0) {
1312
- validateReplayEntries(resumeEvents);
1313
- const tail = resumeEvents[resumeEvents.length - 1];
1314
- const inheritedExecutionId = getLogExecutionId(resumeEvents);
1315
- if (resumeEvents[0].machineId !== machineId) throw new AgentMachineVersionMismatchError(tail.id, tail.index, {
1316
- machineId,
1317
- machineVersion
1318
- }, {
1319
- machineId: resumeEvents[0].machineId,
1320
- machineVersion: tail.machineVersion
1074
+ const onAbort = () => stopRun({
1075
+ cause: "aborted",
1076
+ error: options.signal?.reason ?? /* @__PURE__ */ new Error("Aborted")
1077
+ });
1078
+ const emitFromRoot = (event) => {
1079
+ onTrace({
1080
+ type: "emit",
1081
+ event
1321
1082
  });
1322
- if (tail.machineVersion === machineVersion) {
1323
- logEntries.push(...resumeEvents);
1324
- resumedLogLength = logEntries.length;
1325
- logExecutionId = inheritedExecutionId;
1326
- const cachedMeta = readAgentMeta(options.snapshot);
1327
- const tailStateHash = tail.verification?.stateHash;
1328
- if (!(options.snapshot !== void 0 && inheritedExecutionId !== void 0 && cachedMeta?.logId === inheritedExecutionId && cachedMeta.logIndex === resumeEvents.length && tailStateHash !== void 0 && hashResumeSnapshot(options.snapshot) === tailStateHash)) {
1329
- const cachedIndex = cachedMeta?.logIndex;
1330
- if (options.snapshot !== void 0 && typeof cachedIndex === "number" && cachedIndex >= 1 && cachedIndex <= resumeEvents.length && resumeEvents[cachedIndex - 1].verification?.stateHash !== void 0) {
1331
- const expected = getSnapshotStateHash(replay(logMachine, resumeEvents.slice(0, cachedIndex), {
1332
- machineVersion,
1333
- verify: false
1334
- }).persistedSnapshot);
1335
- const actual = hashResumeSnapshot(options.snapshot);
1336
- if (expected !== actual) throw new AgentSnapshotDivergedError(expected, actual, cachedIndex);
1337
- }
1338
- effectiveSnapshot = replay(logMachine, resumeEvents, {
1339
- machineVersion,
1340
- verify: true
1341
- }).persistedSnapshot;
1342
- }
1343
- } else {
1344
- if (options.snapshot === void 0) throw new AgentMachineVersionMismatchError(tail.id, tail.index, {
1345
- machineId,
1346
- machineVersion
1347
- }, {
1348
- machineId: tail.machineId,
1349
- machineVersion: tail.machineVersion
1350
- });
1351
- const bridgeMeta = readAgentMeta(options.snapshot);
1352
- if (inheritedExecutionId === void 0 || bridgeMeta?.logId !== inheritedExecutionId) throw new AgentMachineVersionMismatchError(tail.id, tail.index, {
1353
- machineId,
1354
- machineVersion
1355
- }, {
1356
- machineId: tail.machineId,
1357
- machineVersion: tail.machineVersion
1083
+ const on = options.on ?? {};
1084
+ on[event.type]?.(event);
1085
+ if (event.type !== "*") on["*"]?.(event);
1086
+ };
1087
+ const inspect = options.inspect === void 0 ? void 0 : (event) => {
1088
+ if (typeof options.inspect === "function") options.inspect(event);
1089
+ else options.inspect?.next?.(event);
1090
+ };
1091
+ let execution;
1092
+ execution = createDurable(boundMachine, {
1093
+ startActor: (actor) => {
1094
+ actor.subscribe({
1095
+ next: recheck,
1096
+ complete: recheck,
1097
+ error: recheck
1358
1098
  });
1359
- const bridgeTailHash = tail.verification?.stateHash;
1360
- const bridgeSnapshotHash = hashResumeSnapshot(options.snapshot);
1361
- if (bridgeMeta.logIndex !== resumeEvents.length) throw new AgentSnapshotDivergedError(bridgeTailHash ?? "(unrecorded)", bridgeSnapshotHash, resumeEvents.length);
1362
- if (bridgeTailHash !== void 0 && bridgeSnapshotHash !== bridgeTailHash) throw new AgentSnapshotDivergedError(bridgeTailHash, bridgeSnapshotHash, resumeEvents.length);
1363
- logExecutionId = inheritedExecutionId;
1364
- const migratedFrom = {
1365
- machineVersion: tail.machineVersion,
1366
- logIndex: resumeEvents.length
1367
- };
1368
- seedInitEntry = () => appendInitEntry(snapshotInitCandidates(), {
1369
- ...logExecutionId !== void 0 ? { executionId: logExecutionId } : {},
1370
- migratedFrom
1099
+ actor.start();
1100
+ },
1101
+ enqueueRootEvent: (_source, event) => enqueue(event),
1102
+ executeAction: (action, _metadata, runtime) => action.exec(runtime),
1103
+ waitForEvent: () => {
1104
+ const queued = mailbox.shift();
1105
+ if (queued !== void 0) return queued;
1106
+ if (stopReason !== void 0 || finished || !workInFlight()) return QUIESCENT;
1107
+ return new Promise((resolve) => {
1108
+ wake = resolve;
1371
1109
  });
1110
+ },
1111
+ emitEvent: (source, event) => {
1112
+ if (source.address === execution.rootAddress) emitFromRoot(event);
1113
+ else source._emit?.(event);
1114
+ },
1115
+ scheduleTimer: (source, id, delay) => {
1116
+ armTimer(source, source.address === execution.rootAddress, id, delay);
1117
+ },
1118
+ cancelTimer: (source, id) => {
1119
+ const isRoot = source.address === execution.rootAddress;
1120
+ if (isRoot && !inProcessTimers) {
1121
+ options.timers.cancel(id);
1122
+ return;
1123
+ }
1124
+ const key = timerKey(isRoot ? void 0 : source, id);
1125
+ clearTimeout(timerHandles.get(key));
1126
+ timerHandles.delete(key);
1127
+ },
1128
+ cancelAllTimers: (source) => {
1129
+ const prefix = timerKey(source.address === execution.rootAddress ? void 0 : source, "");
1130
+ for (const [key, handle] of timerHandles) if (key.startsWith(prefix)) {
1131
+ clearTimeout(handle);
1132
+ timerHandles.delete(key);
1133
+ }
1372
1134
  }
1373
- } else if (options.snapshot !== void 0) {
1374
- logExecutionId = crypto.randomUUID();
1375
- seedInitEntry = () => appendInitEntry(snapshotInitCandidates(), { executionId: logExecutionId });
1376
- } else {
1377
- logExecutionId = crypto.randomUUID();
1378
- seedInitEntry = () => appendInitEntry([resolvedInput !== void 0 ? { input: resolvedInput } : {}], { executionId: logExecutionId });
1379
- }
1380
- const resumedHistory = [...logEntries];
1381
- const siteCallCounts = /* @__PURE__ */ new Map();
1382
- const mintedCallKeys = /* @__PURE__ */ new WeakMap();
1383
- runCtx.callKey = (siteId, self) => {
1384
- if (logExecutionId === void 0) return;
1385
- const memoized = self === void 0 ? void 0 : mintedCallKeys.get(self);
1386
- if (memoized !== void 0) return memoized;
1387
- const startedHere = siteCallCounts.get(siteId) ?? 0;
1388
- siteCallCounts.set(siteId, startedHere + 1);
1389
- const key = `${logExecutionId}:${siteId}#${agentCallOccurrence(resumedHistory, siteId) + startedHere}`;
1390
- if (self !== void 0) mintedCallKeys.set(self, key);
1391
- return key;
1392
- };
1393
- const resumeEventToSend = options.event;
1394
- let ignoredEvent;
1395
- seedInitEntry?.();
1396
- let settled = false;
1397
- let journalAtSettle;
1398
- cycleGate.isResolved = () => settled;
1399
- let lastResult;
1400
- const waiters = [];
1401
- let idleTimer;
1402
- let actor;
1403
- let actorStarted = false;
1404
- let deliveringResumeEvent = resumeEventToSend !== void 0;
1405
- const settle = (outcome) => {
1406
- if (settled) return;
1407
- settled = true;
1408
- let persistedSnapshot;
1409
- let persistenceError;
1410
- if (actorStarted && outcome.status !== "done") try {
1411
- persistedSnapshot = actor.getPersistedSnapshot();
1412
- } catch (error) {
1413
- persistenceError = error;
1414
- }
1415
- const logIndexAtSettle = logEntries.length;
1416
- journalAtSettle = journalChain;
1417
- const persist = () => {
1418
- if (persistenceError !== void 0) throw persistenceError;
1419
- persistedSnapshot ??= actor.getPersistedSnapshot();
1420
- persistedSnapshot.agentMeta = {
1421
- machineId,
1422
- version: machineVersion,
1423
- ...logExecutionId !== void 0 ? { logId: logExecutionId } : {},
1424
- logIndex: logIndexAtSettle
1425
- };
1426
- return persistedSnapshot;
1427
- };
1428
- const result = {
1429
- ...outcome,
1430
- events: [...logEntries],
1431
- persist,
1432
- drain: drainJournal,
1433
- usage: runUsage(),
1434
- ...ignoredEvent !== void 0 ? { ignored: ignoredEvent } : {}
1435
- };
1436
- if (idleTimer !== void 0) clearTimeout(idleTimer);
1135
+ }, inspect ? { inspect } : void 0);
1136
+ const runErrorCause = (error) => budgetExceeded ? "max-model-calls" : wrapsDecisionExhausted(error) ? "decision-exhausted" : "machine";
1137
+ const commit = (snapshot, event) => {
1138
+ current = snapshot;
1437
1139
  onTrace({
1438
- type: "run.end",
1439
- ...outcome
1440
- });
1441
- if (options.signal) options.signal.removeEventListener("abort", onAbort);
1442
- actor.stop();
1443
- lastResult = result;
1444
- for (const resolve of waiters.splice(0)) resolve(result);
1445
- };
1446
- const onAbort = () => {
1447
- settle({
1448
- status: "error",
1449
- cause: "aborted",
1450
- error: options.signal?.reason ?? /* @__PURE__ */ new Error("Aborted"),
1451
- snapshot: actor.getSnapshot()
1452
- });
1453
- };
1454
- const settleIdle = (current) => {
1455
- warnNonSerializableContext(current);
1456
- settle({
1457
- status: "idle",
1458
- snapshot: current
1140
+ type: "machine.transition",
1141
+ snapshot,
1142
+ event
1459
1143
  });
1460
1144
  };
1461
- const runErrorCause = (error) => budgetExceeded ? "max-model-calls" : wrapsDecisionExhausted(error) ? "decision-exhausted" : "machine";
1462
- const scheduleIdleCheck = () => {
1463
- if (idleTimer !== void 0) clearTimeout(idleTimer);
1464
- idleTimer = setTimeout(() => {
1465
- idleTimer = void 0;
1466
- if (settled) return;
1467
- const current = actor.getSnapshot();
1468
- if (isIntentionalIdle(current) && isIdleSnapshot(current)) settleIdle(current);
1469
- }, 0);
1145
+ /** True for a child completion/failure whose child is no longer running under the current state. */
1146
+ const isStaleChildEvent = (event) => {
1147
+ if (!event.type.startsWith("xstate.done.actor") && !event.type.startsWith("xstate.error.actor")) return false;
1148
+ const { sessionId, actorId } = event;
1149
+ return !Object.values(current?.children ?? {}).some((child) => sessionId !== void 0 ? child?.sessionId === sessionId : child?.id === actorId);
1470
1150
  };
1471
- actor = createActor(boundMachine, {
1472
- input: resolvedInput,
1473
- snapshot: effectiveSnapshot,
1474
- inspect: (event) => {
1475
- if (typeof options.inspect === "function") options.inspect(event);
1476
- else options.inspect?.next?.(event);
1477
- if (event.type !== "@xstate.transition" || event.actorRef !== actor.ref) return;
1478
- if (settled) return;
1479
- const snapshot = event.snapshot;
1480
- lastRootSnapshot = snapshot;
1481
- if (resumeEventToSend !== void 0 && event.event === resumeEventToSend && event.microsteps.length === 0 && event.actions.length === 0 && event.sent.length === 0) ignoredEvent = resumeEventToSend;
1482
- if (pendingUsageJournal !== void 0 && event.event === pendingUsageJournal.event) pendingUsageJournal.entry = appendLogEntry(event.event, snapshot);
1483
- else if (event.event.type !== "@xstate.init" && (event.sourceRef !== event.actorRef || event.event.type === "xstate.timer")) appendLogEntry(event.event, snapshot);
1151
+ return {
1152
+ async start(init = {}) {
1153
+ if (started) throw new AgentError("runtime-started", "createAgentRuntime: `start` was already called.");
1154
+ started = true;
1155
+ let resumeEvents = init.events;
1156
+ if (store !== void 0) {
1157
+ const storedThread = await store.read(threadId);
1158
+ if (resumeEvents !== void 0) {
1159
+ if (storedThread.length !== resumeEvents.length) throw new AgentEventLogConflictError(threadId, resumeEvents.length, storedThread.length);
1160
+ assertThreadMatchesEvents(threadId, storedThread, resumeEvents);
1161
+ } else if (storedThread.length > 0) resumeEvents = storedThread;
1162
+ }
1163
+ const resolvedInput = resolveMachineInput(machine, init.input);
1164
+ let effectiveSnapshot = init.snapshot;
1165
+ let seedInitEntry;
1166
+ const restoredPersistedSnapshot = () => {
1167
+ try {
1168
+ const restored = logMachine.restoreSnapshot(init.snapshot);
1169
+ return logMachine.getPersistedSnapshot(restored);
1170
+ } catch {
1171
+ return;
1172
+ }
1173
+ };
1174
+ /**
1175
+ * Appends the log's reserved first entry, trying each candidate `init`
1176
+ * in turn and, for each, an unverified entry if the hash cannot be
1177
+ * computed. A machine or snapshot that defeats every attempt leaves the
1178
+ * run WITHOUT a log rather than failing it.
1179
+ */
1180
+ const appendInitEntry = (candidates, metadata) => {
1181
+ for (const candidate of candidates) for (const verification of verificationEnabled ? [true, false] : [false]) try {
1182
+ const entry = initEntry(logMachine, candidate, {
1183
+ machineVersion,
1184
+ verification,
1185
+ metadata
1186
+ });
1187
+ logEntries.push(entry);
1188
+ journalEntry(entry);
1189
+ options.onEvent?.(entry);
1190
+ return;
1191
+ } catch {}
1192
+ loggingEnabled = false;
1193
+ };
1194
+ /** Init forms for a snapshot resume, most faithful first. */
1195
+ const snapshotInitCandidates = () => {
1196
+ const restored = restoredPersistedSnapshot();
1197
+ return [...restored !== void 0 ? [{ snapshot: restored }] : [], { snapshot: init.snapshot }];
1198
+ };
1199
+ if (resumeEvents !== void 0 && resumeEvents.length > 0) {
1200
+ validateReplayEntries(resumeEvents);
1201
+ const tail = resumeEvents[resumeEvents.length - 1];
1202
+ const inheritedExecutionId = getLogExecutionId(resumeEvents);
1203
+ if (resumeEvents[0].machineId !== machineId) throw new AgentMachineVersionMismatchError(tail.id, tail.index, {
1204
+ machineId,
1205
+ machineVersion
1206
+ }, {
1207
+ machineId: resumeEvents[0].machineId,
1208
+ machineVersion: tail.machineVersion
1209
+ });
1210
+ if (tail.machineVersion === machineVersion) {
1211
+ logEntries.push(...resumeEvents);
1212
+ resumedLogLength = logEntries.length;
1213
+ logExecutionId = inheritedExecutionId;
1214
+ const cachedMeta = readAgentMeta(init.snapshot);
1215
+ const tailStateHash = tail.verification?.stateHash;
1216
+ if (!(init.snapshot !== void 0 && inheritedExecutionId !== void 0 && cachedMeta?.logId === inheritedExecutionId && cachedMeta.logIndex === resumeEvents.length && tailStateHash !== void 0 && hashResumeSnapshot(init.snapshot) === tailStateHash)) {
1217
+ const cachedIndex = cachedMeta?.logIndex;
1218
+ if (init.snapshot !== void 0 && typeof cachedIndex === "number" && cachedIndex >= 1 && cachedIndex <= resumeEvents.length && resumeEvents[cachedIndex - 1].verification?.stateHash !== void 0) {
1219
+ const expected = getSnapshotStateHash(replay(logMachine, resumeEvents.slice(0, cachedIndex), {
1220
+ machineVersion,
1221
+ verify: false
1222
+ }).persistedSnapshot);
1223
+ const actual = hashResumeSnapshot(init.snapshot);
1224
+ if (expected !== actual) throw new AgentSnapshotDivergedError(expected, actual, cachedIndex);
1225
+ }
1226
+ effectiveSnapshot = replay(logMachine, resumeEvents, {
1227
+ machineVersion,
1228
+ verify: true
1229
+ }).persistedSnapshot;
1230
+ }
1231
+ } else {
1232
+ if (init.snapshot === void 0) throw new AgentMachineVersionMismatchError(tail.id, tail.index, {
1233
+ machineId,
1234
+ machineVersion
1235
+ }, {
1236
+ machineId: tail.machineId,
1237
+ machineVersion: tail.machineVersion
1238
+ });
1239
+ const bridgeMeta = readAgentMeta(init.snapshot);
1240
+ if (inheritedExecutionId === void 0 || bridgeMeta?.logId !== inheritedExecutionId) throw new AgentMachineVersionMismatchError(tail.id, tail.index, {
1241
+ machineId,
1242
+ machineVersion
1243
+ }, {
1244
+ machineId: tail.machineId,
1245
+ machineVersion: tail.machineVersion
1246
+ });
1247
+ const bridgeTailHash = tail.verification?.stateHash;
1248
+ const bridgeSnapshotHash = hashResumeSnapshot(init.snapshot);
1249
+ if (bridgeMeta.logIndex !== resumeEvents.length) throw new AgentSnapshotDivergedError(bridgeTailHash ?? "(unrecorded)", bridgeSnapshotHash, resumeEvents.length);
1250
+ if (bridgeTailHash !== void 0 && bridgeSnapshotHash !== bridgeTailHash) throw new AgentSnapshotDivergedError(bridgeTailHash, bridgeSnapshotHash, resumeEvents.length);
1251
+ logExecutionId = inheritedExecutionId;
1252
+ const migratedFrom = {
1253
+ machineVersion: tail.machineVersion,
1254
+ logIndex: resumeEvents.length
1255
+ };
1256
+ seedInitEntry = () => appendInitEntry(snapshotInitCandidates(), {
1257
+ ...logExecutionId !== void 0 ? { executionId: logExecutionId } : {},
1258
+ migratedFrom
1259
+ });
1260
+ }
1261
+ } else if (init.snapshot !== void 0) {
1262
+ logExecutionId = crypto.randomUUID();
1263
+ seedInitEntry = () => appendInitEntry(snapshotInitCandidates(), { executionId: logExecutionId });
1264
+ } else {
1265
+ logExecutionId = crypto.randomUUID();
1266
+ seedInitEntry = () => appendInitEntry([resolvedInput !== void 0 ? { input: resolvedInput } : {}], { executionId: logExecutionId });
1267
+ }
1268
+ const resumedHistory = [...logEntries];
1269
+ const siteCallCounts = /* @__PURE__ */ new Map();
1270
+ const mintedCallKeys = /* @__PURE__ */ new WeakMap();
1271
+ runCtx.callKey = (siteId, self) => {
1272
+ if (logExecutionId === void 0) return;
1273
+ const memoized = self === void 0 ? void 0 : mintedCallKeys.get(self);
1274
+ if (memoized !== void 0) return memoized;
1275
+ const startedHere = siteCallCounts.get(siteId) ?? 0;
1276
+ siteCallCounts.set(siteId, startedHere + 1);
1277
+ const key = `${logExecutionId}:${siteId}#${agentCallOccurrence(resumedHistory, siteId) + startedHere}`;
1278
+ if (self !== void 0) mintedCallKeys.set(self, key);
1279
+ return key;
1280
+ };
1281
+ seedInitEntry?.();
1282
+ const resumeEvent = init.event;
1484
1283
  onTrace({
1485
- type: "machine.transition",
1486
- snapshot,
1487
- event: event.event
1284
+ type: "run.start",
1285
+ ...resolvedInput !== void 0 ? { input: resolvedInput } : {},
1286
+ ...effectiveSnapshot !== void 0 ? { snapshot: effectiveSnapshot } : {},
1287
+ ...resumeEvent !== void 0 ? { event: resumeEvent } : {}
1488
1288
  });
1489
- if (snapshot.status === "done") {
1490
- settle({
1491
- status: "done",
1492
- output: snapshot.output,
1493
- snapshot
1494
- });
1495
- return;
1289
+ if (options.signal) if (options.signal.aborted) stopReason = {
1290
+ cause: "aborted",
1291
+ error: options.signal.reason ?? /* @__PURE__ */ new Error("Aborted")
1292
+ };
1293
+ else options.signal.addEventListener("abort", onAbort);
1294
+ let snapshot;
1295
+ let effects = [];
1296
+ const initEvent = {
1297
+ type: "@xstate.init",
1298
+ ...resolvedInput !== void 0 ? { input: resolvedInput } : {}
1299
+ };
1300
+ if (effectiveSnapshot === void 0) [snapshot, effects] = execution.initialTransition(resolvedInput);
1301
+ else {
1302
+ try {
1303
+ [snapshot, effects] = execution.restore(effectiveSnapshot);
1304
+ } catch (error) {
1305
+ try {
1306
+ current = execution.initialTransition(resolvedInput)[0];
1307
+ } catch {
1308
+ current = {
1309
+ status: "error",
1310
+ error,
1311
+ context: void 0
1312
+ };
1313
+ }
1314
+ stopReason ??= {
1315
+ cause: "machine",
1316
+ error
1317
+ };
1318
+ return [current, []];
1319
+ }
1320
+ current = snapshot;
1496
1321
  }
1497
- if (snapshot.status === "error") {
1498
- settle({
1499
- status: "error",
1500
- cause: runErrorCause(snapshot.error),
1501
- error: snapshot.error,
1502
- snapshot
1322
+ commit(snapshot, initEvent);
1323
+ return [snapshot, effects];
1324
+ },
1325
+ transition(state, event) {
1326
+ if (finished) throw new AgentError("runtime-finished", "createAgentRuntime: the run has finished; start a new runtime to continue.");
1327
+ if (stopReason !== void 0) return [state, []];
1328
+ const [next, effects] = execution.transition(state, event);
1329
+ if (!fromMailbox.has(event) && next === state && effects.length === 0) ignoredEvent = event;
1330
+ appendLogEntry(event, next);
1331
+ commit(next, event);
1332
+ return [next, effects];
1333
+ },
1334
+ async execute(effects) {
1335
+ if (stopReason !== void 0 || finished || effects.length === 0) return;
1336
+ try {
1337
+ await execution.executeEffects(effects);
1338
+ } catch (error) {
1339
+ stopRun({
1340
+ cause: "machine",
1341
+ error
1503
1342
  });
1504
- return;
1505
1343
  }
1506
- if (snapshot.status === "stopped") {
1507
- settle({
1508
- status: "error",
1509
- cause: "stopped",
1510
- error: /* @__PURE__ */ new Error("Actor stopped externally."),
1344
+ recheck();
1345
+ },
1346
+ async nextEvent() {
1347
+ for (;;) {
1348
+ if (finished || stopReason !== void 0 || current?.status !== "active") return;
1349
+ const event = await execution.waitForEvent();
1350
+ if (event === QUIESCENT || stopReason !== void 0) return;
1351
+ if (isStaleChildEvent(event)) continue;
1352
+ return event;
1353
+ }
1354
+ },
1355
+ async finish() {
1356
+ if (finished) throw new AgentError("runtime-finished", "createAgentRuntime: `finish` was already called.");
1357
+ const snapshot = current;
1358
+ let outcome;
1359
+ if (stopReason !== void 0) outcome = {
1360
+ status: "error",
1361
+ cause: stopReason.cause === "machine" ? runErrorCause(stopReason.error) : stopReason.cause,
1362
+ error: stopReason.error,
1363
+ snapshot
1364
+ };
1365
+ else if (snapshot.status === "done") outcome = {
1366
+ status: "done",
1367
+ output: snapshot.output,
1368
+ snapshot
1369
+ };
1370
+ else if (snapshot.status === "error") outcome = {
1371
+ status: "error",
1372
+ cause: runErrorCause(snapshot.error),
1373
+ error: snapshot.error,
1374
+ snapshot
1375
+ };
1376
+ else if (snapshot.status === "stopped") outcome = {
1377
+ status: "error",
1378
+ cause: "stopped",
1379
+ error: /* @__PURE__ */ new Error("Actor stopped externally."),
1380
+ snapshot
1381
+ };
1382
+ else {
1383
+ warnNonSerializableContext(snapshot);
1384
+ outcome = {
1385
+ status: "idle",
1511
1386
  snapshot
1512
- });
1513
- return;
1387
+ };
1514
1388
  }
1515
- if (!deliveringResumeEvent && isIntentionalIdle(snapshot) && isIdleSnapshot(snapshot)) {
1516
- queueMicrotask(() => {
1517
- if (settled) return;
1518
- const current = actor.getSnapshot();
1519
- if (isIntentionalIdle(current) && isIdleSnapshot(current)) settleIdle(current);
1520
- else scheduleIdleCheck();
1521
- });
1522
- return;
1389
+ let persistedSnapshot = persistedAtStop;
1390
+ let persistenceError;
1391
+ if (outcome.status !== "done" && persistedSnapshot === void 0) try {
1392
+ persistedSnapshot = boundMachine.getPersistedSnapshot(snapshot);
1393
+ } catch (error) {
1394
+ persistenceError = error;
1523
1395
  }
1524
- scheduleIdleCheck();
1525
- }
1526
- });
1527
- actorHolder.actorRef = actor;
1528
- actor.subscribe({ error: () => {} });
1529
- actor.on("*", (event) => {
1530
- onTrace({
1531
- type: "emit",
1532
- event
1533
- });
1534
- });
1535
- for (const [type, handler] of Object.entries(options.on ?? {})) if (typeof handler === "function") actor.on(type, handler);
1536
- const sessionApi = { settled: async () => {
1537
- const result = settled && lastResult !== void 0 ? lastResult : await new Promise((resolve) => {
1538
- waiters.push(resolve);
1539
- });
1540
- await journalAtSettle;
1541
- return result;
1542
- } };
1543
- if (options.signal) {
1544
- if (options.signal.aborted) {
1545
- settle({
1546
- status: "error",
1547
- cause: "aborted",
1548
- error: options.signal.reason ?? /* @__PURE__ */ new Error("Aborted"),
1549
- snapshot: actor.getSnapshot()
1396
+ const logIndexAtSettle = logEntries.length;
1397
+ const persist = () => {
1398
+ if (persistenceError !== void 0) throw persistenceError;
1399
+ persistedSnapshot ??= boundMachine.getPersistedSnapshot(snapshot);
1400
+ persistedSnapshot.agentMeta = {
1401
+ machineId,
1402
+ version: machineVersion,
1403
+ ...logExecutionId !== void 0 ? { logId: logExecutionId } : {},
1404
+ logIndex: logIndexAtSettle
1405
+ };
1406
+ return persistedSnapshot;
1407
+ };
1408
+ finished = true;
1409
+ const journalAtSettle = journalChain;
1410
+ const result = {
1411
+ ...outcome,
1412
+ events: [...logEntries],
1413
+ persist,
1414
+ drain: drainJournal,
1415
+ usage: runUsage(),
1416
+ ...ignoredEvent !== void 0 ? { ignored: ignoredEvent } : {}
1417
+ };
1418
+ onTrace({
1419
+ type: "run.end",
1420
+ ...outcome
1550
1421
  });
1551
- return sessionApi;
1422
+ options.signal?.removeEventListener("abort", onAbort);
1423
+ stopChildren(snapshot);
1424
+ if (wake !== void 0) {
1425
+ const waiting = wake;
1426
+ wake = void 0;
1427
+ waiting(QUIESCENT);
1428
+ }
1429
+ await journalAtSettle;
1430
+ return result;
1552
1431
  }
1553
- options.signal.addEventListener("abort", onAbort);
1554
- }
1555
- onTrace({
1556
- type: "run.start",
1557
- ...resolvedInput !== void 0 ? { input: resolvedInput } : {},
1558
- ...effectiveSnapshot !== void 0 ? { snapshot: effectiveSnapshot } : {},
1559
- ...resumeEventToSend !== void 0 ? { event: resumeEventToSend } : {}
1560
- });
1561
- actorStarted = true;
1562
- actor.start();
1563
- if (!settled) {
1564
- const startedSnapshot = actor.getSnapshot();
1565
- lastRootSnapshot ??= startedSnapshot;
1566
- if (startedSnapshot.status === "done") settle({
1567
- status: "done",
1568
- output: startedSnapshot.output,
1569
- snapshot: startedSnapshot
1570
- });
1571
- else if (startedSnapshot.status === "error") settle({
1572
- status: "error",
1573
- cause: runErrorCause(startedSnapshot.error),
1574
- error: startedSnapshot.error,
1575
- snapshot: startedSnapshot
1576
- });
1577
- else if (startedSnapshot.status === "stopped") settle({
1578
- status: "error",
1579
- cause: "stopped",
1580
- error: /* @__PURE__ */ new Error("Actor stopped externally."),
1581
- snapshot: startedSnapshot
1582
- });
1583
- }
1584
- if (resumeEventToSend && !settled) {
1585
- deliveringResumeEvent = false;
1586
- actor.send(resumeEventToSend);
1587
- }
1588
- return sessionApi;
1432
+ };
1589
1433
  }
1590
1434
  /**
1591
1435
  * Wraps a `(snapshot, actorRef) => void` handler into a function usable as
1592
- * {@link RunAgentOptions.inspect}: it filters the raw inspection stream to
1436
+ * {@link AgentRuntimeOptions.inspect}: it filters the raw inspection stream to
1593
1437
  * `@xstate.transition` events and hands the handler the typed
1594
1438
  * {@link AnyMachineSnapshot} and the {@link InspectedActorRef} that
1595
1439
  * transitioned. Attribute a child actor via `actorRef.id`/`actorRef.src`. Saves
@@ -1614,12 +1458,12 @@ function inspectTransitions(handler) {
1614
1458
  * const actor = createActor(bound, { inspect: traceTransitions(onTrace) });
1615
1459
  * ```
1616
1460
  *
1617
- * Only ROOT-actor transitions are traced (matching `runAgent`'s
1461
+ * Only ROOT-actor transitions are traced (matching the runtime's
1618
1462
  * `machine.transition`); child-actor transitions are ignored. Attribute the
1619
1463
  * event via its envelope `runId`.
1620
1464
  *
1621
1465
  * By design this path has NO `run.start`/`run.end` events: `createActor` has no
1622
- * run boundary the way `runAgent` does, so the stream starts at the actor's
1466
+ * run boundary the way `createAgentRuntime` does, so the stream starts at the actor's
1623
1467
  * first transition. It also does NOT emit `emit` trace events: in this xstate
1624
1468
  * build emitted events are delivered through `actor.on(...)`, not the inspection
1625
1469
  * protocol, so they are not observable from an `inspect` handler — subscribe
@@ -1637,26 +1481,14 @@ function traceTransitions(onTrace) {
1637
1481
  }));
1638
1482
  };
1639
1483
  }
1640
- function isIdleSnapshot(snapshot) {
1641
- if (snapshot.status !== "active") return false;
1642
- if (Object.values(snapshot.children ?? {}).some((child) => {
1643
- const childSnapshot = child?.getSnapshot?.();
1644
- if (childSnapshot?.status !== "active") return false;
1645
- if (isMachineSnapshot(childSnapshot)) return !isIdleSnapshot(childSnapshot);
1646
- return true;
1647
- })) return false;
1648
- return !getNextTransitions(snapshot).some((transitionDef) => transitionDef.eventType === "" || transitionDef.eventType.startsWith("xstate.after"));
1649
- }
1650
1484
  /**
1651
- * Default machine-owned idle signal used by {@link runAgent}: an active
1485
+ * Whether a snapshot is asking the outside world for something: an active
1652
1486
  * snapshot that accepts an external event anywhere in its active hierarchy,
1653
1487
  * or declares interaction metadata on an active state.
1654
1488
  *
1655
- * Compose this in `setupAgent({ isIdle })` when the application has additional
1656
- * wait states: `isIdle: (snapshot) => isAgentIdle(snapshot) || snapshot.hasTag('waiting')`.
1657
- * Pending work and active invoked children are checked separately by the
1658
- * runner, so this function only answers whether the state is an intentional
1659
- * external wait.
1489
+ * The runtime does not use this to stop; a run settles when nothing is in
1490
+ * flight. Use it once the run is quiescent, to tell a human wait (render
1491
+ * `getInteraction(snapshot)`) from a machine that is simply stuck.
1660
1492
  */
1661
1493
  function isAgentIdle(snapshot) {
1662
1494
  if (snapshot.status !== "active") return false;
@@ -1666,4 +1498,4 @@ function isAgentIdle(snapshot) {
1666
1498
  }) || getAcceptedEvents(snapshot).length > 0;
1667
1499
  }
1668
1500
  //#endregion
1669
- export { rejectAgentStep as _, bindDecisionForProvide as a, inspectTransitions as c, runAgent as d, serializeTraceEvent as f, initialAgentStep as g, getPendingInvokes as h, bindChildMachineForProvide as i, isAgentIdle as l, executeAgentRequest as m, AgentMaxModelCallsExceededError as n, bindTextForProvide as o, traceTransitions as p, AgentSnapshotDivergedError as r, getConfiguredInvokeSrcs as s, AGENT_TRACE_SCHEMA_VERSION as t, isStateMachineLogic as u, resolveAgentStep as v, transitionAgentStep as y };
1501
+ export { bindDecisionForProvide as a, getConfiguredInvokeSrcs as c, isStateMachineLogic as d, runToQuiescence as f, bindChildMachineForProvide as i, inspectTransitions as l, traceTransitions as m, AgentMaxModelCallsExceededError as n, bindTextForProvide as o, serializeTraceEvent as p, AgentSnapshotDivergedError as r, createAgentRuntime as s, AGENT_TRACE_SCHEMA_VERSION as t, isAgentIdle as u };