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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/ai-sdk.cjs +30 -34
  2. package/dist/ai-sdk.d.cts +27 -31
  3. package/dist/ai-sdk.d.mts +27 -31
  4. package/dist/ai-sdk.mjs +27 -31
  5. package/dist/decision-CM9CYZ6t.cjs +519 -0
  6. package/dist/decision-DzSv1NpD.mjs +453 -0
  7. package/dist/event-log-store-Ba2fsc4E.mjs +576 -0
  8. package/dist/event-log-store-DQmImLYk.cjs +702 -0
  9. package/dist/event-log-store-DvOcs10J.d.cts +338 -0
  10. package/dist/event-log-store-DxyVgnas.d.mts +338 -0
  11. package/dist/index.cjs +429 -4272
  12. package/dist/index.d.cts +104 -1005
  13. package/dist/index.d.mts +104 -1005
  14. package/dist/index.mjs +389 -4202
  15. package/dist/log.cjs +392 -0
  16. package/dist/log.d.cts +32 -0
  17. package/dist/log.d.mts +32 -0
  18. package/dist/log.mjs +371 -0
  19. package/dist/machines.cjs +27 -16
  20. package/dist/machines.d.cts +1 -1
  21. package/dist/machines.d.mts +1 -1
  22. package/dist/machines.mjs +27 -16
  23. package/dist/openai.cjs +16 -15
  24. package/dist/openai.d.cts +9 -9
  25. package/dist/openai.d.mts +9 -9
  26. package/dist/openai.mjs +10 -9
  27. package/dist/otel.d.cts +1 -1
  28. package/dist/otel.d.mts +1 -1
  29. package/dist/{run-agent-Ck7Sa3Tb.d.mts → run-agent-BOfoJg5B.d.cts} +86 -387
  30. package/dist/{run-agent-DWqA8KiI.d.cts → run-agent-CG343kFs.d.mts} +86 -387
  31. package/dist/run-agent-Cb1-LS5P.cjs +1783 -0
  32. package/dist/run-agent-DvDQd4If.mjs +1669 -0
  33. package/dist/{setup-agent-CeO2fARv.mjs → setup-agent-CFW-jPI8.mjs} +9 -20
  34. package/dist/{setup-agent-Dl2VELLi.cjs → setup-agent-CPf7MxnG.cjs} +20 -37
  35. package/dist/testing.cjs +1088 -0
  36. package/dist/testing.d.cts +617 -0
  37. package/dist/testing.d.mts +617 -0
  38. package/dist/testing.mjs +1079 -0
  39. package/dist/{text-logic-DJBK3LbG.d.mts → text-logic-BFEOxyxv.d.cts} +71 -147
  40. package/dist/text-logic-Bl_ZRxDi.mjs +833 -0
  41. package/dist/text-logic-C8xyr6Ag.cjs +1091 -0
  42. package/dist/{text-logic-C7bSnH_B.d.cts → text-logic-hZSiRUo1.d.mts} +71 -147
  43. package/dist/{types-CjHk5OdH.d.cts → types-B1Qs2q3q.d.cts} +2 -1
  44. package/dist/{types-DWA6X9R-.d.mts → types-tA3SdczG.d.mts} +2 -1
  45. package/dist/usage-BdEjFuIi.mjs +5 -0
  46. package/dist/usage-efOQPdf_.cjs +10 -0
  47. package/dist/utils-C2DPqomn.d.mts +109 -0
  48. package/dist/utils-ceStNCOh.d.cts +109 -0
  49. package/package.json +26 -5
  50. package/readme.md +5 -3
  51. package/skills/generate-machine/SKILL.md +4 -6
  52. package/dist/decision-BBZ2jMO1.mjs +0 -1490
  53. package/dist/decision-Cv6SfNWl.cjs +0 -1856
