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

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 +1222 -0
  36. package/dist/testing.d.cts +602 -0
  37. package/dist/testing.d.mts +602 -0
  38. package/dist/testing.mjs +1212 -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-C1p37X_c.d.mts +209 -0
  48. package/dist/utils-RalRn9sk.d.cts +209 -0
  49. package/package.json +24 -4
  50. package/readme.md +2 -2
  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
@@ -1,1856 +0,0 @@
1
- require("./validate.cjs");
2
- let xstate = require("xstate");
3
- //#region src/errors.ts
4
- /**
5
- * Base class for every error this package throws. Carries a stable, kebab-case
6
- * `code` so hosts can branch on the failure without `instanceof` (useful across
7
- * bundle/realm boundaries and after serialization).
8
- *
9
- * @example
10
- * ```ts
11
- * catch (error) {
12
- * if (error instanceof AgentError && error.code === 'agent-idle') { ... }
13
- * }
14
- * ```
15
- */
16
- var AgentError = class extends Error {
17
- code;
18
- constructor(code, message, options) {
19
- super(message, options);
20
- this.name = "AgentError";
21
- this.code = code;
22
- }
23
- };
24
- /**
25
- * A model call ran out of output tokens before it produced anything usable.
26
- * Core never throws this: it is the adapter's signal that a `'length'` finish
27
- * reason left nothing to return — a structured request whose envelope never
28
- * closed, say. A text request that WAS cut off still returns its text, with
29
- * `finishReason: 'length'`.
30
- *
31
- * The code is `'truncated'`, so an invoke's `onError` can branch on it without
32
- * `instanceof`:
33
- *
34
- * @example
35
- * ```ts
36
- * onError: [
37
- * { guard: ({ event }) => event.error.code === 'truncated', target: 'retryShorter' },
38
- * { target: 'failed' },
39
- * ]
40
- * ```
41
- */
42
- var AgentTruncatedError = class extends AgentError {
43
- /** The request that was cut off — `AgentTextRequest.name`. */
44
- requestName;
45
- /** The durable invoke id of the request, when the host knows it. */
46
- requestId;
47
- /** Whatever the model did produce before it hit the limit, if anything. */
48
- partialOutput;
49
- constructor(message, options) {
50
- super("truncated", message, { cause: options.cause });
51
- this.name = "AgentTruncatedError";
52
- this.requestName = options.requestName;
53
- if (options.requestId !== void 0) this.requestId = options.requestId;
54
- if (options.partialOutput !== void 0) this.partialOutput = options.partialOutput;
55
- }
56
- };
57
- //#endregion
58
- //#region src/messages.ts
59
- /** Ordinary machine event emitted after a request returns framework-native messages. */
60
- const AGENT_MESSAGES_EVENT_TYPE = "agent.messages";
61
- /** Runtime schema for the framework-neutral `agent.messages` envelope. */
62
- const agentMessagesEventSchema = { "~standard": {
63
- version: 1,
64
- vendor: "statelyai-agent",
65
- validate(value) {
66
- if (!value || typeof value !== "object") return { issues: [{ message: "Expected an 'agent.messages' payload object" }] };
67
- const event = value;
68
- const issues = [];
69
- if (typeof event.request !== "string") issues.push({
70
- message: "Expected a string",
71
- path: ["request"]
72
- });
73
- if (typeof event.actorId !== "string") issues.push({
74
- message: "Expected a string",
75
- path: ["actorId"]
76
- });
77
- if (!Array.isArray(event.messages)) issues.push({
78
- message: "Expected an array",
79
- path: ["messages"]
80
- });
81
- return issues.length > 0 ? { issues } : { value: event };
82
- }
83
- } };
84
- function appendMessages(options) {
85
- const key = options?.key ?? "messages";
86
- return (({ context, event }) => {
87
- const current = context[key];
88
- const messages = event.messages;
89
- return { context: { [key]: [...Array.isArray(current) ? current : [], ...messages ?? []] } };
90
- });
91
- }
92
- const KNOWN_PART_TYPES = new Set([
93
- "text",
94
- "image",
95
- "file",
96
- "tool-call",
97
- "tool-result"
98
- ]);
99
- const USER_PART_TYPES = new Set([
100
- "text",
101
- "image",
102
- "file"
103
- ]);
104
- const ASSISTANT_PART_TYPES = new Set([
105
- "text",
106
- "file",
107
- "tool-call",
108
- "tool-result"
109
- ]);
110
- const TOOL_PART_TYPES = new Set(["tool-result"]);
111
- const TOOL_RESULT_CONTENT_PART_TYPES = new Set(["text", "image"]);
112
- function isKnownPart(part) {
113
- const type = part && typeof part === "object" ? part.type : void 0;
114
- return typeof type === "string" && KNOWN_PART_TYPES.has(type);
115
- }
116
- function requireString(part, type, field) {
117
- return typeof part[field] === "string" ? void 0 : `${type} part requires a string "${field}"`;
118
- }
119
- function isMediaData(value) {
120
- return typeof value === "string" || value instanceof Uint8Array || value instanceof ArrayBuffer || value instanceof URL;
121
- }
122
- function requireMediaData(part, type, field) {
123
- return isMediaData(part[field]) ? void 0 : `${type} part requires "${field}" to be a string, Uint8Array, ArrayBuffer, or URL`;
124
- }
125
- const TOOL_RESULT_OUTPUT_TYPES = new Set([
126
- "text",
127
- "json",
128
- "error-text",
129
- "error-json",
130
- "content"
131
- ]);
132
- function validateToolResultOutput(output) {
133
- if (!output || typeof output !== "object" || Array.isArray(output)) return "tool-result part requires an \"output\" object";
134
- const record = output;
135
- const outputType = record.type;
136
- if (typeof outputType !== "string" || !TOOL_RESULT_OUTPUT_TYPES.has(outputType)) return `Unknown tool-result output type: ${JSON.stringify(outputType)}`;
137
- if (outputType === "text" || outputType === "error-text") {
138
- if (typeof record.value !== "string") return `tool-result output of type "${outputType}" requires a string "value"`;
139
- return;
140
- }
141
- if (outputType === "content") {
142
- if (!Array.isArray(record.value)) return "tool-result output of type \"content\" requires an array \"value\"";
143
- for (const contentPart of record.value) {
144
- const error = validatePart(contentPart, TOOL_RESULT_CONTENT_PART_TYPES, "tool-result output content");
145
- if (error) return error;
146
- }
147
- return;
148
- }
149
- return record.value !== void 0 ? void 0 : `tool-result output of type "${outputType}" requires a "value"`;
150
- }
151
- function validatePart(part, allowedTypes, location) {
152
- if (!isKnownPart(part)) {
153
- const type = part && typeof part === "object" ? part.type : void 0;
154
- return `Unknown message part type: ${JSON.stringify(type)}`;
155
- }
156
- if (!allowedTypes.has(part.type)) return `${location} does not allow "${part.type}" parts`;
157
- const record = part;
158
- switch (record.type) {
159
- case "text": return requireString(record, "text", "text");
160
- case "image": return requireMediaData(record, "image", "image");
161
- case "file": return requireMediaData(record, "file", "data") ?? requireString(record, "file", "mediaType");
162
- case "tool-call": return requireString(record, "tool-call", "toolCallId") ?? requireString(record, "tool-call", "toolName") ?? ("input" in record ? void 0 : "tool-call part requires an \"input\" value");
163
- case "tool-result": return requireString(record, "tool-result", "toolCallId") ?? requireString(record, "tool-result", "toolName") ?? validateToolResultOutput(record.output);
164
- default: return;
165
- }
166
- }
167
- function validatePartsArray(content, allowedTypes, location) {
168
- if (!Array.isArray(content)) return "Expected content to be a string or an array of parts";
169
- for (const part of content) {
170
- const error = validatePart(part, allowedTypes, location);
171
- if (error) return error;
172
- }
173
- }
174
- /**
175
- * A {@link StandardSchemaV1} validating an `AgentMessage[]` context field —
176
- * checks that every message has a known `role` (`system`/`user`/`assistant`/
177
- * `tool`) and that `content` is either a string (where the role allows it) or
178
- * an array of role-appropriate parts whose required fields and media payloads
179
- * have the right runtime types (extra fields are allowed). Use it directly as
180
- * a context schema's `messages` field when authoring with `createAgentSchemas`
181
- * or `fromConfig`. A zod object cannot nest a Standard Schema, so in a zod
182
- * context schema write `z.custom<AgentMessage[]>(isAgentMessages)`, which runs
183
- * this same validator (see {@link isAgentMessages}).
184
- */
185
- const messagesSchema = { "~standard": {
186
- version: 1,
187
- vendor: "statelyai-agent",
188
- validate(value) {
189
- if (!Array.isArray(value)) return { issues: [{ message: "Expected an array of agent messages" }] };
190
- for (const message of value) {
191
- if (!message || typeof message !== "object") return { issues: [{ message: "Expected an array of agent messages" }] };
192
- const role = message.role;
193
- const content = message.content;
194
- if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") return { issues: [{ message: `Unknown message role: ${JSON.stringify(role)}` }] };
195
- if (role === "system") {
196
- if (typeof content !== "string") return { issues: [{ message: "system message content must be a string" }] };
197
- continue;
198
- }
199
- if (role === "tool") {
200
- const error = validatePartsArray(content, TOOL_PART_TYPES, "tool message content");
201
- if (error) return { issues: [{ message: error }] };
202
- continue;
203
- }
204
- if (typeof content === "string") continue;
205
- const error = validatePartsArray(content, role === "user" ? USER_PART_TYPES : ASSISTANT_PART_TYPES, `${role} message content`);
206
- if (error) return { issues: [{ message: error }] };
207
- }
208
- return { value };
209
- }
210
- } };
211
- /**
212
- * Type guard over {@link messagesSchema}: `true` when `value` is a valid
213
- * `AgentMessage[]`. The one-liner for zod context schemas:
214
- * `messages: z.custom<AgentMessage[]>(isAgentMessages)`.
215
- */
216
- function isAgentMessages(value) {
217
- const result = messagesSchema["~standard"].validate(value);
218
- return !(result instanceof Promise) && !("issues" in result && result.issues);
219
- }
220
- //#endregion
221
- //#region src/utils.ts
222
- /** Returns the human-readable text carried by a message, ignoring non-text parts. */
223
- function getMessageText(message) {
224
- if (typeof message.content === "string") return message.content;
225
- const text = [];
226
- for (const part of message.content) {
227
- if (part.type === "text") {
228
- text.push(part.text);
229
- continue;
230
- }
231
- if (part.type !== "tool-result") continue;
232
- if (part.output.type === "text" || part.output.type === "error-text") text.push(part.output.value);
233
- else if (part.output.type === "content") text.push(...part.output.value.flatMap((item) => item.type === "text" ? [item.text] : []));
234
- }
235
- return text.join("\n");
236
- }
237
- /**
238
- * Walks a context value and returns the dot-paths of the first few values that
239
- * would NOT survive a JSON persist/resume round-trip (persist with XState's
240
- * `actor.getPersistedSnapshot()`, resume with `createActor(machine, { snapshot })`):
241
- * `Date`, `Map`, `Set`, `RegExp`, functions, `undefined`, `bigint`, class
242
- * instances (non-plain objects), and circular references. Plain objects,
243
- * arrays, and JSON primitives are walked/allowed. Returns `[]` for a
244
- * fully-JSON-safe value. Cheap and bounded (stops after `limit` findings) —
245
- * intended for a dev-only warning at the moment persistence matters.
246
- */
247
- function findNonSerializableContextPaths(context, limit = 5) {
248
- const paths = [];
249
- const seen = /* @__PURE__ */ new WeakSet();
250
- const walk = (value, path) => {
251
- if (paths.length >= limit) return;
252
- if (value === null) return;
253
- const type = typeof value;
254
- if (type === "string" || type === "number" || type === "boolean") return;
255
- if (type === "undefined" || type === "bigint" || type === "function" || type === "symbol") {
256
- paths.push(`${path} (${type})`);
257
- return;
258
- }
259
- const obj = value;
260
- if (seen.has(obj)) {
261
- paths.push(`${path} (circular)`);
262
- return;
263
- }
264
- if (Array.isArray(value)) {
265
- seen.add(obj);
266
- value.forEach((item, index) => walk(item, `${path}[${index}]`));
267
- seen.delete(obj);
268
- return;
269
- }
270
- const proto = Object.getPrototypeOf(value);
271
- if (proto === Object.prototype || proto === null) {
272
- seen.add(obj);
273
- for (const [key, item] of Object.entries(value)) walk(item, `${path}.${key}`);
274
- seen.delete(obj);
275
- return;
276
- }
277
- const ctorName = value.constructor?.name ?? "object";
278
- paths.push(`${path} (${ctorName})`);
279
- };
280
- walk(context, "context");
281
- return paths;
282
- }
283
- /**
284
- * A stable, dependency-free structural fingerprint of a machine — a short hex
285
- * `djb2` hash over its **structural** config only: state ids/nesting, transition
286
- * event types and targets, invoke `src`s, `initial`, and any other serializable
287
- * config fields. Function values (context/output builders, prompts, inline
288
- * guards/actions) are excluded entirely, so two machines that differ only in
289
- * their prompts or executors hash identically; adding/removing/retargeting a
290
- * state or transition changes the hash.
291
- *
292
- * Used by {@link runAgent} to stamp settled snapshots with a `version` and to
293
- * detect a structurally-edited machine on resume. It is a change detector, not
294
- * a cryptographic digest — collisions are possible but unlikely for real
295
- * configs. Declare `createMachine({ version })` to pin an explicit version instead.
296
- */
297
- function getMachineStructuralHash(machine) {
298
- return djb2Hex(stableStructuralString(machine.config));
299
- }
300
- /**
301
- * The version a machine is stamped with everywhere: an explicitly declared
302
- * `createMachine({ version })` when present, else its
303
- * {@link getMachineStructuralHash}. Single source of truth so `runAgent`,
304
- * `provideExecutors`/`traceTransitions` trace envelopes agree.
305
- * @internal
306
- */
307
- function resolveMachineVersion(machine) {
308
- return machine.version ?? getMachineStructuralHash(machine);
309
- }
310
- function stableStructuralString(value, seen = /* @__PURE__ */ new WeakSet()) {
311
- if (value === null) return "null";
312
- const type = typeof value;
313
- if (type === "function" || type === "undefined" || type === "symbol") return "";
314
- if (type === "string") return JSON.stringify(value);
315
- if (type === "number" || type === "boolean") return String(value);
316
- if (type === "bigint") return `${value}n`;
317
- const obj = value;
318
- if (seen.has(obj)) return "\"[circular]\"";
319
- seen.add(obj);
320
- let out;
321
- if (Array.isArray(value)) out = `[${value.map((item) => stableStructuralString(item, seen)).join(",")}]`;
322
- else {
323
- const parts = [];
324
- for (const key of Object.keys(obj).sort()) {
325
- const child = obj[key];
326
- const childType = typeof child;
327
- if (childType === "function" || childType === "undefined" || childType === "symbol") continue;
328
- parts.push(`${JSON.stringify(key)}:${stableStructuralString(child, seen)}`);
329
- }
330
- out = `{${parts.join(",")}}`;
331
- }
332
- seen.delete(obj);
333
- return out;
334
- }
335
- /**
336
- * djb2 → unsigned 32-bit → 8-char hex. No dependencies. A change detector, not
337
- * a cryptographic digest.
338
- *
339
- * @internal
340
- */
341
- function djb2Hex(input) {
342
- let hash = 5381;
343
- for (let i = 0; i < input.length; i++) hash = (hash << 5) + hash + input.charCodeAt(i) | 0;
344
- return (hash >>> 0).toString(16).padStart(8, "0");
345
- }
346
- /** Builds a {@link UserMessage} from a string or multimodal content parts. */
347
- function userMessage(content) {
348
- return {
349
- role: "user",
350
- content
351
- };
352
- }
353
- /** Builds an {@link AssistantMessage} from a string or content parts (text, files, tool calls/results). */
354
- function assistantMessage(content) {
355
- return {
356
- role: "assistant",
357
- content
358
- };
359
- }
360
- /** Builds a {@link SystemMessage}. */
361
- function systemMessage(content) {
362
- return {
363
- role: "system",
364
- content
365
- };
366
- }
367
- /** Builds a {@link ToolMessage} from one or more tool-result parts. */
368
- function toolMessage(content) {
369
- return {
370
- role: "tool",
371
- content
372
- };
373
- }
374
- /**
375
- * Returns the merged `meta` of a snapshot's active state(s) — the typed
376
- * replacement for the `Object.values(snapshot.getMeta())[0]` dance.
377
- *
378
- * `snapshot.getMeta()` is keyed by state id; a leaf machine has one active
379
- * state, but parallel/nested machines can have several. This shallow-merges
380
- * every active state's meta into one object and returns `{}` when no active
381
- * state declares meta.
382
- *
383
- * Merge order is fixed and does not depend on XState's internal node order:
384
- * entries are sorted by structural depth (the state node's distance from the
385
- * root, regardless of custom `id` strings), then by state id lexicographically,
386
- * and merged in that order. So a deeper state's key wins over an ancestor's,
387
- * and between equal-depth parallel siblings the later state id alphabetically
388
- * wins.
389
- *
390
- * The return type is recovered from the snapshot's own `getMeta()` type, so a
391
- * schema-typed machine (`setupAgent({ meta })`) yields the meta schema's
392
- * output type. Pass an explicit `TMeta` to override when the snapshot is
393
- * untyped (e.g. `AnyMachineSnapshot`).
394
- *
395
- * @example HITL: read the current state's interaction protocol off an idle
396
- * snapshot to render for a human.
397
- * ```ts
398
- * const { interaction } = getStateMeta(result.snapshot);
399
- * ```
400
- */
401
- function getStateMeta(snapshot) {
402
- const nodes = snapshot.nodes;
403
- const depthById = new Map(nodes?.map((node) => [node.id, node.path.length]));
404
- const depth = (id) => depthById.get(id) ?? id.split(".").length;
405
- const entries = Object.entries(snapshot.getMeta()).filter((entry) => entry[1] != null).sort(([a], [b]) => depth(a) - depth(b) || (a < b ? -1 : a > b ? 1 : 0));
406
- return Object.assign({}, ...entries.map(([, meta]) => meta));
407
- }
408
- /**
409
- * Renders a snapshot's state value as one deterministic string, so a log line,
410
- * a progress field, or a test assertion reads the same on every run.
411
- *
412
- * - An atomic state is its own name: `'writing'`.
413
- * - A nested state joins with dots: `{ review: { editing: 'draft' } }` →
414
- * `'review.editing.draft'`.
415
- * - A parallel state brackets its regions, sorted by region name, so key order
416
- * never leaks into the string: `{ p: { left: 'x', right: { a: 'b' } } }` →
417
- * `'p:{left.x,right.a.b}'`.
418
- *
419
- * Prefer this over `String(snapshot.value)`, which renders every non-atomic
420
- * value as `[object Object]`.
421
- *
422
- * @example
423
- * ```ts
424
- * onTransition: (snapshot) => console.log(getStatePath(snapshot));
425
- * ```
426
- */
427
- function getStatePath(snapshot) {
428
- return serializeStateValue(typeof snapshot === "object" && snapshot !== null && "value" in snapshot && ("status" in snapshot || typeof snapshot.matches === "function") ? snapshot.value : snapshot);
429
- }
430
- function serializeStateValue(value) {
431
- if (typeof value === "string") return value;
432
- const entries = Object.entries(value).map(([key, child]) => {
433
- const rendered = serializeStateValue(child);
434
- if (!rendered) return [key, key];
435
- return [key, `${key}${rendered.startsWith("{") ? ":" : "."}${rendered}`];
436
- }).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
437
- if (entries.length === 0) return "";
438
- if (entries.length === 1) return entries[0][1];
439
- return `{${entries.map(([, rendered]) => rendered).join(",")}}`;
440
- }
441
- /**
442
- * Structural guard for a {@link StandardSchemaV1}: `true` when `value` carries
443
- * the `~standard` marker. Used to tell an already-schema'd tool `inputSchema`
444
- * (a Zod/Valibot/… schema) apart from an SDK-specific schema wrapper that core
445
- * can't read directly — see the `ai-sdk` tool pass-through.
446
- */
447
- function isStandardSchema(value) {
448
- return typeof value === "object" && value !== null && "~standard" in value;
449
- }
450
- /**
451
- * Pulls the JSON Schema off a {@link StandardSchemaV1} via its optional
452
- * `~standard.jsonSchema.input()` extension (implemented by e.g. Zod v4's
453
- * `z.toJSONSchema`), awaiting it when the producer is async. Returns
454
- * `undefined` when the schema doesn't expose the extension. Use this to build
455
- * a provider request's `response_format`/tool `parameters` from a schema.
456
- */
457
- async function getJsonSchema(schema) {
458
- const jsonSchemaFn = schema?.["~standard"].jsonSchema?.input;
459
- if (!jsonSchemaFn) return;
460
- const result = jsonSchemaFn();
461
- return result instanceof Promise ? await result : result;
462
- }
463
- /**
464
- * Synchronous variant of {@link getJsonSchema}, for call sites that can't
465
- * await (building tool/event descriptors inline). An async JSON Schema
466
- * producer is treated as absent (returns `undefined`) — in practice Zod's
467
- * `z.toJSONSchema` resolves synchronously.
468
- */
469
- function getJsonSchemaSync(schema) {
470
- const jsonSchemaFn = schema?.["~standard"].jsonSchema?.input;
471
- if (!jsonSchemaFn) return;
472
- const result = jsonSchemaFn();
473
- return result instanceof Promise ? void 0 : result;
474
- }
475
- /**
476
- * Validates `value` against a {@link StandardSchemaV1}, synchronously.
477
- * Throws if the schema's `validate` returns a `Promise` (async validation is
478
- * not supported anywhere in this library) or if validation reports issues —
479
- * in which case the thrown `Error.message` joins every issue message with
480
- * `', '`.
481
- */
482
- function validateSchemaSync(schema, value) {
483
- const result = schema["~standard"].validate(value);
484
- if (result instanceof Promise) throw new Error("Async schema validation is not supported.");
485
- if (result.issues) throw new Error(result.issues.map((issue) => issue.message).join(", "));
486
- return result.value;
487
- }
488
- //#endregion
489
- //#region src/internal/registry.ts
490
- /** Non-enumerable hook an optional adapter can attach to a model registry. */
491
- const DEFAULT_AGENT_EXECUTORS = Symbol.for("@statelyai/agent.defaultExecutors");
492
- /**
493
- * Registered execution options, keyed on BOTH the machine object and its root
494
- * `config` object. `config` is shared by reference across `machine.provide(...)`
495
- * (unlike the machine itself), so the `config` key is what lets a machine
496
- * rebound by `runAgent` — or by any host `.provide(...)` — still resolve the
497
- * schemas/actors `setupAgent` registered. Always write through
498
- * {@link setAgentExecutionOptions} and read through
499
- * {@link getAgentExecutionOptions}.
500
- */
501
- const agentExecutionOptions = /* @__PURE__ */ new WeakMap();
502
- /** The root `config` object a machine shares with every `.provide(...)` of itself. */
503
- function machineConfigKey(machine) {
504
- const config = machine?.config;
505
- return config && typeof config === "object" ? config : void 0;
506
- }
507
- /** Registers `options` for `machine` and for every `.provide(...)` of it. */
508
- function setAgentExecutionOptions(machine, options) {
509
- if (!machine || typeof machine !== "object") return;
510
- agentExecutionOptions.set(machine, options);
511
- const config = machineConfigKey(machine);
512
- if (config) agentExecutionOptions.set(config, options);
513
- }
514
- /**
515
- * Reads the options registered for `machine`, falling back to the root `config`
516
- * key so a `.provide(...)`-rebound machine (e.g. the one behind a `runAgent`
517
- * result snapshot) resolves the same pack.
518
- */
519
- function getAgentExecutionOptions(machine) {
520
- if (!machine || typeof machine !== "object") return void 0;
521
- const direct = agentExecutionOptions.get(machine);
522
- if (direct) return direct;
523
- const config = machineConfigKey(machine);
524
- return config ? agentExecutionOptions.get(config) : void 0;
525
- }
526
- /**
527
- * Machine-carried wait-state predicates, keyed on the machine's root `config`
528
- * object. `config` is shared by reference across `machine.provide(...)` (unlike
529
- * the machine object itself), so a predicate registered here travels with the
530
- * machine through `.provide` — which is why it is keyed on `config`, not the
531
- * machine. Set by `setupAgent({ isIdle })` in `createMachine` and by
532
- * `setupAgent.fromConfig` (its `isIdle` option or the config's
533
- * `idleTags`), read by `runAgent` before its structural default.
534
- */
535
- const machineIdlePredicates = /* @__PURE__ */ new WeakMap();
536
- /** Reads the {@link machineIdlePredicates} predicate carried by `machine` (via its root `config`), if any. */
537
- function getMachineIdlePredicate(machine) {
538
- const config = machine.config;
539
- return config ? machineIdlePredicates.get(config) : void 0;
540
- }
541
- const unboundPlaceholderLogics = /* @__PURE__ */ new WeakSet();
542
- /** Text/decision logics created WITH their own executor (withExecutor or the
543
- * factory's second arg) — these are runnable as-is, so runAgent's bind check
544
- * must not reject them as direct-object invoke srcs. */
545
- const executorBoundLogics = /* @__PURE__ */ new WeakSet();
546
- function missingActor(src) {
547
- const logic = (0, xstate.createAsyncLogic)({ run: async () => {
548
- throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }).`);
549
- } });
550
- unboundPlaceholderLogics.add(logic);
551
- return logic;
552
- }
553
- function isUnboundPlaceholder(logic) {
554
- return !!logic && typeof logic === "object" && unboundPlaceholderLogics.has(logic);
555
- }
556
- function getRegisteredAgentExecutionOptions(machine, options) {
557
- return {
558
- ...getAgentExecutionOptions(machine),
559
- ...options
560
- };
561
- }
562
- //#endregion
563
- //#region src/text-logic.ts
564
- const USER_INPUT_ACTOR = "agent.userInput";
565
- const GENERATE_TEXT_ACTOR = "agent.generateText";
566
- const STREAM_TEXT_ACTOR = "agent.streamText";
567
- const DECIDE_ACTOR = "agent.decide";
568
- /**
569
- * Splits a portable `"provider/model-id"` model ref (the convention JSON
570
- * workflows and registry-less hosts use, e.g. `"openai/gpt-5.4-mini"`) into
571
- * its parts. A ref with no `/` has no provider — `modelId` is the whole ref.
572
- * The standard building block for a host's `resolveModel`:
573
- *
574
- * @example
575
- * ```ts
576
- * const resolveModel = (ref: string) => openai(parseModelRef(ref).modelId);
577
- * ```
578
- */
579
- function parseModelRef(modelRef) {
580
- const slash = modelRef.indexOf("/");
581
- return slash === -1 ? {
582
- provider: void 0,
583
- modelId: modelRef
584
- } : {
585
- provider: modelRef.slice(0, slash),
586
- modelId: modelRef.slice(slash + 1)
587
- };
588
- }
589
- const AGENT_FINISH_REASONS = new Set([
590
- "stop",
591
- "length",
592
- "tool-calls",
593
- "content-filter",
594
- "other"
595
- ]);
596
- /**
597
- * Reads a settled call's {@link AgentFinishReason} off a RAW executor result's
598
- * `finishReason` field — the finish-reason sibling of {@link getCallUsage},
599
- * applied by `runAgent` before it puts the reason on the `request.end` trace.
600
- * A provider's `'error'` normalizes to `'other'`; anything else the union does
601
- * not name (an un-normalized provider string, a missing field) returns
602
- * `undefined`.
603
- */
604
- function getCallFinishReason(raw) {
605
- const value = raw?.finishReason;
606
- if (typeof value !== "string") return;
607
- if (value === "error") return "other";
608
- return AGENT_FINISH_REASONS.has(value) ? value : void 0;
609
- }
610
- /** The token fields {@link AgentUsage} aggregates. @internal */
611
- const AGENT_USAGE_TOKEN_FIELDS = [
612
- "inputTokens",
613
- "outputTokens",
614
- "totalTokens",
615
- "reasoningTokens",
616
- "cachedInputTokens"
617
- ];
618
- /**
619
- * Reads a settled call's per-call {@link AgentCallUsage} off a RAW executor
620
- * result's `usage` field, keeping only finite numbers — the same normalization
621
- * `runAgent` applies before it delivers `'@agent.usage'`. Returns `undefined`
622
- * when the result reports no usage at all. Works for our `{ output, usage }`
623
- * envelope, for a raw Vercel AI SDK result (its `LanguageModelUsage` carries
624
- * the same flat field names), and for any custom executor that follows the
625
- * shape.
626
- *
627
- * The seam for the step-loop path, where the host holds the raw result itself:
628
- *
629
- * ```ts
630
- * const { output, raw } = await executeAgentRequest(effect, executors);
631
- * const usage = getCallUsage(raw);
632
- * if (usage) append({ type: AGENT_USAGE_EVENT_TYPE, usage }); // journal + transition, like any event
633
- * append(effect.toDoneEvent(output));
634
- * ```
635
- *
636
- * See "Token usage on this path" in docs/steps.md for the full loop.
637
- */
638
- function getCallUsage(raw) {
639
- const usage = raw?.usage;
640
- if (!usage || typeof usage !== "object") return;
641
- let out;
642
- for (const field of AGENT_USAGE_TOKEN_FIELDS) {
643
- const value = usage[field];
644
- if (typeof value === "number" && Number.isFinite(value)) (out ??= {})[field] = value;
645
- }
646
- return out;
647
- }
648
- function textRequestSourceIssue(request) {
649
- const hasPrompt = typeof request.prompt === "string" && request.prompt.length > 0;
650
- const hasMessages = Array.isArray(request.messages) && request.messages.length > 0;
651
- const label = request.name ? ` '${request.name}'` : "";
652
- if (hasPrompt && hasMessages) return `Agent text request${label} has both a non-empty \`prompt\` and \`messages\` — provide exactly one so the model has a single input source.`;
653
- if (!hasPrompt && !hasMessages) return `Agent text request${label} has neither a non-empty \`prompt\` nor \`messages\` — provide at least one so the model has something to respond to.`;
654
- }
655
- const agentTextInputSchema = { "~standard": {
656
- version: 1,
657
- vendor: "statelyai-agent",
658
- validate(value) {
659
- if (!value || typeof value !== "object") return { issues: [{ message: "Expected agent text input object" }] };
660
- const request = value;
661
- if (typeof request.model !== "string") return { issues: [{ message: "Expected agent text input with a string `model`" }] };
662
- const issue = textRequestSourceIssue(request);
663
- if (issue) return { issues: [{ message: issue }] };
664
- return { value: request };
665
- }
666
- } };
667
- const passthroughSchema = { "~standard": {
668
- version: 1,
669
- vendor: "statelyai-agent",
670
- validate(value) {
671
- return { value };
672
- }
673
- } };
674
- const unknownOutputSchema = passthroughSchema;
675
- const stringOutputSchema = { "~standard": {
676
- version: 1,
677
- vendor: "statelyai-agent",
678
- validate(value) {
679
- return typeof value === "string" ? { value } : { issues: [{ message: "Expected string output" }] };
680
- }
681
- } };
682
- const noInputSchema = passthroughSchema;
683
- function createBuiltinTextActor(src, mode, outputSchema) {
684
- const logic = (0, xstate.createAsyncLogic)({ run: async () => {
685
- throw new Error(`'${src}' has no host execution. Provide an implementation with machine.provide({ actors: { '${src}': ... } }) or execute the returned agent request with executeAgentRequest(...).`);
686
- } });
687
- return Object.assign(logic, {
688
- kind: "statelyai.textLogic",
689
- mode,
690
- schemas: {
691
- input: agentTextInputSchema,
692
- output: outputSchema
693
- },
694
- request(input) {
695
- return validateSchemaSync(agentTextInputSchema, input);
696
- },
697
- async execute(input, executors) {
698
- const { output } = await executeAgentTextRequest(mode, src, validateSchemaSync(agentTextInputSchema, input), executors);
699
- return validateSchemaSync(outputSchema, output);
700
- },
701
- withExecutor(execute) {
702
- return createTextLogic({
703
- mode,
704
- schemas: {
705
- input: agentTextInputSchema,
706
- output: outputSchema
707
- },
708
- name: ({ input }) => input.name,
709
- model: ({ input }) => input.model,
710
- system: ({ input }) => input.system,
711
- prompt: ({ input }) => input.prompt,
712
- messages: ({ input }) => input.messages,
713
- tools: ({ input }) => input.tools,
714
- toolChoice: ({ input }) => input.toolChoice,
715
- includeReasoning: ({ input }) => input.includeReasoning,
716
- temperature: ({ input }) => input.temperature,
717
- maxOutputTokens: ({ input }) => input.maxOutputTokens,
718
- topP: ({ input }) => input.topP,
719
- topK: ({ input }) => input.topK,
720
- seed: ({ input }) => input.seed,
721
- stopSequences: ({ input }) => input.stopSequences,
722
- maxSteps: ({ input }) => input.maxSteps,
723
- metadata: ({ input }) => input.metadata
724
- }, execute);
725
- }
726
- });
727
- }
728
- /** The unbound `agent.generateText`/`agent.streamText` builtins registered by setupAgent. @internal */
729
- const builtinTextActors = {
730
- [GENERATE_TEXT_ACTOR]: createBuiltinTextActor(GENERATE_TEXT_ACTOR, "generate", unknownOutputSchema),
731
- [STREAM_TEXT_ACTOR]: createBuiltinTextActor(STREAM_TEXT_ACTOR, "stream", stringOutputSchema)
732
- };
733
- /** The unbound `agent.userInput` builtin registered by setupAgent (an unbound-placeholder logic — see internal/registry.ts). Output is `string` — what the human typed. @internal */
734
- const userInputActor = (0, xstate.createAsyncLogic)({ run: async () => {
735
- throw new Error(`'${USER_INPUT_ACTOR}' has no host execution. Provide an implementation with machine.provide({ actors: { '${USER_INPUT_ACTOR}': ... } }).`);
736
- } });
737
- unboundPlaceholderLogics.add(userInputActor);
738
- /**
739
- * Validates a raw model/executor output against `schema`, returning the
740
- * parsed value. Thin wrapper over {@link validateSchemaSync} for parsing a
741
- * text request's structured output outside of `TextLogic.execute`/
742
- * `executeAgentRequest` (e.g. a custom host loop).
743
- */
744
- function parseOutput(schema, output) {
745
- return validateSchemaSync(schema, output);
746
- }
747
- /** Resolves a `ResolveTextLogicValue` (calls it if it's a function, else returns it as-is). @internal */
748
- function resolveTextLogicValue(value, args) {
749
- return typeof value === "function" ? value(args) : value;
750
- }
751
- /**
752
- * Creates reusable, standalone {@link TextLogic}: an actor that, when run,
753
- * resolves typed input to typed output via a model call. Register the
754
- * result under `actors:` and invoke it by name (equivalent to what
755
- * `setupAgent({ requests })` builds internally for each request entry). Pass
756
- * `execute` here, or bind it later with `.withExecutor(...)`, a runtime
757
- * adapter's `machine.provide(...)`, or `runAgent`'s `generateText`/
758
- * `streamText` options.
759
- *
760
- * @example
761
- * ```ts
762
- * export const tellJoke = createTextLogic({
763
- * mode: 'stream',
764
- * schemas: { input: z.object({ topic: z.string() }), output: z.string() },
765
- * model: 'openai/gpt-5.4-mini',
766
- * system: 'You tell short, punchy jokes.',
767
- * prompt: ({ input }) => `Tell a joke about ${input.topic}.`,
768
- * });
769
- * ```
770
- */
771
- function createTextLogic(config, execute) {
772
- const schemas = {
773
- input: config.schemas?.input ?? noInputSchema,
774
- output: config.schemas?.output ?? stringOutputSchema
775
- };
776
- const request = (input) => {
777
- const parsedInput = validateSchemaSync(schemas.input, input);
778
- const args = { input: parsedInput };
779
- const name = resolveTextLogicValue(config.name, args);
780
- const prompt = resolveTextLogicValue(config.prompt, args);
781
- const messages = resolveTextLogicValue(config.messages, args);
782
- const sourceIssue = textRequestSourceIssue({
783
- name,
784
- prompt,
785
- messages
786
- });
787
- if (sourceIssue) throw new Error(sourceIssue);
788
- return {
789
- name,
790
- input: parsedInput,
791
- model: resolveTextLogicValue(config.model, args),
792
- system: resolveTextLogicValue(config.system, args),
793
- prompt,
794
- messages,
795
- tools: resolveTextLogicValue(config.tools, args),
796
- toolChoice: resolveTextLogicValue(config.toolChoice, args),
797
- outputSchema: schemas.output,
798
- includeReasoning: resolveTextLogicValue(config.includeReasoning, args),
799
- temperature: resolveTextLogicValue(config.temperature, args),
800
- maxOutputTokens: resolveTextLogicValue(config.maxOutputTokens, args),
801
- topP: resolveTextLogicValue(config.topP, args),
802
- topK: resolveTextLogicValue(config.topK, args),
803
- seed: resolveTextLogicValue(config.seed, args),
804
- stopSequences: resolveTextLogicValue(config.stopSequences, args),
805
- maxSteps: resolveTextLogicValue(config.maxSteps, args),
806
- metadata: resolveTextLogicValue(config.metadata, args)
807
- };
808
- };
809
- const logic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal, system, self }, enq) => {
810
- const resolvedRequest = request(input);
811
- if (!execute) throw new Error("Text logic has no host execution. Pass an executor as the second argument to createTextLogic(...), provide a runtime adapter, or bind it through runAgent/provideExecutors, or execute the XState effect in your host.");
812
- const result = await execute({
813
- input,
814
- request: resolvedRequest,
815
- signal,
816
- system,
817
- self,
818
- emit: enq.emit
819
- });
820
- const selfId = self?.id;
821
- const output = await normalizeGeneratorResult(result, typeof selfId === "string" ? selfId : "text logic", { request: resolvedRequest });
822
- return validateSchemaSync(schemas.output, output);
823
- } });
824
- const textLogic = Object.assign(logic, {
825
- kind: "statelyai.textLogic",
826
- mode: config.mode ?? "generate",
827
- schemas,
828
- request,
829
- async execute(input, executors) {
830
- const { output } = await executeAgentTextRequest(config.mode ?? "generate", "textLogic", request(input), executors);
831
- return validateSchemaSync(schemas.output, output);
832
- },
833
- withExecutor(nextExecute) {
834
- return createTextLogic(config, nextExecute);
835
- }
836
- });
837
- if (execute) executorBoundLogics.add(textLogic);
838
- return textLogic;
839
- }
840
- /**
841
- * Binds a child machine's {@link TextLogic} to a raw
842
- * {@link AgentRequestExecutor} (the `generateText`/`streamText` shape hosts
843
- * implement). Encapsulates the `withExecutor` idiom child agents repeat:
844
- * default the request's `tools` to `{}`, forward the actor `signal`, call the
845
- * executor, and return its `{ output }` envelope. Use this to share ONE
846
- * executor across a parent and its nested children.
847
- *
848
- * @example
849
- * ```ts
850
- * childMachine.provide({
851
- * actors: {
852
- * researchTopic: bindRequestExecutor(setup.requests.researchTopic, generateText),
853
- * },
854
- * });
855
- * ```
856
- */
857
- function bindRequestExecutor(logic, executor, info) {
858
- return logic.withExecutor(async ({ request, signal }) => {
859
- const { output } = await executor({
860
- ...request,
861
- tools: request.tools ?? {}
862
- }, {
863
- signal,
864
- onChunk: info?.onChunk
865
- });
866
- return { output };
867
- });
868
- }
869
- /** Type guard: true for any actor logic built by createTextLogic (checks the `kind` marker). @internal */
870
- function isTextLogic(value) {
871
- return !!value && typeof value === "object" && value.kind === "statelyai.textLogic" && typeof value.request === "function";
872
- }
873
- /**
874
- * Classifies a text request's output schema as `'structured'` (its JSON
875
- * Schema is `type: 'object'`, `type: 'array'`, or a top-level union/
876
- * composition — `anyOf`/`oneOf`/`allOf`, which a bare `z.union`/
877
- * `z.discriminatedUnion` emits with no top-level `type`) or `'text'`
878
- * (anything else, including no schema). Reads the schema's
879
- * `~standard.jsonSchema.input()` extension — schemas without it are treated
880
- * as `'text'`.
881
- */
882
- function getAgentOutputMode(schema) {
883
- const jsonSchema = getJsonSchemaSync(schema);
884
- if (!jsonSchema) return "text";
885
- if (jsonSchema.type === "object" || jsonSchema.type === "array") return "structured";
886
- if (jsonSchema.type === void 0 && ("anyOf" in jsonSchema || "oneOf" in jsonSchema || "allOf" in jsonSchema)) return "structured";
887
- return "text";
888
- }
889
- /**
890
- * Builds the uniform structured-output envelope schema every structured request
891
- * is sent to the provider as: a root object `{ result: <inner> }`, plus — when
892
- * `options.reasoning` is `true` — an optional string `reasoning` property listed
893
- * BEFORE `result` (property order nudges the model to reason first). This is THE
894
- * wire contract for structured output: a root object is universally accepted as
895
- * a provider response schema, unlike a bare union/array root that many providers
896
- * reject.
897
- *
898
- * The returned {@link StandardSchemaV1} validates the `{ reasoning?, result }`
899
- * envelope (unwrapping `result` through the original schema, capturing a string
900
- * `reasoning` when present) and exposes the enveloped JSON Schema. Adapters read
901
- * `.result` off the provider output before the machine validates it — so this is
902
- * transparent: user-facing output types stay the declared (un-enveloped) schema,
903
- * and `reasoning` is surfaced only on the raw executor result, never in machine
904
- * context/output.
905
- */
906
- function buildEnvelopeSchema(inner, options = {}) {
907
- const includeReasoning = options.reasoning === true;
908
- const buildJson = (innerJson) => ({
909
- type: "object",
910
- properties: {
911
- ...includeReasoning ? { reasoning: { type: "string" } } : {},
912
- result: innerJson ?? {}
913
- },
914
- required: ["result"],
915
- additionalProperties: false
916
- });
917
- return { "~standard": {
918
- version: 1,
919
- vendor: "statelyai-agent",
920
- validate(value) {
921
- if (!value || typeof value !== "object" || !("result" in value)) return { issues: [{ message: "Expected a { result } envelope object" }] };
922
- const innerResult = inner["~standard"].validate(value.result);
923
- if (innerResult instanceof Promise) throw new Error("Async schema validation is not supported.");
924
- if (innerResult.issues) return innerResult;
925
- const envelope = { result: innerResult.value };
926
- const reasoning = value.reasoning;
927
- if (typeof reasoning === "string") envelope.reasoning = reasoning;
928
- return { value: envelope };
929
- },
930
- jsonSchema: { input: () => {
931
- const innerJson = inner["~standard"].jsonSchema?.input?.();
932
- return innerJson instanceof Promise ? innerJson.then(buildJson) : buildJson(innerJson);
933
- } }
934
- } };
935
- }
936
- /**
937
- * Validates a raw provider value against the structured-output envelope for
938
- * `request` and returns the unwrapped `{ result, reasoning? }` — the checked
939
- * replacement for `raw as StructuredOutputEnvelope` in hand-written hosts.
940
- * Pair with {@link buildEnvelopeSchema} (which produced the schema the
941
- * provider was asked to satisfy).
942
- */
943
- function parseStructuredEnvelope(request, value) {
944
- if (!request.outputSchema) throw new Error("parseStructuredEnvelope: the request declares no outputSchema.");
945
- return validateSchemaSync(buildEnvelopeSchema(request.outputSchema, { reasoning: request.includeReasoning }), value);
946
- }
947
- /**
948
- * Merges request-declared and call-site `tools`, dispatches to the
949
- * `mode`-appropriate executor (`generateText`/`streamText`), and normalizes
950
- * the raw result via {@link normalizeGeneratorResult}. Shared by
951
- * `TextLogic.execute`, `executeAgentRequest`, and the `agent.generateText`/
952
- * `agent.streamText` builtins. Throws if no executor is registered for
953
- * `mode`.
954
- *
955
- * @internal
956
- */
957
- async function executeAgentTextRequest(mode, id, input, executors, tools = {}, info) {
958
- const request = {
959
- ...input,
960
- name: input.name ?? id,
961
- tools: {
962
- ...input.tools,
963
- ...tools
964
- }
965
- };
966
- const sourceIssue = textRequestSourceIssue(request);
967
- if (sourceIssue) throw new Error(sourceIssue);
968
- const executor = mode === "stream" ? executors.streamText : executors.generateText;
969
- if (!executor) throw new Error(`No executor provided for ${mode === "stream" ? "stream" : "generate"} request '${id}'.`);
970
- const raw = await executor(request, info);
971
- return {
972
- output: await normalizeGeneratorResult(raw, id, {
973
- request,
974
- onChunk: info?.onChunk
975
- }),
976
- raw
977
- };
978
- }
979
- function hasTextStream(value) {
980
- return "textStream" in value && typeof value.textStream === "object" && !!value.textStream?.[Symbol.asyncIterator];
981
- }
982
- function parseRawAiSdkText(text, request, id) {
983
- if (!request?.outputSchema) return text;
984
- let parsed = text;
985
- try {
986
- parsed = JSON.parse(text);
987
- } catch {}
988
- try {
989
- return parseOutput(request.outputSchema, parsed);
990
- } catch (error) {
991
- throw new Error(`Executor for '${id}' returned a raw AI SDK result whose text could not be parsed against the request's outputSchema. Structured-output requests through raw AI SDK generateText/streamText functions are best-effort — for reliable structured output, use createAiSdkExecutors from '@statelyai/agent/ai-sdk'. Cause: ${error instanceof Error ? error.message : String(error)}`);
992
- }
993
- }
994
- /**
995
- * Unwraps an executor result into the request's final output. Accepts three
996
- * shapes:
997
- * - `{ output }` (the {@link AgentRequestExecutorResult} envelope) — awaits and
998
- * returns `output` (the fast path; unchanged).
999
- * - a raw AI SDK `streamText` result (`{ textStream }` async iterable) — iterates
1000
- * `textStream`, forwarding each string chunk to `info.onChunk`, then resolves
1001
- * the final text from `await result.text` if present else the accumulated chunks.
1002
- * - a raw AI SDK `generateText` result (`{ text }` string or promise) — awaits `text`.
1003
- *
1004
- * For the two raw AI SDK shapes, if `info.request?.outputSchema` is set the final
1005
- * text is parsed through {@link parseOutput} (best-effort); a parse failure throws
1006
- * an error recommending `createAiSdkExecutors` from '@statelyai/agent/ai-sdk'.
1007
- *
1008
- * A value matching none of these is a runtime error naming `id`. This is
1009
- * generator-result unwrapping only — decision results are extracted separately
1010
- * by `resolveDecision`.
1011
- *
1012
- * @internal
1013
- */
1014
- async function normalizeGeneratorResult(result, id = "text request", info) {
1015
- const resolved = await result;
1016
- if (!resolved || typeof resolved !== "object") throw invalidGeneratorResult(id);
1017
- if ("output" in resolved) return await resolved.output;
1018
- if (hasTextStream(resolved)) {
1019
- let accumulated = "";
1020
- for await (const chunk of resolved.textStream) {
1021
- accumulated += chunk;
1022
- info?.onChunk?.(chunk);
1023
- }
1024
- const finalText = "text" in resolved && resolved.text !== void 0 ? await resolved.text : accumulated;
1025
- return parseRawAiSdkText(String(finalText), info?.request, id);
1026
- }
1027
- if ("text" in resolved) {
1028
- const finalText = await resolved.text;
1029
- return parseRawAiSdkText(String(finalText), info?.request, id);
1030
- }
1031
- throw invalidGeneratorResult(id);
1032
- }
1033
- function invalidGeneratorResult(id) {
1034
- return /* @__PURE__ */ new Error(`Executor for '${id}' returned an invalid result: executors must return { output } (an envelope with the text string or structured object as \`output\`, plus optional passthrough fields). Raw Vercel AI SDK generateText/streamText results ({ text } or { textStream }) are also accepted.`);
1035
- }
1036
- //#endregion
1037
- //#region src/internal/is-record.ts
1038
- /** True for a plain object-like value (arrays excluded). @internal */
1039
- function isRecord(value) {
1040
- return typeof value === "object" && value !== null && !Array.isArray(value);
1041
- }
1042
- //#endregion
1043
- //#region src/events.ts
1044
- /**
1045
- * Thrown by {@link parseAgentEvent} (and {@link eventFromInteraction}) when a
1046
- * wire payload is not a usable event: it is not an object with a string
1047
- * `type`, it names a reserved `@agent.*` type, or its fields fail the schema
1048
- * the machine registered for that type. A host turns it into a 400.
1049
- *
1050
- * Not handling an event is NOT this error — a state machine ignores events it
1051
- * has no transition for, so an event the current state does not handle parses
1052
- * fine and settles the run with `result.ignored` set.
1053
- */
1054
- var AgentInvalidEventPayloadError = class extends AgentError {
1055
- eventType;
1056
- constructor(eventType, detail) {
1057
- super("invalid-event-payload", `Invalid event payload for '${eventType}': ${detail}`);
1058
- this.name = "AgentInvalidEventPayloadError";
1059
- this.eventType = eventType;
1060
- }
1061
- };
1062
- /** Default prefix for the synthetic tool name generated per candidate event (e.g. `send_event_ASK`). Override per-request with {@link AgentEventToolNameResolver}. @internal */
1063
- const EVENT_TOOL_PREFIX = "send_event_";
1064
- function hashString(value) {
1065
- let hash = 5381;
1066
- for (let i = 0; i < value.length; i++) hash = hash * 33 ^ value.charCodeAt(i);
1067
- return (hash >>> 0).toString(36);
1068
- }
1069
- function sanitizeEventToolName(eventType) {
1070
- const base = `${EVENT_TOOL_PREFIX}${eventType.replace(/[^a-zA-Z0-9_-]/g, "_") || "event"}`;
1071
- if (base.length <= 64) return base;
1072
- const hash = hashString(eventType);
1073
- const prefixLength = 64 - hash.length - 1;
1074
- return `${base.slice(0, prefixLength)}_${hash}`;
1075
- }
1076
- function disambiguateEventToolName(toolName, eventType, usedToolNames) {
1077
- if (!usedToolNames.has(toolName)) {
1078
- usedToolNames.add(toolName);
1079
- return toolName;
1080
- }
1081
- const suffix = `_${hashString(eventType)}`;
1082
- const uniqueToolName = `${toolName.slice(0, 64 - suffix.length)}${suffix}`;
1083
- usedToolNames.add(uniqueToolName);
1084
- return uniqueToolName;
1085
- }
1086
- /**
1087
- * True when an event type matches an `allowedEvents` entry: an exact type,
1088
- * `'*'` (every event), or a `'prefix.*'` wildcard matching any deeper
1089
- * segment (`'todo.*'` matches `'todo.add'` and `'todo.list.clear'`, not
1090
- * `'todo'` itself — mirroring xstate's partial wildcard events).
1091
- */
1092
- function matchesEventPattern(eventType, pattern) {
1093
- if (pattern === "*") return true;
1094
- if (pattern.endsWith(".*")) return eventType.startsWith(`${pattern.slice(0, -1)}`);
1095
- return eventType === pattern;
1096
- }
1097
- /**
1098
- * Event types reserved for delivery by the library (`@agent.*` and
1099
- * `agent.messages`). A machine may declare transitions on them,
1100
- * but they are never model-facing: {@link getAcceptedEvents} drops them before
1101
- * any `allowedEvents` matching, so they cannot be offered as a decision
1102
- * candidate (not even under a `'*'` wildcard) and {@link parseAgentEvent}
1103
- * rejects them — a model or a wire message must not be able to forge one.
1104
- * @internal
1105
- */
1106
- const RESERVED_AGENT_EVENT_PREFIX = "@agent.";
1107
- function isReservedAgentEvent(eventType) {
1108
- return eventType.startsWith(RESERVED_AGENT_EVENT_PREFIX) || eventType === "agent.messages";
1109
- }
1110
- /** True when an `allowedEvents` entry is a wildcard pattern rather than a concrete event type. @internal */
1111
- function isEventPattern(entry) {
1112
- return entry === "*" || entry.endsWith(".*");
1113
- }
1114
- /**
1115
- * Parses a wire payload (a request body, a socket frame) into an
1116
- * event typed as the machine's event union, so it can go straight to
1117
- * `runAgent({ event })` / `actor.send(...)` without a cast. Pass the machine
1118
- * itself, or any snapshot of it.
1119
- *
1120
- * It checks exactly what a schema can check: the payload is an object with a
1121
- * string `type`, that type is not one of the library-reserved `@agent.*` types,
1122
- * and — when the machine registered a schema for it — the fields satisfy that
1123
- * schema (defaults filled, transforms applied). Anything else throws
1124
- * {@link AgentInvalidEventPayloadError}, which a host answers with a 400.
1125
- *
1126
- * It deliberately does NOT check whether the current state handles the event.
1127
- * State machines ignore events they have no transition for; that is normal
1128
- * behavior, not a validation failure. Send the parsed event and read
1129
- * `result.ignored` if the host wants to tell the client nothing happened.
1130
- *
1131
- * @example
1132
- * ```ts
1133
- * const event = parseAgentEvent(machine, await request.json());
1134
- * const result = await runAgent(machine, { store, threadId, event, executors });
1135
- * ```
1136
- */
1137
- function parseAgentEvent(source, payload, options = {}) {
1138
- if (!isRecord(payload) || typeof payload.type !== "string") throw new AgentInvalidEventPayloadError("(unknown)", "expected an object with a string `type`.");
1139
- const event = payload;
1140
- if (isReservedAgentEvent(event.type)) throw new AgentInvalidEventPayloadError(event.type, "this event type is reserved for the library and cannot be sent from outside.");
1141
- const registered = getAgentExecutionOptions(typeof source.provide === "function" ? source : source.machine)?.schemas;
1142
- const inputSchema = (options.events ?? options.schemas?.events ?? registered?.events)?.[event.type];
1143
- if (!inputSchema) return event;
1144
- const { type, ...fields } = event;
1145
- try {
1146
- return {
1147
- ...validateSchemaSync(inputSchema, fields),
1148
- type
1149
- };
1150
- } catch (error) {
1151
- throw new AgentInvalidEventPayloadError(event.type, error instanceof Error ? error.message : String(error));
1152
- }
1153
- }
1154
- /**
1155
- * Lists the events a snapshot can currently accept, as {@link AgentEventDescriptor}s
1156
- * a model can be offered (via `resolveDecision`/an adapter's tool-per-event
1157
- * mapping). **Filters by event TYPE only** — it does not evaluate guards, so
1158
- * a type-legal-but-guard-rejected event can still appear here. Guard
1159
- * legality is checked separately, at decision-resolution time, via
1160
- * `snapshot.can(event)` (the `canTake` option of {@link resolveDecision} /
1161
- * {@link ResolveDecisionOptions}). Pass `eventTypes` to further narrow to a
1162
- * declared `allowedEvents` set — entries may be exact types or wildcard
1163
- * patterns (`'*'`, `'todo.*'`; see {@link matchesEventPattern}).
1164
- *
1165
- * XState-internal (`xstate.*`) and library-reserved
1166
- * ({@link RESERVED_AGENT_EVENT_PREFIX}) event types are always excluded, before
1167
- * any `allowedEvents` matching — a machine that handles `'@agent.usage'` still
1168
- * never offers it to a model.
1169
- */
1170
- function getAcceptedEvents(snapshot, options = {}) {
1171
- const eventTypes = options.eventTypes;
1172
- const seen = /* @__PURE__ */ new Set();
1173
- const usedToolNames = /* @__PURE__ */ new Set();
1174
- return (0, xstate.getNextTransitions)(snapshot).flatMap((transitionDefinition) => {
1175
- const eventType = transitionDefinition.eventType;
1176
- if (!eventType || eventType === "*" || eventType.startsWith("xstate.") || isReservedAgentEvent(eventType) || eventTypes && !eventTypes.some((pattern) => matchesEventPattern(eventType, pattern)) || seen.has(eventType)) return [];
1177
- seen.add(eventType);
1178
- const defaultToolName = sanitizeEventToolName(eventType);
1179
- const toolName = options.eventToolName ? options.eventToolName({
1180
- eventType,
1181
- defaultToolName
1182
- }) : disambiguateEventToolName(defaultToolName, eventType, usedToolNames);
1183
- const inputSchema = (options.events ?? options.schemas?.events)?.[eventType];
1184
- return [{
1185
- type: eventType,
1186
- toolName,
1187
- ...inputSchema ? { inputSchema } : {}
1188
- }];
1189
- });
1190
- }
1191
- //#endregion
1192
- //#region src/decision.ts
1193
- function decideRequestFromInput(input) {
1194
- const allowedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input) ?? [];
1195
- return {
1196
- kind: "decision",
1197
- id: "",
1198
- name: input.name,
1199
- input,
1200
- model: input.model,
1201
- system: input.system,
1202
- prompt: input.prompt,
1203
- messages: input.messages,
1204
- events: allowedEventTypes.filter((type) => !isEventPattern(type)).map((type) => ({
1205
- type,
1206
- toolName: sanitizeEventToolName(type)
1207
- })),
1208
- attempts: [],
1209
- temperature: input.temperature,
1210
- maxOutputTokens: input.maxOutputTokens,
1211
- topP: input.topP,
1212
- topK: input.topK,
1213
- seed: input.seed,
1214
- stopSequences: input.stopSequences,
1215
- metadata: input.metadata
1216
- };
1217
- }
1218
- function decideActorWithExecutor(execute) {
1219
- const logic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal }) => {
1220
- if (!execute) throw new Error(`'${DECIDE_ACTOR}' has no host execution. Provide an implementation with machine.provide({ actors: { '${DECIDE_ACTOR}': ... } }) or resolve the returned agent request with resolveDecision(...).`);
1221
- const resolvedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input);
1222
- if (resolvedEventTypes === void 0) throw new Error(`'${DECIDE_ACTOR}' input has omitted \`allowedEvents\`, which means "all currently-legal events" — but that requires a snapshot-aware host (runAgent or the step path) to resolve. Under a bare createActor(...), declare \`allowedEvents\` explicitly to use this actor here.`);
1223
- if (resolvedEventTypes.some(isEventPattern)) throw new Error(`'${DECIDE_ACTOR}' input uses wildcard \`allowedEvents\` patterns, which expand against the live snapshot — that requires a snapshot-aware host (runAgent or the step path). Under a bare createActor(...), list event types explicitly.`);
1224
- return resolveDecision(decideRequestFromInput(input), { decide: execute }, {
1225
- maxRetries: input.maxRetries ?? 2,
1226
- signal
1227
- });
1228
- } });
1229
- return Object.assign(logic, {
1230
- kind: "statelyai.decisionLogic",
1231
- maxRetries: 2,
1232
- request: decideRequestFromInput,
1233
- allowedEventTypes: (input) => resolveAllowedEventTypes(input.allowedEvents, input),
1234
- withExecutor: (nextExecute) => decideActorWithExecutor(nextExecute)
1235
- });
1236
- }
1237
- function createDecideActor() {
1238
- return decideActorWithExecutor();
1239
- }
1240
- function resolveAllowedEventTypes(allowedEvents, input) {
1241
- if (allowedEvents === void 0) return;
1242
- const resolved = typeof allowedEvents === "function" ? allowedEvents({ input }) : allowedEvents;
1243
- return typeof resolved === "string" ? [resolved] : resolved;
1244
- }
1245
- /**
1246
- * Creates reusable, standalone {@link DecisionLogic}: an actor that, when
1247
- * run, resolves to exactly one currently-legal {@link ChosenEvent} by
1248
- * calling the host `decide` executor (passed here as `execute`, or supplied
1249
- * later via {@link DecisionLogic.withExecutor}, `machine.provide(...)`, or
1250
- * `runAgent`'s `decide` option). Register the result under `actors:` and
1251
- * invoke it by name; for a one-off, state-local decision, prefer the
1252
- * `agent.decide` builtin invoke instead — it needs no separate declaration
1253
- * and types `allowedEvents` against the machine's own event schemas.
1254
- *
1255
- * @example
1256
- * ```ts
1257
- * import { createDecisionLogic } from '@statelyai/agent';
1258
- * import { z } from 'zod';
1259
- *
1260
- * export const chooseMove = createDecisionLogic({
1261
- * schemas: { input: z.object({ playerHp: z.number(), enemyHp: z.number() }) },
1262
- * model: 'openai/gpt-5.4-mini',
1263
- * system: 'You are playing a turn-based game. Choose exactly one legal move.',
1264
- * prompt: ({ input }) => `Player HP: ${input.playerHp}\nEnemy HP: ${input.enemyHp}`,
1265
- * allowedEvents: ({ input }) =>
1266
- * input.playerHp <= 6
1267
- * ? ['ATTACK', 'DEFEND', 'HEAL', 'FLEE']
1268
- * : ['ATTACK', 'DEFEND', 'FLEE'],
1269
- * });
1270
- * ```
1271
- */
1272
- function createDecisionLogic(config, execute) {
1273
- const maxRetries = config.maxRetries ?? 2;
1274
- const request = (input) => {
1275
- const parsedInput = config.schemas ? validateSchemaSync(config.schemas.input, input) : input;
1276
- const args = { input: parsedInput };
1277
- const allowedEventTypes = resolveAllowedEventTypes(config.allowedEvents, parsedInput);
1278
- return {
1279
- kind: "decision",
1280
- id: "",
1281
- name: resolveTextLogicValue(config.name, args),
1282
- input: parsedInput,
1283
- model: resolveTextLogicValue(config.model, args),
1284
- system: resolveTextLogicValue(config.system, args),
1285
- prompt: resolveTextLogicValue(config.prompt, args),
1286
- messages: resolveTextLogicValue(config.messages, args),
1287
- events: (allowedEventTypes ?? []).map((type) => ({
1288
- type,
1289
- toolName: sanitizeEventToolName(type)
1290
- })),
1291
- attempts: [],
1292
- temperature: resolveTextLogicValue(config.temperature, args),
1293
- maxOutputTokens: resolveTextLogicValue(config.maxOutputTokens, args),
1294
- topP: resolveTextLogicValue(config.topP, args),
1295
- topK: resolveTextLogicValue(config.topK, args),
1296
- seed: resolveTextLogicValue(config.seed, args),
1297
- stopSequences: resolveTextLogicValue(config.stopSequences, args),
1298
- metadata: resolveTextLogicValue(config.metadata, args)
1299
- };
1300
- };
1301
- const logic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal }) => {
1302
- if (!execute) throw new Error("Decision logic has no host execution. Pass an executor as the second argument to createDecisionLogic(...), provide a runtime adapter, or bind it through runAgent/provideExecutors, or call resolveDecision from your host.");
1303
- const allowedEventTypes = resolveAllowedEventTypes(config.allowedEvents, input);
1304
- if (allowedEventTypes === void 0) throw new Error("Decision logic has omitted `allowedEvents`, which means \"all currently-legal events\" — but that requires a snapshot-aware host (runAgent or the step path) to resolve. Under a bare createActor(...), declare `allowedEvents` explicitly on this logic to use it here.");
1305
- if (allowedEventTypes.some(isEventPattern)) throw new Error("Decision logic uses wildcard `allowedEvents` patterns, which expand against the live snapshot — that requires a snapshot-aware host (runAgent or the step path). Under a bare createActor(...), list event types explicitly.");
1306
- return resolveDecision(request(input), { decide: execute }, {
1307
- maxRetries,
1308
- signal
1309
- });
1310
- } });
1311
- const decisionLogic = Object.assign(logic, {
1312
- kind: "statelyai.decisionLogic",
1313
- maxRetries,
1314
- request,
1315
- allowedEventTypes: (input) => resolveAllowedEventTypes(config.allowedEvents, input),
1316
- withExecutor(nextExecute) {
1317
- return createDecisionLogic(config, nextExecute);
1318
- }
1319
- });
1320
- if (execute) executorBoundLogics.add(decisionLogic);
1321
- return decisionLogic;
1322
- }
1323
- /** Type guard: true for any actor logic built by createDecisionLogic/createDecideActor (checks the `kind` marker). @internal */
1324
- function isDecisionLogic(value) {
1325
- return !!value && typeof value === "object" && value.kind === "statelyai.decisionLogic" && typeof value.request === "function";
1326
- }
1327
- /**
1328
- * Builds an {@link AgentDecisionRequest} for {@link resolveDecision}, filling
1329
- * in the fields a hand-written literal has to restate every time: `kind`, an
1330
- * `id`, an empty `attempts` list, and each candidate event's `toolName`.
1331
- *
1332
- * @example
1333
- * ```ts
1334
- * const chosen = await resolveDecision(
1335
- * createDecisionRequest({
1336
- * name: 'judge',
1337
- * model: 'reviewer',
1338
- * prompt: `Judge this draft.\n\n${snapshot.context.draft}`,
1339
- * events: getAcceptedEvents(snapshot),
1340
- * }),
1341
- * { decide },
1342
- * { canTake: (event) => snapshot.can(event) },
1343
- * );
1344
- * ```
1345
- */
1346
- function createDecisionRequest(options) {
1347
- const { id, name, events, attempts, ...rest } = options;
1348
- return {
1349
- ...rest,
1350
- kind: "decision",
1351
- id: id ?? name ?? "decision",
1352
- ...name === void 0 ? {} : { name },
1353
- events: dedupeToolNames(events.map((event) => typeof event === "string" ? {
1354
- type: event,
1355
- toolName: sanitizeEventToolName(event)
1356
- } : event)),
1357
- attempts: attempts ?? []
1358
- };
1359
- }
1360
- function dedupeToolNames(candidates) {
1361
- const taken = /* @__PURE__ */ new Set();
1362
- return candidates.map((candidate) => {
1363
- if (candidate.toolName === void 0) return candidate;
1364
- let toolName = candidate.toolName;
1365
- for (let n = 2; taken.has(toolName); n++) {
1366
- const suffix = `_${n}`;
1367
- toolName = `${candidate.toolName.slice(0, 64 - suffix.length)}${suffix}`;
1368
- }
1369
- taken.add(toolName);
1370
- return toolName === candidate.toolName ? candidate : {
1371
- ...candidate,
1372
- toolName
1373
- };
1374
- });
1375
- }
1376
- /**
1377
- * Thrown by {@link resolveDecision} when every attempt (up to
1378
- * `maxRetries + 1` of them) fails one of the three checks recorded in
1379
- * {@link DecisionAttempt.failure}. Carries the full `attempts` list for
1380
- * diagnostics; a machine typically routes this via the decision invoke's
1381
- * `onError`.
1382
- */
1383
- var AgentDecisionExhaustedError = class extends AgentError {
1384
- attempts;
1385
- constructor(attempts) {
1386
- super("decision-exhausted", `Decision exhausted after ${attempts.length} attempt${attempts.length === 1 ? "" : "s"}: ` + attempts.map((attempt) => attempt.reason).join("; "));
1387
- this.name = "AgentDecisionExhaustedError";
1388
- this.attempts = attempts;
1389
- }
1390
- };
1391
- /**
1392
- * Renders a decision request's prior failed `attempts` into feedback messages
1393
- * a host appends to the model call so retries converge — the transport-agnostic
1394
- * "your last choice failed because X, choose again from Y" logic every adapter
1395
- * and raw-SDK host repeats. Returns one `user`-role {@link AgentMessage} per
1396
- * attempt (empty when there are none); adapters map each onto their wire
1397
- * message shape (`attempt.content` is always a string). Core never rewrites the
1398
- * request itself — this only turns the recorded attempts into messages.
1399
- *
1400
- * @example
1401
- * ```ts
1402
- * const messages = [...baseMessages, ...renderDecisionAttempts(request)];
1403
- * ```
1404
- */
1405
- function renderDecisionAttempts(request) {
1406
- const types = request.events.map((event) => event.type).join(", ") || "(none)";
1407
- return request.attempts.map((attempt) => userMessage(`Your previous choice failed: ${attempt.reason}. Choose again from: ${types}`));
1408
- }
1409
- /**
1410
- * Validation + retry core for decisions. No provider mechanics — the
1411
- * `executors.decide` is responsible for making the model choose an event; this
1412
- * function only validates the choice and retries on failure, up to
1413
- * `options.maxRetries` (default 2, i.e. up to 3 attempts total).
1414
- *
1415
- * Each attempt is checked in order and can fail one of three ways (recorded
1416
- * as a {@link DecisionAttempt}): `'unknown-event'` (the chosen `type` is not
1417
- * among `request.events`), `'invalid-payload'` (the payload fails that
1418
- * event's schema), or `'rejected-by-guard'` (passes both checks but
1419
- * `options.canTake` returns `false` — a type/payload-legal event the
1420
- * machine's guard rejects right now; omit `canTake` to skip this check).
1421
- * Every prior failed attempt for this call is fed back to the executor on
1422
- * the next attempt via `request.attempts`, so an adapter can render "your
1423
- * last choice failed because X — try again" into the next model call; core
1424
- * never rewrites the request itself. Exhausting all attempts throws
1425
- * {@link AgentDecisionExhaustedError} with the full attempts list.
1426
- *
1427
- * @example
1428
- * ```ts
1429
- * const event = await resolveDecision(request, executors, {
1430
- * canTake: (e) => snapshot.can(e),
1431
- * });
1432
- * ```
1433
- */
1434
- async function resolveDecision(request, executors, options = {}) {
1435
- const executor = executors?.decide;
1436
- if (typeof executor !== "function") throw new AgentError("missing-decide-executor", "resolveDecision(request, executors, ...) takes the EXECUTOR SET (the same object executeAgentRequest takes), and this one has no 'decide' function. Build one with createAiSdkExecutors({ models }), or pass { decide }.");
1437
- const maxRetries = options.maxRetries ?? 2;
1438
- const attempts = [];
1439
- const eventsByType = new Map(request.events.map((event) => [event.type, event]));
1440
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
1441
- options.signal?.throwIfAborted();
1442
- const result = await executor({
1443
- ...request,
1444
- name: request.name ?? (request.id || "agent.decide"),
1445
- attempts: [...attempts]
1446
- }, {
1447
- ...options.signal !== void 0 ? { signal: options.signal } : {},
1448
- ...request.id ? { requestId: request.id } : {}
1449
- });
1450
- if (!result || typeof result !== "object" || typeof result.event !== "object" || result.event === null || typeof result.event.type !== "string") throw new Error(`decide executor must return { event: { type: string, ... } }; got ${JSON.stringify(result)}. Wrap the chosen event: return { event: { type: 'SAFE' } }.`);
1451
- const { event } = result;
1452
- const descriptor = eventsByType.get(event.type);
1453
- if (!descriptor) {
1454
- attempts.push({
1455
- event,
1456
- failure: "unknown-event",
1457
- reason: `'${event.type}' is not among the currently allowed events: ${request.events.map((candidate) => candidate.type).join(", ") || "(none)"}.`
1458
- });
1459
- continue;
1460
- }
1461
- let validatedEvent = event;
1462
- if (descriptor.inputSchema) {
1463
- const { type, ...payload } = event;
1464
- try {
1465
- validatedEvent = {
1466
- ...validateSchemaSync(descriptor.inputSchema, payload),
1467
- type
1468
- };
1469
- } catch (error) {
1470
- attempts.push({
1471
- event,
1472
- failure: "invalid-payload",
1473
- reason: `'${event.type}' payload failed validation: ${error instanceof Error ? error.message : String(error)}`
1474
- });
1475
- continue;
1476
- }
1477
- }
1478
- if (options.canTake?.(validatedEvent) === false) {
1479
- attempts.push({
1480
- event: validatedEvent,
1481
- failure: "rejected-by-guard",
1482
- reason: `'${validatedEvent.type}' is not currently takeable (guard rejected it).`
1483
- });
1484
- continue;
1485
- }
1486
- return validatedEvent;
1487
- }
1488
- throw new AgentDecisionExhaustedError(attempts);
1489
- }
1490
- //#endregion
1491
- Object.defineProperty(exports, "AGENT_MESSAGES_EVENT_TYPE", {
1492
- enumerable: true,
1493
- get: function() {
1494
- return AGENT_MESSAGES_EVENT_TYPE;
1495
- }
1496
- });
1497
- Object.defineProperty(exports, "AGENT_USAGE_TOKEN_FIELDS", {
1498
- enumerable: true,
1499
- get: function() {
1500
- return AGENT_USAGE_TOKEN_FIELDS;
1501
- }
1502
- });
1503
- Object.defineProperty(exports, "AgentDecisionExhaustedError", {
1504
- enumerable: true,
1505
- get: function() {
1506
- return AgentDecisionExhaustedError;
1507
- }
1508
- });
1509
- Object.defineProperty(exports, "AgentError", {
1510
- enumerable: true,
1511
- get: function() {
1512
- return AgentError;
1513
- }
1514
- });
1515
- Object.defineProperty(exports, "AgentInvalidEventPayloadError", {
1516
- enumerable: true,
1517
- get: function() {
1518
- return AgentInvalidEventPayloadError;
1519
- }
1520
- });
1521
- Object.defineProperty(exports, "AgentTruncatedError", {
1522
- enumerable: true,
1523
- get: function() {
1524
- return AgentTruncatedError;
1525
- }
1526
- });
1527
- Object.defineProperty(exports, "DECIDE_ACTOR", {
1528
- enumerable: true,
1529
- get: function() {
1530
- return DECIDE_ACTOR;
1531
- }
1532
- });
1533
- Object.defineProperty(exports, "DEFAULT_AGENT_EXECUTORS", {
1534
- enumerable: true,
1535
- get: function() {
1536
- return DEFAULT_AGENT_EXECUTORS;
1537
- }
1538
- });
1539
- Object.defineProperty(exports, "GENERATE_TEXT_ACTOR", {
1540
- enumerable: true,
1541
- get: function() {
1542
- return GENERATE_TEXT_ACTOR;
1543
- }
1544
- });
1545
- Object.defineProperty(exports, "STREAM_TEXT_ACTOR", {
1546
- enumerable: true,
1547
- get: function() {
1548
- return STREAM_TEXT_ACTOR;
1549
- }
1550
- });
1551
- Object.defineProperty(exports, "USER_INPUT_ACTOR", {
1552
- enumerable: true,
1553
- get: function() {
1554
- return USER_INPUT_ACTOR;
1555
- }
1556
- });
1557
- Object.defineProperty(exports, "agentMessagesEventSchema", {
1558
- enumerable: true,
1559
- get: function() {
1560
- return agentMessagesEventSchema;
1561
- }
1562
- });
1563
- Object.defineProperty(exports, "appendMessages", {
1564
- enumerable: true,
1565
- get: function() {
1566
- return appendMessages;
1567
- }
1568
- });
1569
- Object.defineProperty(exports, "assistantMessage", {
1570
- enumerable: true,
1571
- get: function() {
1572
- return assistantMessage;
1573
- }
1574
- });
1575
- Object.defineProperty(exports, "bindRequestExecutor", {
1576
- enumerable: true,
1577
- get: function() {
1578
- return bindRequestExecutor;
1579
- }
1580
- });
1581
- Object.defineProperty(exports, "buildEnvelopeSchema", {
1582
- enumerable: true,
1583
- get: function() {
1584
- return buildEnvelopeSchema;
1585
- }
1586
- });
1587
- Object.defineProperty(exports, "builtinTextActors", {
1588
- enumerable: true,
1589
- get: function() {
1590
- return builtinTextActors;
1591
- }
1592
- });
1593
- Object.defineProperty(exports, "createDecideActor", {
1594
- enumerable: true,
1595
- get: function() {
1596
- return createDecideActor;
1597
- }
1598
- });
1599
- Object.defineProperty(exports, "createDecisionLogic", {
1600
- enumerable: true,
1601
- get: function() {
1602
- return createDecisionLogic;
1603
- }
1604
- });
1605
- Object.defineProperty(exports, "createDecisionRequest", {
1606
- enumerable: true,
1607
- get: function() {
1608
- return createDecisionRequest;
1609
- }
1610
- });
1611
- Object.defineProperty(exports, "createTextLogic", {
1612
- enumerable: true,
1613
- get: function() {
1614
- return createTextLogic;
1615
- }
1616
- });
1617
- Object.defineProperty(exports, "djb2Hex", {
1618
- enumerable: true,
1619
- get: function() {
1620
- return djb2Hex;
1621
- }
1622
- });
1623
- Object.defineProperty(exports, "executeAgentTextRequest", {
1624
- enumerable: true,
1625
- get: function() {
1626
- return executeAgentTextRequest;
1627
- }
1628
- });
1629
- Object.defineProperty(exports, "executorBoundLogics", {
1630
- enumerable: true,
1631
- get: function() {
1632
- return executorBoundLogics;
1633
- }
1634
- });
1635
- Object.defineProperty(exports, "findNonSerializableContextPaths", {
1636
- enumerable: true,
1637
- get: function() {
1638
- return findNonSerializableContextPaths;
1639
- }
1640
- });
1641
- Object.defineProperty(exports, "getAcceptedEvents", {
1642
- enumerable: true,
1643
- get: function() {
1644
- return getAcceptedEvents;
1645
- }
1646
- });
1647
- Object.defineProperty(exports, "getAgentExecutionOptions", {
1648
- enumerable: true,
1649
- get: function() {
1650
- return getAgentExecutionOptions;
1651
- }
1652
- });
1653
- Object.defineProperty(exports, "getAgentOutputMode", {
1654
- enumerable: true,
1655
- get: function() {
1656
- return getAgentOutputMode;
1657
- }
1658
- });
1659
- Object.defineProperty(exports, "getCallFinishReason", {
1660
- enumerable: true,
1661
- get: function() {
1662
- return getCallFinishReason;
1663
- }
1664
- });
1665
- Object.defineProperty(exports, "getCallUsage", {
1666
- enumerable: true,
1667
- get: function() {
1668
- return getCallUsage;
1669
- }
1670
- });
1671
- Object.defineProperty(exports, "getJsonSchema", {
1672
- enumerable: true,
1673
- get: function() {
1674
- return getJsonSchema;
1675
- }
1676
- });
1677
- Object.defineProperty(exports, "getJsonSchemaSync", {
1678
- enumerable: true,
1679
- get: function() {
1680
- return getJsonSchemaSync;
1681
- }
1682
- });
1683
- Object.defineProperty(exports, "getMachineIdlePredicate", {
1684
- enumerable: true,
1685
- get: function() {
1686
- return getMachineIdlePredicate;
1687
- }
1688
- });
1689
- Object.defineProperty(exports, "getMachineStructuralHash", {
1690
- enumerable: true,
1691
- get: function() {
1692
- return getMachineStructuralHash;
1693
- }
1694
- });
1695
- Object.defineProperty(exports, "getMessageText", {
1696
- enumerable: true,
1697
- get: function() {
1698
- return getMessageText;
1699
- }
1700
- });
1701
- Object.defineProperty(exports, "getRegisteredAgentExecutionOptions", {
1702
- enumerable: true,
1703
- get: function() {
1704
- return getRegisteredAgentExecutionOptions;
1705
- }
1706
- });
1707
- Object.defineProperty(exports, "getStateMeta", {
1708
- enumerable: true,
1709
- get: function() {
1710
- return getStateMeta;
1711
- }
1712
- });
1713
- Object.defineProperty(exports, "getStatePath", {
1714
- enumerable: true,
1715
- get: function() {
1716
- return getStatePath;
1717
- }
1718
- });
1719
- Object.defineProperty(exports, "isAgentMessages", {
1720
- enumerable: true,
1721
- get: function() {
1722
- return isAgentMessages;
1723
- }
1724
- });
1725
- Object.defineProperty(exports, "isDecisionLogic", {
1726
- enumerable: true,
1727
- get: function() {
1728
- return isDecisionLogic;
1729
- }
1730
- });
1731
- Object.defineProperty(exports, "isRecord", {
1732
- enumerable: true,
1733
- get: function() {
1734
- return isRecord;
1735
- }
1736
- });
1737
- Object.defineProperty(exports, "isStandardSchema", {
1738
- enumerable: true,
1739
- get: function() {
1740
- return isStandardSchema;
1741
- }
1742
- });
1743
- Object.defineProperty(exports, "isTextLogic", {
1744
- enumerable: true,
1745
- get: function() {
1746
- return isTextLogic;
1747
- }
1748
- });
1749
- Object.defineProperty(exports, "isUnboundPlaceholder", {
1750
- enumerable: true,
1751
- get: function() {
1752
- return isUnboundPlaceholder;
1753
- }
1754
- });
1755
- Object.defineProperty(exports, "machineIdlePredicates", {
1756
- enumerable: true,
1757
- get: function() {
1758
- return machineIdlePredicates;
1759
- }
1760
- });
1761
- Object.defineProperty(exports, "messagesSchema", {
1762
- enumerable: true,
1763
- get: function() {
1764
- return messagesSchema;
1765
- }
1766
- });
1767
- Object.defineProperty(exports, "missingActor", {
1768
- enumerable: true,
1769
- get: function() {
1770
- return missingActor;
1771
- }
1772
- });
1773
- Object.defineProperty(exports, "normalizeGeneratorResult", {
1774
- enumerable: true,
1775
- get: function() {
1776
- return normalizeGeneratorResult;
1777
- }
1778
- });
1779
- Object.defineProperty(exports, "parseAgentEvent", {
1780
- enumerable: true,
1781
- get: function() {
1782
- return parseAgentEvent;
1783
- }
1784
- });
1785
- Object.defineProperty(exports, "parseModelRef", {
1786
- enumerable: true,
1787
- get: function() {
1788
- return parseModelRef;
1789
- }
1790
- });
1791
- Object.defineProperty(exports, "parseOutput", {
1792
- enumerable: true,
1793
- get: function() {
1794
- return parseOutput;
1795
- }
1796
- });
1797
- Object.defineProperty(exports, "parseStructuredEnvelope", {
1798
- enumerable: true,
1799
- get: function() {
1800
- return parseStructuredEnvelope;
1801
- }
1802
- });
1803
- Object.defineProperty(exports, "renderDecisionAttempts", {
1804
- enumerable: true,
1805
- get: function() {
1806
- return renderDecisionAttempts;
1807
- }
1808
- });
1809
- Object.defineProperty(exports, "resolveDecision", {
1810
- enumerable: true,
1811
- get: function() {
1812
- return resolveDecision;
1813
- }
1814
- });
1815
- Object.defineProperty(exports, "resolveMachineVersion", {
1816
- enumerable: true,
1817
- get: function() {
1818
- return resolveMachineVersion;
1819
- }
1820
- });
1821
- Object.defineProperty(exports, "setAgentExecutionOptions", {
1822
- enumerable: true,
1823
- get: function() {
1824
- return setAgentExecutionOptions;
1825
- }
1826
- });
1827
- Object.defineProperty(exports, "systemMessage", {
1828
- enumerable: true,
1829
- get: function() {
1830
- return systemMessage;
1831
- }
1832
- });
1833
- Object.defineProperty(exports, "toolMessage", {
1834
- enumerable: true,
1835
- get: function() {
1836
- return toolMessage;
1837
- }
1838
- });
1839
- Object.defineProperty(exports, "userInputActor", {
1840
- enumerable: true,
1841
- get: function() {
1842
- return userInputActor;
1843
- }
1844
- });
1845
- Object.defineProperty(exports, "userMessage", {
1846
- enumerable: true,
1847
- get: function() {
1848
- return userMessage;
1849
- }
1850
- });
1851
- Object.defineProperty(exports, "validateSchemaSync", {
1852
- enumerable: true,
1853
- get: function() {
1854
- return validateSchemaSync;
1855
- }
1856
- });