package/dist/log.mjs ADDED
@@ -0,0 +1,371 @@
1
+ import { _ as getUsageFromEvents, a as AgentEventLogError, b as replay, c as NonSerializableAgentEventError, d as assertJsonSerializable, g as getSnapshotStateHash, h as getLogExecutionId, i as AGENT_INIT_EVENT_TYPE, l as agentCallOccurrence, m as forkEventLog, n as createInMemoryEventLogStore, o as AgentMachineVersionMismatchError, p as createReplayEntry, r as AGENT_EVENT_SCHEMA_VERSION, s as AgentReplayDivergenceError, t as AgentEventLogConflictError, u as assertAgentLogEntry, v as initEntry, x as validateReplayEntries, y as rebindActorSession } from "./event-log-store-Ba2fsc4E.mjs";
2
+ //#region src/event-log-store-conformance.ts
3
+ /**
4
+ * The runner-agnostic conformance suite for {@link AgentEventLogStore}
5
+ * implementations. Split out of `event-log-store.ts` so the store contract and
6
+ * the reference implementation stay readable.
7
+ *
8
+ * Every case throws a descriptive `Error` on the first violation, so any test
9
+ * runner (or a plain script) can drive it against its own store. Pass a
10
+ * `{ describe, it }` harness to register one test per case instead.
11
+ * @module
12
+ */
13
+ function fail(message) {
14
+ throw new Error(`event-log-store conformance: ${message}`);
15
+ }
16
+ function entry(index, type, metadata) {
17
+ return {
18
+ schemaVersion: 1,
19
+ id: `evt_${index}`,
20
+ index,
21
+ recordedAt: "2026-01-01T00:00:00.000Z",
22
+ machineId: "conformance",
23
+ machineVersion: "v1",
24
+ event: {
25
+ type,
26
+ seq: index
27
+ },
28
+ ...metadata !== void 0 ? { metadata } : {}
29
+ };
30
+ }
31
+ function entriesFrom(start, count) {
32
+ return Array.from({ length: count }, (_unused, i) => entry(start + i, `e${start + i}`));
33
+ }
34
+ function assertJsonEqual(actual, expected, message) {
35
+ const a = JSON.stringify(actual);
36
+ const b = JSON.stringify(expected);
37
+ if (a !== b) fail(`${message} (expected ${b}, got ${a})`);
38
+ }
39
+ const cases = [
40
+ {
41
+ name: "an unknown thread reads empty and has length 0",
42
+ async run(create) {
43
+ const store = await create();
44
+ assertJsonEqual(await store.read("missing"), [], "read of an unknown thread must be empty");
45
+ if (await store.length("missing") !== 0) fail("length of an unknown thread must be 0");
46
+ }
47
+ },
48
+ {
49
+ name: "a single append reads back verbatim",
50
+ async run(create) {
51
+ const store = await create();
52
+ const e = entry(0, "start", { label: "first" });
53
+ await store.append({
54
+ threadId: "t",
55
+ expectedIndex: 0,
56
+ entries: [e]
57
+ });
58
+ assertJsonEqual(await store.read("t"), [e], "read must return the appended entry");
59
+ if (await store.length("t") !== 1) fail("length after a single append must be 1");
60
+ }
61
+ },
62
+ {
63
+ name: "a second append extends the log in order",
64
+ async run(create) {
65
+ const store = await create();
66
+ await store.append({
67
+ threadId: "t",
68
+ expectedIndex: 0,
69
+ entries: entriesFrom(0, 3)
70
+ });
71
+ await store.append({
72
+ threadId: "t",
73
+ expectedIndex: 3,
74
+ entries: entriesFrom(3, 2)
75
+ });
76
+ if (await store.length("t") !== 5) fail("length after appending 3 then 2 entries must be 5");
77
+ assertJsonEqual((await store.read("t")).map((e) => e.index), [
78
+ 0,
79
+ 1,
80
+ 2,
81
+ 3,
82
+ 4
83
+ ], "reads must return entries in contiguous log order");
84
+ }
85
+ },
86
+ {
87
+ name: "a non-contiguous entry.index is misuse, not a race",
88
+ async run(create) {
89
+ const store = await create();
90
+ let caught;
91
+ try {
92
+ await store.append({
93
+ threadId: "t",
94
+ expectedIndex: 0,
95
+ entries: [entry(1, "gap")]
96
+ });
97
+ } catch (error) {
98
+ caught = error;
99
+ }
100
+ if (!(caught instanceof Error) || caught instanceof AgentEventLogConflictError) fail("a non-contiguous entry.index must throw a plain Error, not a conflict");
101
+ }
102
+ },
103
+ {
104
+ name: "a stale expectedIndex conflicts with correct fields",
105
+ async run(create) {
106
+ const store = await create();
107
+ await store.append({
108
+ threadId: "t",
109
+ expectedIndex: 0,
110
+ entries: [entry(0, "a")]
111
+ });
112
+ let caught;
113
+ try {
114
+ await store.append({
115
+ threadId: "t",
116
+ expectedIndex: 0,
117
+ entries: [entry(0, "b")]
118
+ });
119
+ } catch (error) {
120
+ caught = error;
121
+ }
122
+ if (!(caught instanceof AgentEventLogConflictError)) fail("a stale expectedIndex must throw AgentEventLogConflictError");
123
+ if (caught.threadId !== "t" || caught.expectedIndex !== 0 || caught.actualIndex !== 1) fail("conflict error must carry threadId, expectedIndex, and the actual index");
124
+ }
125
+ },
126
+ {
127
+ name: "entry ids are unique within a thread",
128
+ async run(create) {
129
+ const store = await create();
130
+ await store.append({
131
+ threadId: "t",
132
+ expectedIndex: 0,
133
+ entries: [entry(0, "a")]
134
+ });
135
+ let caught;
136
+ try {
137
+ await store.append({
138
+ threadId: "t",
139
+ expectedIndex: 1,
140
+ entries: [{
141
+ ...entry(1, "b"),
142
+ id: "evt_0"
143
+ }]
144
+ });
145
+ } catch (error) {
146
+ caught = error;
147
+ }
148
+ if (!(caught instanceof Error) || caught instanceof AgentEventLogConflictError) fail("a duplicate event id within a thread must throw a plain Error");
149
+ }
150
+ },
151
+ {
152
+ name: "concurrent appends at the same index have exactly one winner",
153
+ async run(create) {
154
+ const store = await create();
155
+ await store.append({
156
+ threadId: "t",
157
+ expectedIndex: 0,
158
+ entries: [entry(0, "a")]
159
+ });
160
+ const results = await Promise.allSettled([store.append({
161
+ threadId: "t",
162
+ expectedIndex: 1,
163
+ entries: [entry(1, "w-a")]
164
+ }), store.append({
165
+ threadId: "t",
166
+ expectedIndex: 1,
167
+ entries: [entry(1, "w-b")]
168
+ })]);
169
+ const winners = results.filter((r) => r.status === "fulfilled");
170
+ const conflicts = results.filter((r) => r.status === "rejected" && r.reason instanceof AgentEventLogConflictError);
171
+ if (winners.length !== 1) fail(`exactly one racing append must win, got ${winners.length}`);
172
+ if (conflicts.length !== 1) fail(`exactly one racing append must reject with a conflict, got ${conflicts.length}`);
173
+ if (await store.length("t") !== 2) fail("after the race the log length must be 2");
174
+ }
175
+ },
176
+ {
177
+ name: "read({ from }) is incrementally correct",
178
+ async run(create) {
179
+ const store = await create();
180
+ await store.append({
181
+ threadId: "t",
182
+ expectedIndex: 0,
183
+ entries: entriesFrom(0, 5)
184
+ });
185
+ assertJsonEqual((await store.read("t", { from: 2 })).map((e) => e.index), [
186
+ 2,
187
+ 3,
188
+ 4
189
+ ], "read({ from }) must skip entries below `from`");
190
+ assertJsonEqual(await store.read("t", { from: 5 }), [], "read({ from }) at the log length must be empty");
191
+ }
192
+ },
193
+ {
194
+ name: "threads are isolated and grow independently",
195
+ async run(create) {
196
+ const store = await create();
197
+ await store.append({
198
+ threadId: "a",
199
+ expectedIndex: 0,
200
+ entries: [entry(0, "x")]
201
+ });
202
+ if (await store.length("b") !== 0) fail("an append to one thread must not create another");
203
+ await store.append({
204
+ threadId: "b",
205
+ expectedIndex: 0,
206
+ entries: entriesFrom(0, 2)
207
+ });
208
+ if (await store.length("a") !== 1 || await store.length("b") !== 2) fail("threads must grow independently");
209
+ }
210
+ },
211
+ {
212
+ name: "metadata round-trips verbatim",
213
+ async run(create) {
214
+ const store = await create();
215
+ const metadata = {
216
+ source: "user",
217
+ nested: { attempt: 2 },
218
+ tags: ["x", "y"]
219
+ };
220
+ await store.append({
221
+ threadId: "t",
222
+ expectedIndex: 0,
223
+ entries: [entry(0, "e", metadata)]
224
+ });
225
+ assertJsonEqual((await store.read("t"))[0].metadata, metadata, "metadata must round-trip verbatim");
226
+ }
227
+ },
228
+ {
229
+ name: "stored entries are isolated from caller mutation",
230
+ async run(create) {
231
+ const store = await create();
232
+ const e = entry(0, "e", { tags: ["a"] });
233
+ await store.append({
234
+ threadId: "t",
235
+ expectedIndex: 0,
236
+ entries: [e]
237
+ });
238
+ e.metadata.tags.push("mutated-after-append");
239
+ e.event.seq = 999;
240
+ const read = await store.read("t");
241
+ read[0].metadata.tags.push("mutated-after-read");
242
+ read[0].event.seq = 888;
243
+ const reread = await store.read("t");
244
+ assertJsonEqual(reread[0].metadata, { tags: ["a"] }, "stored entry must be isolated from post-append and post-read mutation");
245
+ assertJsonEqual(reread[0].event, {
246
+ type: "e",
247
+ seq: 0
248
+ }, "stored event must be isolated from mutation of an appended or read copy");
249
+ }
250
+ },
251
+ {
252
+ name: "fork copies a prefix and then diverges independently",
253
+ async run(create) {
254
+ const store = await create();
255
+ await store.append({
256
+ threadId: "src",
257
+ expectedIndex: 0,
258
+ entries: entriesFrom(0, 3)
259
+ });
260
+ await store.fork({
261
+ threadId: "src",
262
+ newThreadId: "fork-full"
263
+ });
264
+ if (await store.length("fork-full") !== 3) fail("fork with the default upToIndex must copy the full log");
265
+ await store.fork({
266
+ threadId: "src",
267
+ newThreadId: "fork-1",
268
+ upToIndex: 1
269
+ });
270
+ assertJsonEqual((await store.read("fork-1")).map((e) => e.index), [0], "fork with upToIndex 1 must copy only entry 0");
271
+ await store.fork({
272
+ threadId: "src",
273
+ newThreadId: "fork-2",
274
+ upToIndex: 2
275
+ });
276
+ assertJsonEqual((await store.read("fork-2")).map((e) => e.id), ["evt_0", "evt_1"], "fork with upToIndex 2 must copy entries 0 and 1 only");
277
+ await store.append({
278
+ threadId: "fork-1",
279
+ expectedIndex: 1,
280
+ entries: [entry(1, "branch")]
281
+ });
282
+ assertJsonEqual((await store.read("fork-1")).map((e) => e.event.type), ["e0", "branch"], "a forked thread must append independently from its copied length");
283
+ if (await store.length("src") !== 3) fail("appending to a fork must not touch the source thread");
284
+ }
285
+ },
286
+ {
287
+ name: "fork rejects a non-empty target, an unknown source, and an out-of-range cutoff (0 included)",
288
+ async run(create) {
289
+ const store = await create();
290
+ await store.append({
291
+ threadId: "src",
292
+ expectedIndex: 0,
293
+ entries: entriesFrom(0, 3)
294
+ });
295
+ await store.fork({
296
+ threadId: "src",
297
+ newThreadId: "fork-full"
298
+ });
299
+ let caughtExisting;
300
+ try {
301
+ await store.fork({
302
+ threadId: "src",
303
+ newThreadId: "fork-full"
304
+ });
305
+ } catch (error) {
306
+ caughtExisting = error;
307
+ }
308
+ if (!(caughtExisting instanceof Error) || caughtExisting instanceof AgentEventLogConflictError) fail("forking onto a non-empty thread must reject with a plain Error");
309
+ let caughtUnknown;
310
+ try {
311
+ await store.fork({
312
+ threadId: "nope",
313
+ newThreadId: "fork-nope"
314
+ });
315
+ } catch (error) {
316
+ caughtUnknown = error;
317
+ }
318
+ if (!(caughtUnknown instanceof Error) || caughtUnknown instanceof AgentEventLogConflictError) fail("forking an unknown source thread must reject with a plain Error");
319
+ let caughtRange;
320
+ try {
321
+ await store.fork({
322
+ threadId: "src",
323
+ newThreadId: "fork-range",
324
+ upToIndex: 99
325
+ });
326
+ } catch (error) {
327
+ caughtRange = error;
328
+ }
329
+ if (!(caughtRange instanceof Error) || caughtRange instanceof AgentEventLogConflictError) fail("forking past the source length must reject with a plain Error");
330
+ let caughtZero;
331
+ try {
332
+ await store.fork({
333
+ threadId: "src",
334
+ newThreadId: "fork-zero",
335
+ upToIndex: 0
336
+ });
337
+ } catch (error) {
338
+ caughtZero = error;
339
+ }
340
+ if (!(caughtZero instanceof Error) || caughtZero instanceof AgentEventLogConflictError) fail("forking with upToIndex 0 must reject with a plain Error");
341
+ if (await store.length("fork-zero") !== 0) fail("a rejected fork must not create the target thread");
342
+ }
343
+ }
344
+ ];
345
+ /**
346
+ * Validates a store against the reference's semantics: empty read + zero length
347
+ * for unknown threads; single and multi-entry append; contiguity misuse guard;
348
+ * stale-`expectedIndex` conflict with correct fields; an interleaved concurrent
349
+ * append race (exactly one winner); entry-id uniqueness; `read({ from })`
350
+ * incremental correctness; thread isolation; metadata round-trip; deep-copy
351
+ * isolation on append and read; and the full fork contract.
352
+ *
353
+ * Without a `harness`, every case runs in sequence and the first violation
354
+ * throws. With one, each case is registered as its own test:
355
+ *
356
+ * ```ts
357
+ * import { describe, it } from 'vitest';
358
+ * assertEventLogStoreConformance(createMyStore, { describe, it });
359
+ * ```
360
+ */
361
+ async function assertEventLogStoreConformance(create, harness) {
362
+ if (harness) {
363
+ harness.describe("AgentEventLogStore conformance", () => {
364
+ for (const testCase of cases) harness.it(testCase.name, () => testCase.run(create));
365
+ });
366
+ return;
367
+ }
368
+ for (const testCase of cases) await testCase.run(create);
369
+ }
370
+ //#endregion
371
+ export { AGENT_EVENT_SCHEMA_VERSION, AGENT_INIT_EVENT_TYPE, AgentEventLogConflictError, AgentEventLogError, AgentMachineVersionMismatchError, AgentReplayDivergenceError, NonSerializableAgentEventError, agentCallOccurrence, assertAgentLogEntry, assertEventLogStoreConformance, assertJsonSerializable, createInMemoryEventLogStore, createReplayEntry, forkEventLog, getLogExecutionId, getSnapshotStateHash, getUsageFromEvents, initEntry, rebindActorSession, replay, validateReplayEntries };
package/dist/machines.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_setup_agent = require("./setup-agent-Dl2VELLi.cjs");
2
+ const require_setup_agent = require("./setup-agent-CPf7MxnG.cjs");
3
3
  //#region src/machines/internal.ts
4
4
  /** The builtin inline text request every preset lowers a request entry to. */
5
5
  const GENERATE_TEXT_SRC = "agent.generateText";
@@ -81,6 +81,14 @@ function machineActors(entries) {
81
81
  function entrySrc(name, entry) {
82
82
  return isMachineEntry(entry) ? name : GENERATE_TEXT_SRC;
83
83
  }
84
+ /**
85
+ * The value an entry's invoke produced: a child machine's output as-is, or the
86
+ * validated `result` off the `{ result, messages }` a text request
87
+ * resolves with. @internal
88
+ */
89
+ function entryOutput(entry, output) {
90
+ return isMachineEntry(entry) ? output : output.result;
91
+ }
84
92
  /** Builds an entry's invoke `input` from the current prompt. @internal */
85
93
  function entryInput(name, entry, defaultModel, prompt) {
86
94
  if (isMachineEntry(entry)) return entry.input ? entry.input({ prompt }) : { prompt };
@@ -177,7 +185,7 @@ function createToolLoopMachine(config) {
177
185
  }),
178
186
  onDone: ({ output }) => ({
179
187
  target: "done",
180
- context: { result: output }
188
+ context: { result: output.result }
181
189
  })
182
190
  } },
183
191
  done: {
@@ -247,9 +255,9 @@ function createSequentialMachine(config) {
247
255
  context: {
248
256
  results: {
249
257
  ...context.results,
250
- [step.name]: output
258
+ [step.name]: output.result
251
259
  },
252
- previous: output
260
+ previous: output.result
253
261
  }
254
262
  })
255
263
  } };
@@ -357,7 +365,7 @@ function createRouterMachine(config) {
357
365
  input: ({ context }) => entryInput(name, entry, model, context.prompt),
358
366
  onDone: ({ output }) => ({
359
367
  target: "done",
360
- context: { result: output }
368
+ context: { result: entryOutput(entry, output) }
361
369
  })
362
370
  } };
363
371
  const machineConfig = {
@@ -423,7 +431,7 @@ function createParallelMachine(config) {
423
431
  target: "done",
424
432
  context: { results: {
425
433
  ...context.results,
426
- [name]: output
434
+ [name]: entryOutput(entry, output)
427
435
  } }
428
436
  })
429
437
  } },
@@ -516,14 +524,17 @@ function createLoopMachine(config) {
516
524
  id: "body",
517
525
  src: entrySrc("body", body),
518
526
  input: ({ context }) => entryInput("body", body, model, body.prompt ? body.prompt(loopState(context)) : context.prompt),
519
- onDone: ({ context, output }) => ({
520
- target: "checking",
521
- context: {
522
- iterations: context.iterations + 1,
523
- results: [...context.results, output],
524
- last: output
525
- }
526
- })
527
+ onDone: ({ context, output }) => {
528
+ const result = entryOutput(body, output);
529
+ return {
530
+ target: "checking",
531
+ context: {
532
+ iterations: context.iterations + 1,
533
+ results: [...context.results, result],
534
+ last: result
535
+ }
536
+ };
537
+ }
527
538
  } },
528
539
  checking: {
529
540
  type: "choice",
@@ -643,7 +654,7 @@ function createSupervisorMachine(config) {
643
654
  context: {
644
655
  results: {
645
656
  ...context.results,
646
- [name]: output
657
+ [name]: entryOutput(entry, output)
647
658
  },
648
659
  turns: context.turns + 1
649
660
  }
@@ -746,7 +757,7 @@ function createHandoffMachine(config) {
746
757
  input: ({ context }) => entryInput(name, entry, model, context.message),
747
758
  onDone: ({ output }) => ({
748
759
  target: "waiting",
749
- context: { reply: output }
760
+ context: { reply: entryOutput(entry, output) }
750
761
  })
751
762
  } };
752
763
  const machineConfig = {
@@ -1,4 +1,4 @@
1
- import { _ as StandardSchemaV1, s as AgentTools } from "./types-CjHk5OdH.cjs";
1
+ import { _ as StandardSchemaV1, s as AgentTools } from "./types-B1Qs2q3q.cjs";
2
2
  import { AnyActorRef, AnyStateMachine, EventObject, MachineContext, MetaObject, StateMachine, StateSchema, StateValue } from "xstate";
3
3
 
4
4
  //#region src/machines/internal.d.ts
@@ -1,4 +1,4 @@
1
- import { _ as StandardSchemaV1, s as AgentTools } from "./types-DWA6X9R-.mjs";
1
+ import { _ as StandardSchemaV1, s as AgentTools } from "./types-tA3SdczG.mjs";
2
2
  import { AnyActorRef, AnyStateMachine, EventObject, MachineContext, MetaObject, StateMachine, StateSchema, StateValue } from "xstate";
3
3
 
4
4
  //#region src/machines/internal.d.ts
package/dist/machines.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { r as setupAgent } from "./setup-agent-CeO2fARv.mjs";
1
+ import { r as setupAgent } from "./setup-agent-CFW-jPI8.mjs";
2
2
  //#region src/machines/internal.ts
3
3
  /** The builtin inline text request every preset lowers a request entry to. */
4
4
  const GENERATE_TEXT_SRC = "agent.generateText";
@@ -80,6 +80,14 @@ function machineActors(entries) {
80
80
  function entrySrc(name, entry) {
81
81
  return isMachineEntry(entry) ? name : GENERATE_TEXT_SRC;
82
82
  }
83
+ /**
84
+ * The value an entry's invoke produced: a child machine's output as-is, or the
85
+ * validated `result` off the `{ result, messages }` a text request
86
+ * resolves with. @internal
87
+ */
88
+ function entryOutput(entry, output) {
89
+ return isMachineEntry(entry) ? output : output.result;
90
+ }
83
91
  /** Builds an entry's invoke `input` from the current prompt. @internal */
84
92
  function entryInput(name, entry, defaultModel, prompt) {
85
93
  if (isMachineEntry(entry)) return entry.input ? entry.input({ prompt }) : { prompt };
@@ -176,7 +184,7 @@ function createToolLoopMachine(config) {
176
184
  }),
177
185
  onDone: ({ output }) => ({
178
186
  target: "done",
179
- context: { result: output }
187
+ context: { result: output.result }
180
188
  })
181
189
  } },
182
190
  done: {
@@ -246,9 +254,9 @@ function createSequentialMachine(config) {
246
254
  context: {
247
255
  results: {
248
256
  ...context.results,
249
- [step.name]: output
257
+ [step.name]: output.result
250
258
  },
251
- previous: output
259
+ previous: output.result
252
260
  }
253
261
  })
254
262
  } };
@@ -356,7 +364,7 @@ function createRouterMachine(config) {
356
364
  input: ({ context }) => entryInput(name, entry, model, context.prompt),
357
365
  onDone: ({ output }) => ({
358
366
  target: "done",
359
- context: { result: output }
367
+ context: { result: entryOutput(entry, output) }
360
368
  })
361
369
  } };
362
370
  const machineConfig = {
@@ -422,7 +430,7 @@ function createParallelMachine(config) {
422
430
  target: "done",
423
431
  context: { results: {
424
432
  ...context.results,
425
- [name]: output
433
+ [name]: entryOutput(entry, output)
426
434
  } }
427
435
  })
428
436
  } },
@@ -515,14 +523,17 @@ function createLoopMachine(config) {
515
523
  id: "body",
516
524
  src: entrySrc("body", body),
517
525
  input: ({ context }) => entryInput("body", body, model, body.prompt ? body.prompt(loopState(context)) : context.prompt),
518
- onDone: ({ context, output }) => ({
519
- target: "checking",
520
- context: {
521
- iterations: context.iterations + 1,
522
- results: [...context.results, output],
523
- last: output
524
- }
525
- })
526
+ onDone: ({ context, output }) => {
527
+ const result = entryOutput(body, output);
528
+ return {
529
+ target: "checking",
530
+ context: {
531
+ iterations: context.iterations + 1,
532
+ results: [...context.results, result],
533
+ last: result
534
+ }
535
+ };
536
+ }
526
537
  } },
527
538
  checking: {
528
539
  type: "choice",
@@ -642,7 +653,7 @@ function createSupervisorMachine(config) {
642
653
  context: {
643
654
  results: {
644
655
  ...context.results,
645
- [name]: output
656
+ [name]: entryOutput(entry, output)
646
657
  },
647
658
  turns: context.turns + 1
648
659
  }
@@ -745,7 +756,7 @@ function createHandoffMachine(config) {
745
756
  input: ({ context }) => entryInput(name, entry, model, context.message),
746
757
  onDone: ({ output }) => ({
747
758
  target: "waiting",
748
- context: { reply: output }
759
+ context: { reply: entryOutput(entry, output) }
749
760
  })
750
761
  } };
751
762
  const machineConfig = {