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

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 (48) hide show
  1. package/dist/ai-sdk.cjs +53 -25
  2. package/dist/ai-sdk.d.cts +20 -12
  3. package/dist/ai-sdk.d.mts +20 -12
  4. package/dist/ai-sdk.mjs +53 -25
  5. package/dist/{decision-DxSTqzgZ.mjs → decision-BBZ2jMO1.mjs} +444 -79
  6. package/dist/{decision-1o45_ZrS.cjs → decision-Cv6SfNWl.cjs} +525 -94
  7. package/dist/index.cjs +2224 -1032
  8. package/dist/index.d.cts +413 -275
  9. package/dist/index.d.mts +413 -275
  10. package/dist/index.mjs +2163 -983
  11. package/dist/machines.cjs +30 -2
  12. package/dist/machines.d.cts +1 -1
  13. package/dist/machines.d.mts +1 -1
  14. package/dist/machines.mjs +30 -2
  15. package/dist/openai.cjs +447 -0
  16. package/dist/openai.d.cts +177 -0
  17. package/dist/openai.d.mts +177 -0
  18. package/dist/openai.mjs +437 -0
  19. package/dist/otel.cjs +3 -6
  20. package/dist/otel.d.cts +1 -1
  21. package/dist/otel.d.mts +1 -1
  22. package/dist/otel.mjs +3 -6
  23. package/dist/run-agent-Ck7Sa3Tb.d.mts +968 -0
  24. package/dist/run-agent-DWqA8KiI.d.cts +968 -0
  25. package/dist/setup-agent-CeO2fARv.mjs +639 -0
  26. package/dist/setup-agent-Dl2VELLi.cjs +663 -0
  27. package/dist/{text-logic-BaxPrcLk.d.cts → text-logic-C7bSnH_B.d.cts} +195 -44
  28. package/dist/{text-logic-DhfFWzu9.d.mts → text-logic-DJBK3LbG.d.mts} +195 -44
  29. package/dist/{types-DSdj2tGs.d.cts → types-CjHk5OdH.d.cts} +4 -19
  30. package/dist/{types-CvWRGFxP.d.mts → types-DWA6X9R-.d.mts} +4 -19
  31. package/package.json +17 -21
  32. package/readme.md +32 -17
  33. package/dist/errors-BQRk9eiZ.d.cts +0 -19
  34. package/dist/errors-C9rxnWbX.d.mts +0 -19
  35. package/dist/errors-CeSXQx0v.mjs +0 -23
  36. package/dist/errors-DUBBzRLP.cjs +0 -28
  37. package/dist/event-log-store-Bz7HDBkE.d.cts +0 -145
  38. package/dist/event-log-store-DmIDosD6.mjs +0 -427
  39. package/dist/event-log-store-a_TKy1gk.cjs +0 -468
  40. package/dist/event-log-store-hrA1vqtN.d.mts +0 -145
  41. package/dist/run-agent-0fpEZ1wJ.d.mts +0 -1098
  42. package/dist/run-agent-Zbkx7XP8.d.cts +0 -1098
  43. package/dist/setup-agent-DkCTy5Eu.cjs +0 -1707
  44. package/dist/setup-agent-nSFYrNTP.mjs +0 -1587
  45. package/dist/sqlite.cjs +0 -129
  46. package/dist/sqlite.d.cts +0 -57
  47. package/dist/sqlite.d.mts +0 -57
  48. package/dist/sqlite.mjs +0 -127
@@ -1,1707 +0,0 @@
1
- const require_errors = require("./errors-DUBBzRLP.cjs");
2
- const require_decision = require("./decision-1o45_ZrS.cjs");
3
- const require_event_log_store = require("./event-log-store-a_TKy1gk.cjs");
4
- require("./validate.cjs");
5
- let xstate = require("xstate");
6
- //#region src/messages.ts
7
- function addMessages(resolve) {
8
- return (args) => {
9
- const resolved = typeof resolve === "function" ? resolve(args) : resolve;
10
- return [...args.context.messages, ...Array.isArray(resolved) ? resolved : [resolved]];
11
- };
12
- }
13
- /**
14
- * Builds a transition-function result that appends one or more
15
- * {@link AgentMessage}s to a context's `messages` array. `resolve` is either
16
- * a message (or array of messages) or a function of `{ context, event }`
17
- * returning them; the returned function is meant to be used directly as (or
18
- * composed into) a transition's result, e.g. `on: { USER_REPLIED:
19
- * agent.appendMessages(({ event }) => userMessage(event.text)) }`. Requires
20
- * `messages: AgentMessage[]` on context — see {@link messagesSchema} for a
21
- * ready-made schema for that field.
22
- *
23
- * @example
24
- * ```ts
25
- * on: {
26
- * USER_REPLIED: appendMessages(({ event }) => userMessage(event.text)),
27
- * }
28
- * ```
29
- */
30
- function appendMessages(resolve) {
31
- return (args) => ({ context: { messages: addMessages(resolve)(args) } });
32
- }
33
- const KNOWN_PART_TYPES = new Set([
34
- "text",
35
- "image",
36
- "file",
37
- "tool-call",
38
- "tool-result"
39
- ]);
40
- const USER_PART_TYPES = new Set([
41
- "text",
42
- "image",
43
- "file"
44
- ]);
45
- const ASSISTANT_PART_TYPES = new Set([
46
- "text",
47
- "file",
48
- "tool-call",
49
- "tool-result"
50
- ]);
51
- const TOOL_PART_TYPES = new Set(["tool-result"]);
52
- const TOOL_RESULT_CONTENT_PART_TYPES = new Set(["text", "image"]);
53
- function isKnownPart(part) {
54
- const type = part && typeof part === "object" ? part.type : void 0;
55
- return typeof type === "string" && KNOWN_PART_TYPES.has(type);
56
- }
57
- function requireString(part, type, field) {
58
- return typeof part[field] === "string" ? void 0 : `${type} part requires a string "${field}"`;
59
- }
60
- function isMediaData(value) {
61
- return typeof value === "string" || value instanceof Uint8Array || value instanceof ArrayBuffer || value instanceof URL;
62
- }
63
- function requireMediaData(part, type, field) {
64
- return isMediaData(part[field]) ? void 0 : `${type} part requires "${field}" to be a string, Uint8Array, ArrayBuffer, or URL`;
65
- }
66
- const TOOL_RESULT_OUTPUT_TYPES = new Set([
67
- "text",
68
- "json",
69
- "error-text",
70
- "error-json",
71
- "content"
72
- ]);
73
- function validateToolResultOutput(output) {
74
- if (!output || typeof output !== "object" || Array.isArray(output)) return "tool-result part requires an \"output\" object";
75
- const record = output;
76
- const outputType = record.type;
77
- if (typeof outputType !== "string" || !TOOL_RESULT_OUTPUT_TYPES.has(outputType)) return `Unknown tool-result output type: ${JSON.stringify(outputType)}`;
78
- if (outputType === "text" || outputType === "error-text") {
79
- if (typeof record.value !== "string") return `tool-result output of type "${outputType}" requires a string "value"`;
80
- return;
81
- }
82
- if (outputType === "content") {
83
- if (!Array.isArray(record.value)) return "tool-result output of type \"content\" requires an array \"value\"";
84
- for (const contentPart of record.value) {
85
- const error = validatePart(contentPart, TOOL_RESULT_CONTENT_PART_TYPES, "tool-result output content");
86
- if (error) return error;
87
- }
88
- return;
89
- }
90
- return record.value !== void 0 ? void 0 : `tool-result output of type "${outputType}" requires a "value"`;
91
- }
92
- function validatePart(part, allowedTypes, location) {
93
- if (!isKnownPart(part)) {
94
- const type = part && typeof part === "object" ? part.type : void 0;
95
- return `Unknown message part type: ${JSON.stringify(type)}`;
96
- }
97
- if (!allowedTypes.has(part.type)) return `${location} does not allow "${part.type}" parts`;
98
- const record = part;
99
- switch (record.type) {
100
- case "text": return requireString(record, "text", "text");
101
- case "image": return requireMediaData(record, "image", "image");
102
- case "file": return requireMediaData(record, "file", "data") ?? requireString(record, "file", "mediaType");
103
- 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");
104
- case "tool-result": return requireString(record, "tool-result", "toolCallId") ?? requireString(record, "tool-result", "toolName") ?? validateToolResultOutput(record.output);
105
- default: return;
106
- }
107
- }
108
- function validatePartsArray(content, allowedTypes, location) {
109
- if (!Array.isArray(content)) return "Expected content to be a string or an array of parts";
110
- for (const part of content) {
111
- const error = validatePart(part, allowedTypes, location);
112
- if (error) return error;
113
- }
114
- }
115
- /**
116
- * A {@link StandardSchemaV1} validating an `AgentMessage[]` context field —
117
- * checks that every message has a known `role` (`system`/`user`/`assistant`/
118
- * `tool`) and that `content` is either a string (where the role allows it) or
119
- * an array of role-appropriate parts whose required fields and media payloads
120
- * have the right runtime types (extra fields are allowed). Use it directly as
121
- * a context schema's `messages` field when authoring with `createAgentSchemas`.
122
- */
123
- const messagesSchema = { "~standard": {
124
- version: 1,
125
- vendor: "statelyai-agent",
126
- validate(value) {
127
- if (!Array.isArray(value)) return { issues: [{ message: "Expected an array of agent messages" }] };
128
- for (const message of value) {
129
- if (!message || typeof message !== "object") return { issues: [{ message: "Expected an array of agent messages" }] };
130
- const role = message.role;
131
- const content = message.content;
132
- if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") return { issues: [{ message: `Unknown message role: ${JSON.stringify(role)}` }] };
133
- if (role === "system") {
134
- if (typeof content !== "string") return { issues: [{ message: "system message content must be a string" }] };
135
- continue;
136
- }
137
- if (role === "tool") {
138
- const error = validatePartsArray(content, TOOL_PART_TYPES, "tool message content");
139
- if (error) return { issues: [{ message: error }] };
140
- continue;
141
- }
142
- if (typeof content === "string") continue;
143
- const error = validatePartsArray(content, role === "user" ? USER_PART_TYPES : ASSISTANT_PART_TYPES, `${role} message content`);
144
- if (error) return { issues: [{ message: error }] };
145
- }
146
- return { value };
147
- }
148
- } };
149
- //#endregion
150
- //#region src/steps.ts
151
- /**
152
- * The step envelope: an INTERNAL, per-model-call-checkpoint view of an agent
153
- * machine (`AgentStep` = snapshot + actions + discovered requests + done). It
154
- * once backed a public step API; the public step path is now the thin
155
- * effect/replay loop in `./effects.ts` (re-exported from `@statelyai/agent`).
156
- * These helpers survive as internals that `runAgent`, `verify.ts`, and the
157
- * effect path build on — the only symbol still on the public surface is
158
- * {@link executeAgentRequest} (the `text`-effect resolver).
159
- * @module
160
- */
161
- /** @internal Normalizes current and legacy XState invoke effect shapes. */
162
- function getInvokeEffectMetadata(action) {
163
- if (action.type === "@xstate.spawn") return action;
164
- if (action.type === "xstate.spawnChild") {
165
- const params = action.params;
166
- return params ? {
167
- ...params,
168
- logic: action.logic
169
- } : void 0;
170
- }
171
- if (action.type === "@xstate.start" && typeof action.src === "string") return action;
172
- }
173
- /**
174
- * Scans a set of executable actions (as returned by xstate's `transition`/
175
- * `initialTransition`) for spawned `TextLogic`/`DecisionLogic` invokes and
176
- * lowers each into an {@link AgentStepRequest}. Pass `options.machine` to
177
- * pre-fill `schemas`/`actors` from the machine's registered `setupAgent`
178
- * options instead of passing them by hand.
179
- * `options.snapshot` is required to resolve a decision's candidate events
180
- * (intersecting declared `allowedEvents` with what's currently legal) — omit
181
- * it and decision requests report an empty `events` list.
182
- *
183
- * @internal
184
- */
185
- function getAgentRequests(actions, { machine, ...options } = {}) {
186
- if (machine) options = {
187
- ...require_decision.getRegisteredAgentExecutionOptions(machine),
188
- ...options
189
- };
190
- return actions.flatMap((action) => {
191
- const params = getInvokeEffectMetadata(action);
192
- if (!params || typeof params.src !== "string") return [];
193
- if (typeof params.id !== "string" || params.id.length === 0) throw new Error(`Agent invoke '${params.src}' must define a durable string id.`);
194
- const registeredLogic = require_decision.isTextLogic(params.logic) || require_decision.isDecisionLogic(params.logic) ? params.logic : options.actors?.[params.src];
195
- if (require_decision.isDecisionLogic(registeredLogic)) {
196
- const decisionRequest = registeredLogic.request(params.input);
197
- const allowedEventTypes = registeredLogic.allowedEventTypes?.(params.input);
198
- const events = options.snapshot ? require_decision.getAcceptedEvents(options.snapshot, {
199
- events: options.events,
200
- schemas: options.schemas,
201
- eventTypes: allowedEventTypes,
202
- eventToolName: options.eventToolName
203
- }) : [];
204
- return [{
205
- ...decisionRequest,
206
- id: params.id,
207
- events
208
- }];
209
- }
210
- const textLogic = require_decision.isTextLogic(registeredLogic) ? registeredLogic : void 0;
211
- const input = textLogic ? textLogic.request(params.input) : void 0;
212
- if (!input) return [];
213
- return [{
214
- kind: "text",
215
- id: params.id,
216
- src: params.src,
217
- ...textLogic ? { mode: textLogic.mode } : {},
218
- input,
219
- tools: input.tools ?? {},
220
- events: []
221
- }];
222
- });
223
- }
224
- /**
225
- * Builds the canonical `xstate.done.actor` event xstate's `transition()`
226
- * expects to resolve a spawned invoke — including the child session identity
227
- * that rejects a stale completion from an earlier incarnation.
228
- *
229
- * @internal
230
- */
231
- function doneEvent(snapshot, request, output) {
232
- const id = typeof request === "string" ? request : request.id;
233
- const child = snapshot.children[id];
234
- return {
235
- type: "xstate.done.actor",
236
- actorId: id,
237
- ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {},
238
- output
239
- };
240
- }
241
- /**
242
- * Applies a request's `output` as a done event via `transition(...)`,
243
- * returning the raw `[snapshot, actions]` tuple. Lower-level than
244
- * {@link resolveAgentStep} — that helper wraps this and also runs
245
- * {@link getAgentRequests} to produce the next {@link AgentStep}.
246
- *
247
- * @internal
248
- */
249
- function transitionResult(logic, snapshot, request, output) {
250
- const event = doneEvent(snapshot, request, output);
251
- const result = (0, xstate.transition)(logic, snapshot, event);
252
- applyFinalStateOutput(logic, result[0], event);
253
- return result;
254
- }
255
- /**
256
- * Starts a machine and returns its first {@link AgentStep} — the step-path
257
- * equivalent of `initialTransition` plus request discovery. Begins the
258
- * durable/per-model-call-checkpoint loop: resolve each `step.requests` entry
259
- * (via {@link executeAgentRequest} for `kind: 'text'`, or
260
- * {@link resolveDecision} for `kind: 'decision'`), then advance with
261
- * {@link resolveAgentStep} or {@link transitionAgentStep}.
262
- */
263
- function initialAgentStep(machine, input, options) {
264
- const [snapshot, actions] = (0, xstate.initialTransition)(machine, input);
265
- return createAgentStep(machine, snapshot, actions, require_decision.getRegisteredAgentExecutionOptions(machine, options));
266
- }
267
- /**
268
- * Applies an externally-sent event (e.g. a decision's chosen event, or a
269
- * human's reply) and returns the next {@link AgentStep}. Accepts **either**
270
- * a raw snapshot **or** a prior `AgentStep` as the second argument —
271
- * `.snapshot` is unwrapped automatically, so callers can thread the whole
272
- * step object through without manually plucking the snapshot out.
273
- */
274
- function transitionAgentStep(machine, snapshotOrStep, event, options) {
275
- const [nextSnapshot, actions] = (0, xstate.transition)(machine, isAgentStep(snapshotOrStep) ? snapshotOrStep.snapshot : snapshotOrStep, event);
276
- return createAgentStep(machine, nextSnapshot, actions, require_decision.getRegisteredAgentExecutionOptions(machine, options));
277
- }
278
- /**
279
- * Applies a resolved text request's output (a `kind: 'text'`
280
- * {@link AgentRequest} — not a decision) as a done event and returns the
281
- * next {@link AgentStep}. For decisions, resolve with `resolveDecision`
282
- * (which returns a {@link ChosenEvent}) and apply it with
283
- * {@link transitionAgentStep} instead — a decision has no output value of
284
- * its own to feed here.
285
- */
286
- function resolveAgentStep(machine, step, request, output, options) {
287
- const [snapshot, actions] = transitionResult(machine, step.snapshot, request, output);
288
- return createAgentStep(machine, snapshot, actions, require_decision.getRegisteredAgentExecutionOptions(machine, options));
289
- }
290
- async function executeAgentRequest(requestOrEffect, executors) {
291
- if (requestOrEffect.kind === "decision") throw new Error("executeAgentRequest(...) is text-only. Resolve a 'decision' request with resolveDecision(request, executors, ...) instead.");
292
- const request = "requestId" in requestOrEffect ? {
293
- kind: "text",
294
- id: requestOrEffect.requestId,
295
- src: "",
296
- mode: requestOrEffect.mode,
297
- input: requestOrEffect.request,
298
- tools: requestOrEffect.request.tools ?? {},
299
- events: []
300
- } : requestOrEffect;
301
- assertTextExecutor(request, executors);
302
- const { output, raw } = await require_decision.executeAgentTextRequest(request.mode ?? "generate", request.id, request.input, executors, request.tools);
303
- return {
304
- output: request.input.outputSchema ? require_decision.validateSchemaSync(request.input.outputSchema, output) : output,
305
- raw
306
- };
307
- }
308
- function assertTextExecutor(request, executors) {
309
- const mode = request.mode ?? "generate";
310
- const kind = mode === "stream" ? "streamText" : "generateText";
311
- if (!(mode === "stream" ? executors.streamText : executors.generateText)) throw new Error(`this step's text request '${request.src}' needs a '${kind}' executor but none was provided.`);
312
- }
313
- function createAgentStep(machine, snapshot, actions, options) {
314
- applyFinalStateOutput(machine, snapshot);
315
- return {
316
- snapshot,
317
- actions,
318
- requests: getAgentRequests(actions, {
319
- ...options,
320
- snapshot
321
- }),
322
- done: snapshot.status === "done"
323
- };
324
- }
325
- function resolveStateValueConfig(config, value) {
326
- if (typeof value === "string") return config.states?.[value];
327
- if (!value || typeof value !== "object") return;
328
- for (const [key, childValue] of Object.entries(value)) {
329
- const childConfig = config.states?.[key];
330
- if (!childConfig) continue;
331
- if (childConfig.type === "final") return childConfig;
332
- const nested = resolveStateValueConfig(childConfig, childValue);
333
- if (nested) return nested;
334
- }
335
- }
336
- function applyFinalStateOutput(logic, snapshot, event) {
337
- const machineSnapshot = snapshot;
338
- if (machineSnapshot.status !== "done" || machineSnapshot.output !== void 0 || !("config" in logic)) return;
339
- const config = logic.config;
340
- if (!config) return;
341
- const output = resolveStateValueConfig(config, machineSnapshot.value)?.output;
342
- if (output === void 0) return;
343
- machineSnapshot.output = typeof output === "function" ? output({
344
- context: machineSnapshot.context,
345
- event
346
- }) : output;
347
- }
348
- function isAgentStep(value) {
349
- return !!value && typeof value === "object" && "snapshot" in value && "actions" in value && "requests" in value;
350
- }
351
- //#endregion
352
- //#region src/effects.ts
353
- /**
354
- * The effect/replay path: the library's new append-only-log core. Two
355
- * primitives sit under every host loop:
356
- *
357
- * - {@link getAgentEffects} maps a transition's ordered executable actions
358
- * (reconciled with the still-owed effects visible only on the snapshot) into
359
- * an ordered {@link AgentEffect} list a host starts at the frontier.
360
- * - {@link replay} folds a journal of EXTERNAL inputs through xstate's pure
361
- * `initialTransition`/`transition` WITHOUT executing anything, returning the
362
- * final snapshot plus the effects still owed — crash recovery, fork resume,
363
- * and time travel in one function.
364
- *
365
- * The journal of external inputs (effect completions with outputs inline, user
366
- * events, timer firings) is the authoritative artifact; raised/internal events
367
- * are never journaled (replay re-derives them). A reserved first entry
368
- * {@link initEntry} carries the machine input so a log is self-contained.
369
- * @module
370
- */
371
- /**
372
- * The reserved journal event type of the {@link initEntry} first entry: it
373
- * carries the machine `input` so a log replays with no side-channel. Named in
374
- * the `agent.*` builtin-actor namespace so it never collides with a machine's
375
- * own event vocabulary. Consumed by {@link replay}; never fed to `transition`.
376
- */
377
- const AGENT_INIT_EVENT_TYPE = "@agent.init";
378
- /**
379
- * The reserved event type `runAgent` delivers to the running machine after
380
- * every settled model call that reported usage — the one seam that puts a
381
- * call's tokens in reach of ordinary `context` and guards, so a token budget
382
- * is a plain machine transition instead of host bookkeeping.
383
- *
384
- * Like {@link AGENT_INIT_EVENT_TYPE} it lives in the reserved `@agent.*`
385
- * namespace: it can never collide with a machine's own vocabulary, and
386
- * `getAcceptedEvents`/`parseAgentEvent` never offer it — a model can neither
387
- * be shown it as a decision candidate nor forge one.
388
- *
389
- * Delivery is opt-in BY CONSTRUCTION: the event is sent only when the live
390
- * snapshot can currently take it (i.e. the machine declares an
391
- * `'@agent.usage'` transition, usually machine-level `on`). A machine without
392
- * one sees no extra transition, no extra trace event, and no extra log entry.
393
- * When it IS taken it rides the event log like any other external input, so
394
- * events-only recovery (`runAgent({ events })`) replays the folded tokens
395
- * without re-calling a model.
396
- */
397
- const AGENT_USAGE_EVENT_TYPE = "@agent.usage";
398
- /**
399
- * Creates a JSON-safe, self-describing entry and records the state/effect
400
- * hashes produced after replaying it. `entries` must be the complete prefix.
401
- */
402
- function createReplayEntry(machine, entries, event, options = {}) {
403
- const index = entries.length;
404
- const machineId = machine.config.id ?? machine.id ?? "(machine)";
405
- const machineVersion = options.machineVersion ?? require_decision.resolveMachineVersion(machine);
406
- const entry = {
407
- schemaVersion: 1,
408
- id: options.id ?? `evt_${String(index).padStart(8, "0")}`,
409
- index,
410
- recordedAt: options.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
411
- machineId,
412
- machineVersion,
413
- event: normalizeEventErrors(event),
414
- ...options.causationId !== void 0 ? { causationId: options.causationId } : {},
415
- ...options.correlationId !== void 0 ? { correlationId: options.correlationId } : {},
416
- ...options.metadata !== void 0 ? { metadata: options.metadata } : {}
417
- };
418
- require_event_log_store.assertAgentLogEntry(entry);
419
- if (options.verification !== false) {
420
- const result = replay(machine, [...entries, entry], {
421
- machineVersion,
422
- verify: false
423
- });
424
- entry.verification = replayVerification(result.snapshot, result.effects);
425
- }
426
- return entry;
427
- }
428
- /** Reserved first replay entry carrying machine input and verification hashes. */
429
- function initEntry(machine, input, options = {}) {
430
- return createReplayEntry(machine, [], {
431
- type: AGENT_INIT_EVENT_TYPE,
432
- ...input !== void 0 ? { input } : {}
433
- }, options);
434
- }
435
- /** Normalizes a mixed `EventObject | AgentLogEntry` history into bare events. */
436
- function toEvents(history) {
437
- if (!history) return [];
438
- return history.map((entry) => {
439
- const candidate = entry;
440
- return candidate && typeof candidate === "object" && "event" in candidate && candidate.event ? candidate.event : entry;
441
- });
442
- }
443
- function rebindActorSession(event, snapshot, sessions) {
444
- const actorEvent = event;
445
- if (typeof actorEvent.actorId !== "string" || typeof actorEvent.sessionId !== "string") return event;
446
- const key = `${actorEvent.actorId}\0${actorEvent.sessionId}`;
447
- let sessionId = sessions.get(key);
448
- if (!sessionId) {
449
- const child = snapshot.children[actorEvent.actorId];
450
- if (typeof child?.sessionId !== "string") return event;
451
- sessionId = child.sessionId;
452
- sessions.set(key, sessionId);
453
- }
454
- return {
455
- ...event,
456
- sessionId
457
- };
458
- }
459
- const DONE_ACTOR_EVENT_TYPE = "xstate.done.actor";
460
- const ERROR_ACTOR_EVENT_TYPE = "xstate.error.actor";
461
- function isInvokeCompletion(event, id) {
462
- return (event.type === DONE_ACTOR_EVENT_TYPE || event.type === ERROR_ACTOR_EVENT_TYPE) && event.actorId === id;
463
- }
464
- /**
465
- * 1-based occurrence for an invoke/spawn site: `1 + completions` (done AND
466
- * error both count — an error is a semantic completion) for `id` in `events`.
467
- */
468
- function invokeOccurrence(events, id) {
469
- let count = 0;
470
- for (const event of events) if (isInvokeCompletion(event, id)) count++;
471
- return count + 1;
472
- }
473
- /** The exact canonical done/error events xstate's actor system delivers for an invoke `id`. */
474
- function invokeEventMinters(id, snapshot) {
475
- const child = snapshot.children[id];
476
- const identity = {
477
- actorId: id,
478
- ...typeof child?.sessionId === "string" ? { sessionId: child.sessionId } : {}
479
- };
480
- return {
481
- toDoneEvent: (output) => ({
482
- type: DONE_ACTOR_EVENT_TYPE,
483
- output,
484
- ...identity
485
- }),
486
- toErrorEvent: (error) => ({
487
- type: ERROR_ACTOR_EVENT_TYPE,
488
- error,
489
- ...identity
490
- })
491
- };
492
- }
493
- /** Splits an `xstate.after.<delayKey>.<statePath>` id into its parts. */
494
- function parseAfterId(afterId) {
495
- if (!afterId.startsWith("xstate.after.")) return;
496
- const rest = afterId.slice(13);
497
- const dot = rest.indexOf(".");
498
- if (dot === -1) return;
499
- return {
500
- delayKey: rest.slice(0, dot),
501
- statePath: rest.slice(dot + 1)
502
- };
503
- }
504
- function buildInvokeEffect(meta, mapped, events, snapshot, options) {
505
- const id = typeof meta.id === "string" ? meta.id : void 0;
506
- if (!id) return;
507
- const requestId = `${id}#${invokeOccurrence(events, id)}`;
508
- if (mapped?.kind === "text") return {
509
- kind: "text",
510
- requestId,
511
- request: mapped.input,
512
- mode: mapped.mode,
513
- ...invokeEventMinters(id, snapshot)
514
- };
515
- if (mapped?.kind === "decision") return {
516
- kind: "decision",
517
- requestId,
518
- request: mapped
519
- };
520
- const logic = require_decision.isTextLogic(meta.logic) || require_decision.isDecisionLogic(meta.logic) ? meta.logic : typeof meta.src === "string" ? options.actors?.[meta.src] : void 0;
521
- if (require_decision.isTextLogic(logic)) return {
522
- kind: "text",
523
- requestId,
524
- request: logic.request(meta.input),
525
- mode: logic.mode,
526
- ...invokeEventMinters(id, snapshot)
527
- };
528
- if (require_decision.isDecisionLogic(logic)) {
529
- const base = logic.request(meta.input);
530
- const eventTypes = logic.allowedEventTypes?.(meta.input);
531
- const candidateEvents = require_decision.getAcceptedEvents(snapshot, {
532
- events: options.schemas?.events,
533
- schemas: options.schemas,
534
- eventTypes
535
- });
536
- return {
537
- kind: "decision",
538
- requestId,
539
- request: {
540
- ...base,
541
- id,
542
- events: candidateEvents
543
- }
544
- };
545
- }
546
- return {
547
- kind: "task",
548
- requestId,
549
- id,
550
- src: typeof meta.src === "string" ? meta.src : id,
551
- input: meta.input,
552
- ...invokeEventMinters(id, snapshot)
553
- };
554
- }
555
- /**
556
- * Maps a transition's ORDERED executable actions (plus the still-owed effects
557
- * visible only on the snapshot) into an ordered {@link AgentEffect} list the
558
- * host starts at the frontier.
559
- *
560
- * Ordering is load-bearing: a single transition's actions are emitted in
561
- * document order (a custom entry action, a spawn, and a `sendTo` in that order
562
- * yield `execute`, then `task`/agent effect, then `execute` — never a
563
- * reordered set). Effects visible only on the snapshot (children spawned by
564
- * an EARLIER transition that
565
- * have not completed yet — the fan-out / crash-resume case) are appended after
566
- * the action-derived effects, deduped by site id.
567
- *
568
- * Every `requestId` is `${siteId}#${n}`, `n` the 1-based occurrence derived
569
- * from `options.history` — so the same log yields identical requestIds on every
570
- * replay. XState alpha.24 prunes a child when its matching completion is
571
- * folded; the history check also prevents older/restored snapshots from
572
- * re-surfacing completed work. A re-entered invoke site instead re-derives
573
- * from the action list each fresh entry (so `#2`, `#3`, … stay correct).
574
- */
575
- function getAgentEffects(machine, snapshot, actions, options = {}) {
576
- const resolved = require_decision.getRegisteredAgentExecutionOptions(machine, options);
577
- const events = toEvents(options.history);
578
- const rawActions = actions;
579
- const requests = getAgentRequests(rawActions, {
580
- ...resolved,
581
- snapshot
582
- });
583
- const requestById = new Map(requests.map((request) => [request.id, request]));
584
- const effects = [];
585
- const emitted = /* @__PURE__ */ new Set();
586
- for (const rawAction of rawActions) {
587
- const meta = getInvokeEffectMetadata(rawAction);
588
- if (meta) {
589
- const effect = buildInvokeEffect(meta, typeof meta.id === "string" ? requestById.get(meta.id) : void 0, events, snapshot, resolved);
590
- if (effect && typeof meta.id === "string") {
591
- effects.push(effect);
592
- emitted.add(meta.id);
593
- }
594
- continue;
595
- }
596
- if (rawAction.type === "@xstate.raise" && typeof rawAction.delay === "number") {
597
- const afterId = typeof rawAction.id === "string" ? rawAction.id : "";
598
- const parsed = parseAfterId(afterId);
599
- const siteId = parsed ? `${parsed.statePath}#${parsed.delayKey}` : afterId;
600
- const event = {
601
- type: "xstate.timer",
602
- id: afterId
603
- };
604
- const firings = events.filter((candidate) => candidate.type === event.type && candidate.id === afterId).length;
605
- effects.push({
606
- kind: "delay",
607
- requestId: `${siteId}#${firings + 1}`,
608
- id: afterId,
609
- delayMs: rawAction.delay,
610
- event
611
- });
612
- continue;
613
- }
614
- if (rawAction.type === "@xstate.start" || rawAction.type === "@xstate.stop" || rawAction.type === "@xstate.terminate" || rawAction.type === "@xstate.raise" || rawAction.type === "registerChild") continue;
615
- if (typeof rawAction.exec === "function") {
616
- const action = rawAction;
617
- effects.push({
618
- kind: "execute",
619
- action: rawAction,
620
- exec: () => {
621
- action.exec();
622
- }
623
- });
624
- }
625
- }
626
- const children = snapshot.children;
627
- for (const [id, child] of Object.entries(children ?? {})) {
628
- if (emitted.has(id) || invokeOccurrence(events, id) > 1) continue;
629
- const ref = child;
630
- if (typeof ref?.getSnapshot !== "function") continue;
631
- const effect = buildInvokeEffect({
632
- id,
633
- src: ref.src,
634
- input: ref.getSnapshot().input,
635
- logic: ref.logic
636
- }, requestById.get(id), events, snapshot, resolved);
637
- if (effect) {
638
- effects.push(effect);
639
- emitted.add(id);
640
- }
641
- }
642
- return effects;
643
- }
644
- var AgentReplayMachineMismatchError = class extends require_errors.AgentError {
645
- constructor(eventId, index, expected, actual) {
646
- super("replay-machine-mismatch", `Replay entry '${eventId}' at index ${index} targets machine '${actual.machineId}'@'${actual.machineVersion}', expected '${expected.machineId}'@'${expected.machineVersion}'.`);
647
- this.eventId = eventId;
648
- this.index = index;
649
- this.expected = expected;
650
- this.actual = actual;
651
- this.name = "AgentReplayMachineMismatchError";
652
- }
653
- };
654
- var AgentReplayDivergenceError = class extends require_errors.AgentError {
655
- constructor(eventId, index, kind, expected, actual) {
656
- super("replay-divergence", kind === "missing-verification" ? `Replay entry '${eventId}' at index ${index} has no verification hashes.` : `Replay diverged after '${eventId}' at index ${index} (${kind}): expected '${expected}', got '${actual}'.`);
657
- this.eventId = eventId;
658
- this.index = index;
659
- this.kind = kind;
660
- this.expected = expected;
661
- this.actual = actual;
662
- this.name = "AgentReplayDivergenceError";
663
- }
664
- };
665
- /**
666
- * Folds a journal through `initialTransition`/`transition` WITHOUT executing
667
- * anything, then returns the final snapshot plus the still-owed effects
668
- * ({@link getAgentEffects} of the final frontier, occurrence counts taken from
669
- * the whole log). Crash recovery, fork resume, and time travel in one call.
670
- *
671
- * `entries` is a versioned {@link AgentLogEntry} array. A reserved
672
- * {@link initEntry} first envelope carries `{ type: '@agent.init', input }`,
673
- * so a complete log replays with no side-channel; when absent,
674
- * `options.input` is used instead.
675
- * Raised/internal events are never in the journal — replay re-derives them
676
- * deterministically from the machine's own logic.
677
- */
678
- /**
679
- * Validates a replay entry sequence: envelope shape, contiguity from index 0,
680
- * unique event ids, and machine identity.
681
- *
682
- * @internal
683
- */
684
- function validateReplayEntries(entries, expected, label) {
685
- const eventIds = /* @__PURE__ */ new Set();
686
- for (let index = 0; index < entries.length; index++) {
687
- const entry = entries[index];
688
- require_event_log_store.assertAgentLogEntry(entry);
689
- if (entry.index !== index) throw new Error(`${label} must be contiguous from index 0; found entry.index ${entry.index} at position ${index}.`);
690
- if (eventIds.has(entry.id)) throw new Error(`${label} contain duplicate event id '${entry.id}'.`);
691
- eventIds.add(entry.id);
692
- if (entry.machineId !== expected.machineId || entry.machineVersion !== expected.machineVersion) throw new AgentReplayMachineMismatchError(entry.id, entry.index, expected, {
693
- machineId: entry.machineId,
694
- machineVersion: entry.machineVersion
695
- });
696
- }
697
- }
698
- function replay(machine, entries, options = {}) {
699
- validateReplayEntries(entries, {
700
- machineId: machine.config.id ?? machine.id ?? "(machine)",
701
- machineVersion: options.machineVersion ?? require_decision.resolveMachineVersion(machine)
702
- }, "Replay entries");
703
- const events = toEvents(entries);
704
- const initOffset = events[0]?.type === "@agent.init" ? 1 : 0;
705
- const input = initOffset ? events[0].input : options.input;
706
- const journal = events.slice(initOffset);
707
- const journalEntries = entries.slice(initOffset);
708
- let [snapshot, actions] = (0, xstate.initialTransition)(machine, input);
709
- let effects = getAgentEffects(machine, snapshot, actions, {
710
- ...options,
711
- history: entries.slice(0, initOffset)
712
- });
713
- if (initOffset) verifyEntry(entries[0], snapshot, effects, options.verify);
714
- const sessions = /* @__PURE__ */ new Map();
715
- for (let index = 0; index < journal.length; index++) {
716
- const event = journal[index];
717
- const reboundEvent = rebindActorSession(event, snapshot, sessions);
718
- [snapshot, actions] = (0, xstate.transition)(machine, snapshot, reboundEvent);
719
- const consumed = entries.slice(0, initOffset + index + 1);
720
- effects = getAgentEffects(machine, snapshot, actions, {
721
- ...options,
722
- history: consumed
723
- });
724
- verifyEntry(journalEntries[index], snapshot, effects, options.verify);
725
- }
726
- return {
727
- snapshot,
728
- effects
729
- };
730
- }
731
- /** Structural event-tail, logical-state, and owed-effect comparison. */
732
- function diffEventLogs(machine, parentEntries, forkEntries, options = {}) {
733
- let commonLength = 0;
734
- while (commonLength < parentEntries.length && commonLength < forkEntries.length && stableJson(parentEntries[commonLength]) === stableJson(forkEntries[commonLength])) commonLength++;
735
- const parent = replay(machine, parentEntries, options);
736
- const fork = replay(machine, forkEntries, options);
737
- const parentState = logicalReplayState(parent.snapshot);
738
- const forkState = logicalReplayState(fork.snapshot);
739
- return {
740
- commonPrefix: {
741
- length: commonLength,
742
- ...commonLength > 0 ? { throughEventId: parentEntries[commonLength - 1].id } : {}
743
- },
744
- parentOnly: parentEntries.slice(commonLength),
745
- forkOnly: forkEntries.slice(commonLength),
746
- parent,
747
- fork,
748
- stateChanges: diffJson(parentState, forkState),
749
- effectChanges: diffEffects(parent.effects, fork.effects)
750
- };
751
- }
752
- function verifyEntry(entry, snapshot, effects, mode) {
753
- if (mode === false) return;
754
- if (!entry.verification) {
755
- if (mode === "strict") throw new AgentReplayDivergenceError(entry.id, entry.index, "missing-verification");
756
- return;
757
- }
758
- const actual = replayVerification(snapshot, effects);
759
- if (actual.stateHash !== entry.verification.stateHash) throw new AgentReplayDivergenceError(entry.id, entry.index, "state", entry.verification.stateHash, actual.stateHash);
760
- if (actual.effectsHash !== entry.verification.effectsHash) throw new AgentReplayDivergenceError(entry.id, entry.index, "effects", entry.verification.effectsHash, actual.effectsHash);
761
- }
762
- function replayVerification(snapshot, effects) {
763
- return {
764
- stateHash: hashStableJson(logicalReplayState(snapshot)),
765
- effectsHash: hashStableJson(serializableEffects(effects))
766
- };
767
- }
768
- function logicalReplayState(snapshot) {
769
- const state = {
770
- status: snapshot.status,
771
- value: snapshot.value,
772
- context: snapshot.context
773
- };
774
- if (snapshot.output !== void 0) state.output = snapshot.output;
775
- if (snapshot.error !== void 0) state.error = snapshot.error;
776
- const canonical = canonicalizeForHash(state);
777
- require_event_log_store.assertJsonSerializable(canonical, "snapshot");
778
- return canonical;
779
- }
780
- function serializableEffects(effects) {
781
- return effects.map((effect) => canonicalizeEffect(effect));
782
- }
783
- function canonicalizeEffect(effect) {
784
- if (effect.kind === "execute") return {
785
- kind: effect.kind,
786
- type: String(effect.action.type)
787
- };
788
- const value = { ...effect };
789
- delete value.toDoneEvent;
790
- delete value.toErrorEvent;
791
- delete value.exec;
792
- delete value.action;
793
- return canonicalizeForHash(value);
794
- }
795
- function canonicalizeForHash(value, seen = /* @__PURE__ */ new WeakSet()) {
796
- if (value === void 0) return "[undefined]";
797
- if (typeof value === "function") return "[function]";
798
- if (typeof value === "bigint") return `[bigint:${String(value)}]`;
799
- if (typeof value === "symbol") return `[symbol:${String(value)}]`;
800
- if (value === null || typeof value !== "object") return value;
801
- if (value instanceof Date) return `[Date:${value.toISOString()}]`;
802
- if (value instanceof Error) return {
803
- errorName: value.name,
804
- message: value.message,
805
- ...value.cause !== void 0 ? { cause: canonicalizeForHash(value.cause, seen) } : {}
806
- };
807
- if (value instanceof Set) return [...value].map((item) => canonicalizeForHash(item, seen)).sort((a, b) => stableJson(a).localeCompare(stableJson(b)));
808
- if (value instanceof Map) return [...value.entries()].map(([key, item]) => [canonicalizeForHash(key, seen), canonicalizeForHash(item, seen)]).sort(([a], [b]) => stableJson(a).localeCompare(stableJson(b)));
809
- if (seen.has(value)) return "[circular]";
810
- seen.add(value);
811
- if (Array.isArray(value)) {
812
- const result = value.map((item) => canonicalizeForHash(item, seen));
813
- seen.delete(value);
814
- return result;
815
- }
816
- const standard = value["~standard"];
817
- if (standard) {
818
- seen.delete(value);
819
- return {
820
- schemaVendor: standard.vendor,
821
- schemaVersion: standard.version
822
- };
823
- }
824
- const result = {};
825
- for (const key of Object.keys(value).sort()) result[key] = canonicalizeForHash(value[key], seen);
826
- seen.delete(value);
827
- return result;
828
- }
829
- function normalizeEventErrors(value, seen = /* @__PURE__ */ new WeakMap()) {
830
- if (value instanceof Error) {
831
- const normalized = {
832
- name: value.name,
833
- message: value.message
834
- };
835
- seen.set(value, normalized);
836
- if (value.cause !== void 0) normalized.cause = normalizeEventErrors(value.cause, seen);
837
- return normalized;
838
- }
839
- if (value === null || typeof value !== "object") return value;
840
- const existing = seen.get(value);
841
- if (existing !== void 0) return existing;
842
- if (Array.isArray(value)) {
843
- const result = [];
844
- seen.set(value, result);
845
- for (const item of value) result.push(normalizeEventErrors(item, seen));
846
- return result;
847
- }
848
- const prototype = Object.getPrototypeOf(value);
849
- if (prototype !== Object.prototype && prototype !== null) return value;
850
- const result = {};
851
- seen.set(value, result);
852
- for (const [key, item] of Object.entries(value)) result[key] = normalizeEventErrors(item, seen);
853
- return result;
854
- }
855
- function stableJson(value) {
856
- return JSON.stringify(sortJson(value));
857
- }
858
- function sortJson(value) {
859
- if (Array.isArray(value)) return value.map(sortJson);
860
- if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortJson(value[key])]));
861
- return value;
862
- }
863
- function hashStableJson(value) {
864
- return require_decision.djb2Hex(stableJson(value));
865
- }
866
- function diffJson(before, after, path = "") {
867
- if (stableJson(before) === stableJson(after)) return [];
868
- if (before === null || after === null || typeof before !== "object" || typeof after !== "object" || Array.isArray(before) || Array.isArray(after)) return [{
869
- op: "replace",
870
- path,
871
- value: after
872
- }];
873
- const changes = [];
874
- const beforeObject = before;
875
- const afterObject = after;
876
- const keys = new Set([...Object.keys(beforeObject), ...Object.keys(afterObject)]);
877
- for (const key of [...keys].sort()) {
878
- const childPath = `${path}/${key.replaceAll("~", "~0").replaceAll("/", "~1")}`;
879
- if (!(key in afterObject)) changes.push({
880
- op: "remove",
881
- path: childPath
882
- });
883
- else if (!(key in beforeObject)) changes.push({
884
- op: "add",
885
- path: childPath,
886
- value: afterObject[key]
887
- });
888
- else changes.push(...diffJson(beforeObject[key], afterObject[key], childPath));
889
- }
890
- return changes;
891
- }
892
- function diffEffects(before, after) {
893
- const beforeValues = serializableEffects(before);
894
- const afterValues = serializableEffects(after);
895
- const key = (value, index) => {
896
- const record = value;
897
- return typeof record.requestId === "string" ? record.requestId : `${String(record.kind ?? "effect")}:${index}`;
898
- };
899
- const beforeMap = new Map(beforeValues.map((value, index) => [key(value, index), value]));
900
- const afterMap = new Map(afterValues.map((value, index) => [key(value, index), value]));
901
- const added = [];
902
- const removed = [];
903
- const changed = [];
904
- for (const [id, value] of beforeMap) {
905
- const next = afterMap.get(id);
906
- if (next === void 0) removed.push(value);
907
- else if (stableJson(value) !== stableJson(next)) changed.push({
908
- before: value,
909
- after: next
910
- });
911
- }
912
- for (const [id, value] of afterMap) if (!beforeMap.has(id)) added.push(value);
913
- return {
914
- added,
915
- removed,
916
- changed
917
- };
918
- }
919
- //#endregion
920
- //#region src/workflow-config.ts
921
- /**
922
- * The machines-as-data lowering: `setupAgent(...).fromConfig(config, options)`.
923
- *
924
- * Takes a serializable `AgentWorkflowConfig` (the JSON form of an agent
925
- * machine — published schema at `schemas/agent-workflow.json`, docs at
926
- * docs/machines-as-data.md), compiles its embedded JSON Schemas with the
927
- * host-provided `compileSchema`, resolves named guards/actions/actors from
928
- * `options`, and lowers the whole thing to an ordinary XState machine plus its
929
- * schema pack. This is what makes LLM-generated and stored-as-JSON machines
930
- * first-class: the config validates against the shipped schema before any
931
- * model call, and the resulting machine lints, simulates, and runs like a
932
- * hand-authored one.
933
- */
934
- const DECIDE_SRC = "agent.decide";
935
- const workflowConfigWholeExpressionPattern = /^\{\{\s*([\s\S]*?)\s*\}\}$/;
936
- const workflowConfigTemplateExpressionPattern = /\{\{\s*([\s\S]*?)\s*\}\}/g;
937
- const workflowConfigHasTemplatePattern = /\{\{[\s\S]*?\}\}/;
938
- function toArray(value) {
939
- return Array.isArray(value) ? value : [value];
940
- }
941
- function evaluateWorkflowConfigPath(expression, scope) {
942
- const parts = expression.trim().split(".").filter(Boolean);
943
- let current = scope;
944
- for (const part of parts) {
945
- if (!current || typeof current !== "object") return;
946
- current = current[part];
947
- }
948
- return current;
949
- }
950
- function evaluateWorkflowConfigValue(value, scope) {
951
- if (typeof value === "string") {
952
- const wholeMatch = value.match(workflowConfigWholeExpressionPattern);
953
- if (wholeMatch?.[1]) return evaluateWorkflowConfigPath(wholeMatch[1], scope);
954
- return value.replace(workflowConfigTemplateExpressionPattern, (_match, expression) => {
955
- const resolved = evaluateWorkflowConfigPath(expression, scope);
956
- return resolved === void 0 || resolved === null ? "" : String(resolved);
957
- });
958
- }
959
- if (Array.isArray(value)) return value.map((item) => evaluateWorkflowConfigValue(item, scope));
960
- if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, evaluateWorkflowConfigValue(item, scope)]));
961
- return value;
962
- }
963
- function createSchemasFromWorkflowConfig(config, compileSchema) {
964
- return createAgentSchemas({
965
- context: compileSchema(config.schemas?.context ?? { type: "object" }, "context"),
966
- events: Object.fromEntries(Object.entries(config.schemas?.events ?? {}).map(([key, schema]) => [key, compileSchema(schema, `event.${key}`)])),
967
- emitted: Object.fromEntries(Object.entries(config.schemas?.emitted ?? {}).map(([key, schema]) => [key, compileSchema(schema, `emitted.${key}`)])),
968
- input: compileSchema(config.schemas?.input ?? {}, "input"),
969
- output: compileSchema(config.schemas?.output ?? {}, "output"),
970
- meta: compileSchema(config.schemas?.meta ?? {}, "meta")
971
- });
972
- }
973
- function createRequestsFromWorkflowConfig(config, compileSchema) {
974
- const resolver = (value) => value === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(value, { input });
975
- return Object.fromEntries(Object.entries(config.requests ?? {}).map(([key, request]) => [key, {
976
- mode: request.mode,
977
- description: request.description,
978
- schemas: {
979
- input: compileSchema(request.input, `${key}.input`),
980
- output: compileSchema(request.output, `${key}.output`)
981
- },
982
- model: ({ input }) => String(evaluateWorkflowConfigValue(request.model, { input }) ?? ""),
983
- system: resolver(request.system),
984
- prompt: resolver(request.prompt),
985
- messages: resolver(request.messages),
986
- tools: request.tools,
987
- toolChoice: request.toolChoice,
988
- includeReasoning: request.includeReasoning,
989
- temperature: resolver(request.temperature),
990
- maxOutputTokens: resolver(request.maxOutputTokens),
991
- topP: resolver(request.topP),
992
- topK: resolver(request.topK),
993
- seed: resolver(request.seed),
994
- stopSequences: resolver(request.stopSequences),
995
- metadata: resolver(request.metadata)
996
- }]));
997
- }
998
- function createActorPlaceholdersFromWorkflowConfig(config) {
999
- return Object.fromEntries(Object.keys(config.actors ?? {}).map((key) => [key, require_decision.missingActor(key)]));
1000
- }
1001
- const AGENT_EXPRESSION_LANG = "agent-template";
1002
- function containsTemplateExpression(value) {
1003
- if (typeof value === "string") return workflowConfigHasTemplatePattern.test(value);
1004
- if (Array.isArray(value)) return value.some(containsTemplateExpression);
1005
- if (value && typeof value === "object") return Object.values(value).some(containsTemplateExpression);
1006
- return false;
1007
- }
1008
- function toWorkflowExpression(value) {
1009
- if (!containsTemplateExpression(value)) return value;
1010
- return { "@expr": JSON.stringify(value) };
1011
- }
1012
- function createWorkflowConfigEvaluator(contextSchema) {
1013
- const decoded = /* @__PURE__ */ new Map();
1014
- const decode = (source) => {
1015
- if (!decoded.has(source)) decoded.set(source, JSON.parse(source));
1016
- return decoded.get(source);
1017
- };
1018
- return ({ source, slot, scope }) => {
1019
- const value = decode(source);
1020
- if (slot === "context") return require_decision.validateSchemaSync(contextSchema, evaluateWorkflowConfigValue(value, { input: scope.input }));
1021
- return evaluateWorkflowConfigValue(value, {
1022
- context: scope.context,
1023
- event: scope.event,
1024
- input: scope.input
1025
- });
1026
- };
1027
- }
1028
- function translateWorkflowGuard(guard, translation, location) {
1029
- if (guard === void 0) return;
1030
- if (typeof guard === "string") {
1031
- if (workflowConfigHasTemplatePattern.test(guard)) return { "@expr": JSON.stringify(guard) };
1032
- if (!(guard in translation.guards)) throw new Error(`setupAgent.fromConfig: ${location} references guard '${guard}', which has no implementation. Provide one via fromConfig options: setupAgent.fromConfig(config, { guards: { '${guard}': ({ context, event }) => ... } }).`);
1033
- return { type: guard };
1034
- }
1035
- if (typeof guard === "function") {
1036
- const name = `__fromConfigGuard${Object.keys(translation.syntheticGuards).length}`;
1037
- translation.syntheticGuards[name] = guard;
1038
- return { type: name };
1039
- }
1040
- throw new Error(`setupAgent.fromConfig: ${location} has an unsupported guard value. Use a '{{ ... }}' template string, a named guard reference (string), or a function.`);
1041
- }
1042
- function translateWorkflowAction(action, translation, location) {
1043
- if (action.assign !== void 0) return {
1044
- type: "@xstate.assign",
1045
- context: toWorkflowExpression(action.assign)
1046
- };
1047
- if (action.emit !== void 0) return {
1048
- type: "@xstate.emit",
1049
- event: toWorkflowExpression(action.emit)
1050
- };
1051
- if (action.type) {
1052
- if (!(action.type in translation.actions)) throw new Error(`setupAgent.fromConfig: ${location} uses named action type '${action.type}', which has no implementation. Provide one via fromConfig options: setupAgent.fromConfig(config, { actions: { '${action.type}': (params) => ... } }).`);
1053
- return {
1054
- type: action.type,
1055
- ...action.params !== void 0 ? { params: toWorkflowExpression(action.params) } : {}
1056
- };
1057
- }
1058
- throw new Error(`setupAgent.fromConfig: ${location} action must declare 'assign', 'emit', or 'type'.`);
1059
- }
1060
- function translateWorkflowTransition(transitionConfig, translation, location) {
1061
- const actionConfigs = transitionConfig.actions ? toArray(transitionConfig.actions) : [];
1062
- const assignConfigs = [...transitionConfig.assign !== void 0 ? [transitionConfig.assign] : [], ...actionConfigs.filter((action) => action.assign !== void 0).map((action) => action.assign)];
1063
- const contextPatch = assignConfigs.length ? toWorkflowExpression(Object.assign({}, ...assignConfigs)) : void 0;
1064
- const actions = actionConfigs.filter((action) => action.assign === void 0).map((action) => translateWorkflowAction(action, translation, location));
1065
- const guard = translateWorkflowGuard(transitionConfig.guard, translation, location);
1066
- return {
1067
- ...transitionConfig.target !== void 0 ? { target: transitionConfig.target } : {},
1068
- ...guard ? { guard } : {},
1069
- ...contextPatch !== void 0 ? { context: contextPatch } : {},
1070
- ...actions.length ? { actions } : {},
1071
- ...transitionConfig.description !== void 0 ? { description: transitionConfig.description } : {},
1072
- ...transitionConfig.reenter !== void 0 ? { reenter: transitionConfig.reenter } : {},
1073
- ...transitionConfig.meta !== void 0 ? { meta: transitionConfig.meta } : {}
1074
- };
1075
- }
1076
- function translateWorkflowTransitions(transitionConfig, translation, location) {
1077
- if (!transitionConfig) return;
1078
- return Array.isArray(transitionConfig) ? transitionConfig.map((candidate) => translateWorkflowTransition(candidate, translation, location)) : translateWorkflowTransition(transitionConfig, translation, location);
1079
- }
1080
- function addTransitionMatch(transition, matches) {
1081
- if (Array.isArray(transition)) return transition.map((candidate) => addTransitionMatch(candidate, matches));
1082
- return transition ? {
1083
- ...transition,
1084
- matches: {
1085
- ...transition.matches,
1086
- ...matches
1087
- }
1088
- } : void 0;
1089
- }
1090
- function translateWorkflowInvoke(invokeConfig, translation, stateKey) {
1091
- if (invokeConfig.src === DECIDE_SRC && invokeConfig.onDone !== void 0) throw new Error(`setupAgent.fromConfig: state '${stateKey}' declares an 'onDone' on its 'agent.decide' invoke, which is always a config error. A decision has no output of its own — the chosen event is delivered automatically and handled by the state's 'on' transitions. Remove the 'onDone' (use 'on' for the chosen event; 'onError' still handles retries exhausted).`);
1092
- const location = `state '${stateKey}'`;
1093
- return {
1094
- ...invokeConfig.id !== void 0 ? { id: invokeConfig.id } : {},
1095
- src: invokeConfig.src,
1096
- ...invokeConfig.input !== void 0 ? { input: toWorkflowExpression(invokeConfig.input) } : {},
1097
- ...invokeConfig.onDone !== void 0 ? { onDone: translateWorkflowTransitions(invokeConfig.onDone, translation, location) } : {},
1098
- ...invokeConfig.onError !== void 0 ? { onError: translateWorkflowTransitions(invokeConfig.onError, translation, location) } : {}
1099
- };
1100
- }
1101
- function assertValidTransitionTargets(stateKey, location, transitionConfig, siblingKeys, declaredTargets) {
1102
- if (!transitionConfig) return;
1103
- for (const transition of toArray(transitionConfig)) {
1104
- const targets = transition.target === void 0 ? [] : toArray(transition.target);
1105
- for (const target of targets) {
1106
- if (typeof target !== "string") continue;
1107
- declaredTargets.push(target);
1108
- if (target.startsWith("#") || target.startsWith(".")) continue;
1109
- const head = target.split(".")[0];
1110
- if (!siblingKeys.has(head)) throw new Error(`setupAgent.fromConfig: state '${stateKey}' has a ${location} transition targeting '${target}', which is not a state at that level. Valid targets: ${[...siblingKeys].join(", ") || "(none)"}.`);
1111
- }
1112
- }
1113
- }
1114
- function assertStateTransitionTargets(stateKey, stateConfig, siblingKeys) {
1115
- const declaredTargets = [];
1116
- const invokes = stateConfig.invoke ? toArray(stateConfig.invoke) : [];
1117
- const sources = [
1118
- ...Object.entries(stateConfig.on ?? {}).map(([eventType, transition]) => [`'on.${eventType}'`, transition]),
1119
- ...Object.entries(stateConfig.after ?? {}).map(([delay, transition]) => [`'after.${delay}'`, transition]),
1120
- ["'always'", stateConfig.always],
1121
- ["'onDone'", stateConfig.onDone],
1122
- ["'choice'", stateConfig.choice],
1123
- ...invokes.flatMap((invoke) => [["invoke 'onDone'", invoke.onDone], ["invoke 'onError'", invoke.onError]])
1124
- ];
1125
- for (const [location, transition] of sources) assertValidTransitionTargets(stateKey, location, transition, siblingKeys, declaredTargets);
1126
- return declaredTargets;
1127
- }
1128
- function translateWorkflowState(stateKey, stateConfig, siblingKeys, path, translation) {
1129
- const location = `state '${stateKey}'`;
1130
- if (stateKey.includes(".")) throw new Error(`setupAgent.fromConfig: state key '${stateKey}' contains a '.', which is reserved as the state-path separator. Rename the state (e.g. '${stateKey.replace(/\./g, "_")}').`);
1131
- const childKeys = new Set(Object.keys(stateConfig.states ?? {}));
1132
- const childPath = [...path, stateKey];
1133
- translation.transitionTargets[childPath.join(".")] = assertStateTransitionTargets(stateKey, stateConfig, siblingKeys);
1134
- const doneStateId = stateConfig.onDone !== void 0 ? `@state.${childPath.join(".")}` : void 0;
1135
- const translatedOn = {
1136
- ...Object.fromEntries(Object.entries(stateConfig.on ?? {}).map(([eventType, transitionConfig]) => [eventType, translateWorkflowTransitions(transitionConfig, translation, location)])),
1137
- ...doneStateId ? { "xstate.done.state": addTransitionMatch(translateWorkflowTransitions(stateConfig.onDone, translation, location), { stateId: doneStateId }) } : {}
1138
- };
1139
- return {
1140
- ...stateConfig.description !== void 0 ? { description: stateConfig.description } : {},
1141
- ...stateConfig.type !== void 0 ? { type: stateConfig.type } : {},
1142
- ...doneStateId ? { id: doneStateId } : {},
1143
- ...stateConfig.initial !== void 0 ? { initial: stateConfig.initial } : {},
1144
- ...stateConfig.states !== void 0 ? { states: Object.fromEntries(Object.entries(stateConfig.states).map(([key, child]) => [key, translateWorkflowState(key, child, childKeys, childPath, translation)])) } : {},
1145
- ...stateConfig.choice !== void 0 ? { choice: toArray(stateConfig.choice).map((branch) => {
1146
- if (branch.actions !== void 0) throw new Error(`setupAgent.fromConfig: state '${stateKey}' has a 'choice' branch with 'actions', which is not supported. Use 'assign' on the branch, or move the actions to the target state's 'entry'.`);
1147
- const when = translateWorkflowGuard(branch.guard, translation, location);
1148
- return {
1149
- ...when ? { when } : {},
1150
- target: branch.target,
1151
- ...branch.assign !== void 0 ? { context: toWorkflowExpression(branch.assign) } : {},
1152
- ...branch.description !== void 0 ? { description: branch.description } : {},
1153
- ...branch.reenter !== void 0 ? { reenter: branch.reenter } : {},
1154
- ...branch.meta !== void 0 ? { meta: branch.meta } : {}
1155
- };
1156
- }) } : {},
1157
- ...stateConfig.invoke !== void 0 ? { invoke: Array.isArray(stateConfig.invoke) ? stateConfig.invoke.map((invoke) => translateWorkflowInvoke(invoke, translation, stateKey)) : translateWorkflowInvoke(stateConfig.invoke, translation, stateKey) } : {},
1158
- ...Object.keys(translatedOn).length ? { on: translatedOn } : {},
1159
- ...stateConfig.always !== void 0 ? { always: translateWorkflowTransitions(stateConfig.always, translation, location) } : {},
1160
- ...stateConfig.after !== void 0 ? { after: Object.fromEntries(Object.entries(stateConfig.after).map(([delay, transitionConfig]) => [delay, translateWorkflowTransitions(transitionConfig, translation, location)])) } : {},
1161
- ...stateConfig.entry !== void 0 ? { entry: toArray(stateConfig.entry).map((action) => translateWorkflowAction(action, translation, location)) } : {},
1162
- ...stateConfig.exit !== void 0 ? { exit: toArray(stateConfig.exit).map((action) => translateWorkflowAction(action, translation, location)) } : {},
1163
- ...stateConfig.tags !== void 0 ? { tags: stateConfig.tags } : {},
1164
- ...stateConfig.output !== void 0 ? { output: toWorkflowExpression(stateConfig.output) } : {},
1165
- ...stateConfig.meta !== void 0 ? { meta: stateConfig.meta } : {}
1166
- };
1167
- }
1168
- function collectTranslatedFinalOutputs(states, outputs = []) {
1169
- for (const state of Object.values(states ?? {})) {
1170
- if (state?.type === "final" && state.output !== void 0) outputs.push(state.output);
1171
- collectTranslatedFinalOutputs(state?.states, outputs);
1172
- }
1173
- return outputs;
1174
- }
1175
- function translateWorkflowConfig(config, translation) {
1176
- const rootKeys = new Set(Object.keys(config.states));
1177
- const states = Object.fromEntries(Object.entries(config.states).map(([key, state]) => [key, translateWorkflowState(key, state, rootKeys, [], translation)]));
1178
- const json = {
1179
- "@exprLang": AGENT_EXPRESSION_LANG,
1180
- ...config.id !== void 0 ? { id: config.id } : {},
1181
- ...config.description !== void 0 ? { description: config.description } : {},
1182
- ...config.context !== void 0 ? { context: { "@expr": JSON.stringify(config.context) } } : {},
1183
- initial: config.initial,
1184
- states,
1185
- ...config.meta !== void 0 ? { meta: config.meta } : {}
1186
- };
1187
- const finalOutputs = collectTranslatedFinalOutputs(states);
1188
- if (json.output === void 0 && finalOutputs.length === 1) json.output = finalOutputs[0];
1189
- return json;
1190
- }
1191
- function collectDeclaredStateTags(states, tags = /* @__PURE__ */ new Set()) {
1192
- for (const state of Object.values(states ?? {})) {
1193
- for (const tag of state.tags ?? []) tags.add(tag);
1194
- collectDeclaredStateTags(state.states, tags);
1195
- }
1196
- return tags;
1197
- }
1198
- function resolveIdlePredicate(config, options) {
1199
- if (options.isIdle) return options.isIdle;
1200
- const idleTags = config.idleTags ?? [];
1201
- if (!idleTags.length) return;
1202
- const declaredTags = collectDeclaredStateTags(config.states);
1203
- const unknown = idleTags.filter((tag) => !declaredTags.has(tag));
1204
- if (unknown.length) throw new Error(`setupAgent.fromConfig: 'idleTags' lists ${unknown.map((tag) => `'${tag}'`).join(", ")}, which no state declares in its 'tags'. Tag the waiting state(s) — e.g. "tags": ["${unknown[0]}"] — or remove the entry.`);
1205
- return (snapshot) => idleTags.some((tag) => snapshot.hasTag(tag));
1206
- }
1207
- function setupAgentFromConfig(config, options) {
1208
- if (!options || typeof options.compileSchema !== "function") throw new Error("setupAgent.fromConfig(...) requires a 'compileSchema' option: { compileSchema: (jsonSchema, name) => StandardSchemaV1 }. Bring your own JSON Schema engine (Ajv, @cfworker/json-schema, a compiled-Zod-from-JSON-Schema pipeline, ...). Core intentionally ships no JSON Schema engine.");
1209
- const { compileSchema } = options;
1210
- const schemas = createSchemasFromWorkflowConfig(config, compileSchema);
1211
- const requestActors = createRequestActors(createRequestsFromWorkflowConfig(config, compileSchema));
1212
- const actors = createAgentActors(createActorPlaceholdersFromWorkflowConfig(config), requestActors);
1213
- const translation = {
1214
- guards: options.guards ?? {},
1215
- actions: options.actions ?? {},
1216
- syntheticGuards: {},
1217
- transitionTargets: {}
1218
- };
1219
- const machine = (0, xstate.createMachineFromConfig)(translateWorkflowConfig(config, translation), {
1220
- guards: Object.fromEntries(Object.entries({
1221
- ...translation.guards,
1222
- ...translation.syntheticGuards
1223
- }).map(([name, implementation]) => [name, (args) => Boolean(implementation({
1224
- context: args.context,
1225
- event: args.event
1226
- }))])),
1227
- ...options.actions ? { actions: options.actions } : {},
1228
- actors: Object.fromEntries(Object.keys(actors).map((key) => [key, key])),
1229
- evaluators: { [AGENT_EXPRESSION_LANG]: createWorkflowConfigEvaluator(schemas.context) }
1230
- }).provide({ actors });
1231
- require_decision.agentExecutionOptions.set(machine, {
1232
- schemas,
1233
- actors,
1234
- models: {}
1235
- });
1236
- if (machine.config) require_decision.machineStaticTransitionTargets.set(machine.config, translation.transitionTargets);
1237
- const isIdle = resolveIdlePredicate(config, options);
1238
- if (isIdle && machine.config) require_decision.machineIdlePredicates.set(machine.config, isIdle);
1239
- return {
1240
- machine,
1241
- schemas
1242
- };
1243
- }
1244
- //#endregion
1245
- //#region src/setup-agent.ts
1246
- const emptyEventSchema = { "~standard": {
1247
- version: 1,
1248
- vendor: "statelyai-agent",
1249
- validate(value) {
1250
- return value !== null && typeof value === "object" && Object.keys(value).length === 0 ? { value: {} } : { issues: [{ message: "Expected an empty event payload" }] };
1251
- }
1252
- } };
1253
- const USAGE_TOKEN_FIELDS = [
1254
- "inputTokens",
1255
- "outputTokens",
1256
- "totalTokens",
1257
- "reasoningTokens",
1258
- "cachedInputTokens"
1259
- ];
1260
- const USAGE_ATTRIBUTION_FIELDS = [
1261
- "id",
1262
- "src",
1263
- "model",
1264
- "name"
1265
- ];
1266
- const USAGE_KINDS = ["text", "decision"];
1267
- /**
1268
- * Standard Schema for the reserved `'@agent.usage'` payload. Hand-rolled (no
1269
- * validation-library dependency in core) and deliberately permissive about
1270
- * unknown fields, so a newer runtime adding an attribution field cannot fail
1271
- * an older machine's validation.
1272
- */
1273
- const agentUsageEventSchema = { "~standard": {
1274
- version: 1,
1275
- vendor: "statelyai-agent",
1276
- validate(value) {
1277
- const issues = [];
1278
- if (!value || typeof value !== "object") return { issues: [{ message: "Expected an '@agent.usage' payload object" }] };
1279
- const event = value;
1280
- const usage = event.usage;
1281
- if (!usage || typeof usage !== "object") issues.push({
1282
- message: "Expected 'usage' to be an object",
1283
- path: ["usage"]
1284
- });
1285
- else for (const field of USAGE_TOKEN_FIELDS) {
1286
- const token = usage[field];
1287
- if (token !== void 0 && (typeof token !== "number" || !Number.isFinite(token))) issues.push({
1288
- message: `Expected a finite number`,
1289
- path: ["usage", field]
1290
- });
1291
- }
1292
- if (event.kind !== void 0 && !USAGE_KINDS.some((kind) => kind === event.kind)) issues.push({
1293
- message: `Expected one of ${USAGE_KINDS.join(", ")}`,
1294
- path: ["kind"]
1295
- });
1296
- for (const field of USAGE_ATTRIBUTION_FIELDS) if (event[field] !== void 0 && typeof event[field] !== "string") issues.push({
1297
- message: "Expected a string",
1298
- path: [field]
1299
- });
1300
- return issues.length > 0 ? { issues } : { value: event };
1301
- }
1302
- } };
1303
- /**
1304
- * Adds the reserved `'@agent.usage'` schema to an authored event map. A
1305
- * user-declared entry under that key is rejected: the `@agent.*` namespace
1306
- * belongs to the library (same rule as the reserved `agent.*` actor keys), and
1307
- * a custom payload schema would silently disagree with what `runAgent`
1308
- * delivers.
1309
- */
1310
- function withAgentUsageEventSchema(events) {
1311
- const declared = events?.[AGENT_USAGE_EVENT_TYPE];
1312
- if (declared !== void 0 && declared !== agentUsageEventSchema) throw new Error(`setupAgent: event type '${AGENT_USAGE_EVENT_TYPE}' is in the reserved '@agent.' namespace and cannot be declared in 'events' — setupAgent registers it for you with the usage payload schema. Remove it from 'events'; to receive it, declare a transition instead: on: { '${AGENT_USAGE_EVENT_TYPE}': … }.`);
1313
- return {
1314
- ...events,
1315
- [AGENT_USAGE_EVENT_TYPE]: agentUsageEventSchema
1316
- };
1317
- }
1318
- function normalizeEventSchemas(events) {
1319
- return Object.fromEntries(Object.entries(events).map(([type, schema]) => [type, schema && typeof schema === "object" && "~standard" in schema ? schema : emptyEventSchema]));
1320
- }
1321
- /**
1322
- * Builds a machine's {@link AgentSchemaPack} from a partial schema
1323
- * declaration — only `context` is required; `events`/`input`/`output`/`meta`
1324
- * default to empty/unknown schemas when omitted. Pass the result as
1325
- * `setupAgent({ schemas })`'s `schemas` (or spread the individual fields
1326
- * directly into `setupAgent({ context, events, ... })` — both forms are
1327
- * accepted).
1328
- */
1329
- function createAgentSchemas(schemas) {
1330
- return {
1331
- context: schemas.context,
1332
- events: normalizeEventSchemas(withAgentUsageEventSchema(schemas.events)),
1333
- input: schemas.input,
1334
- output: schemas.output,
1335
- meta: schemas.meta,
1336
- emitted: schemas.emitted
1337
- };
1338
- }
1339
- function mergeContextSchema(base, fields) {
1340
- return { "~standard": {
1341
- version: 1,
1342
- vendor: "statelyai-agent",
1343
- validate(value) {
1344
- const baseResult = base["~standard"].validate(value);
1345
- if (baseResult instanceof Promise) throw new Error("setupAgent: async context schemas are not supported.");
1346
- if (baseResult.issues) return baseResult;
1347
- const merged = { ...baseResult.value };
1348
- const issues = [];
1349
- for (const [key, fieldSchema] of Object.entries(fields)) {
1350
- const fieldResult = fieldSchema["~standard"].validate(value[key]);
1351
- if (fieldResult instanceof Promise) throw new Error("setupAgent: async context schemas are not supported.");
1352
- if (fieldResult.issues) issues.push(...fieldResult.issues.map((issue) => ({
1353
- ...issue,
1354
- path: [key, ...issue.path ?? []]
1355
- })));
1356
- else merged[key] = fieldResult.value;
1357
- }
1358
- return issues.length > 0 ? { issues } : { value: merged };
1359
- }
1360
- } };
1361
- }
1362
- function resolveAgentStateSchemas(contextSchema, states) {
1363
- return Object.fromEntries(Object.entries(states).map(([key, state]) => {
1364
- if (!state || typeof state !== "object") return [key, state];
1365
- const children = "states" in state && state.states ? resolveAgentStateSchemas(contextSchema, state.states) : void 0;
1366
- if ("context" in state && state.context) return [key, {
1367
- schemas: { context: mergeContextSchema(contextSchema, state.context) },
1368
- ...children ? { states: children } : {}
1369
- }];
1370
- return [key, children ? {
1371
- ...state,
1372
- states: children
1373
- } : state];
1374
- }));
1375
- }
1376
- /**
1377
- * Schema-first `setup(...)` for agent machines — the standard entry point
1378
- * for authoring a machine (the blueprint) that this library then runs (via
1379
- * {@link runAgent} or the step helpers) against host-supplied model/decision
1380
- * executors. Context, events, machine input, machine output, and
1381
- * state/transition meta are all standard schemas — no `{} as Type` casts —
1382
- * and are retained on `result.schemas` for runtime validation. Also
1383
- * registers the `agent.generateText`/`agent.streamText`/`agent.userInput`/
1384
- * `agent.decide` builtin actors and lowers `requests`/`actors` into the
1385
- * machine's actor sources. The result is the xstate `setup(...)` object with
1386
- * a wrapped `result.createMachine(...)` plus `result.schemas`/`models`/
1387
- * `requests`/`appendMessages` attached. Also has a
1388
- * `setupAgent.fromConfig(...)` namespace member for building a machine from
1389
- * a serializable {@link AgentWorkflowConfig} instead of this TS API.
1390
- *
1391
- * @example
1392
- * ```ts
1393
- * const schemas = createAgentSchemas({
1394
- * context: z.object({ topic: z.string(), joke: z.string().nullable() }),
1395
- * input: z.object({ topic: z.string() }),
1396
- * output: z.object({ joke: z.string() }),
1397
- * });
1398
- *
1399
- * const agent = setupAgent({
1400
- * schemas,
1401
- * actors: { tellJoke },
1402
- * });
1403
- *
1404
- * const jokeMachine = agent.createMachine({
1405
- * context: ({ input }) => ({ topic: input.topic, joke: null }),
1406
- * initial: 'telling',
1407
- * states: {
1408
- * telling: {
1409
- * invoke: {
1410
- * id: 'joke',
1411
- * src: 'tellJoke',
1412
- * input: ({ context }) => ({ topic: context.topic }),
1413
- * onDone: ({ output }) => ({ target: 'done', context: { joke: output } }),
1414
- * },
1415
- * },
1416
- * done: { type: 'final', output: ({ context }) => ({ joke: context.joke ?? '' }) },
1417
- * },
1418
- * });
1419
- * ```
1420
- */
1421
- function setupAgent(config) {
1422
- const schemas = normalizeAgentSchemas(config);
1423
- const requestActors = createRequestActors(config.requests ?? {});
1424
- const actors = createAgentActors(config.actors, requestActors);
1425
- const base = (0, xstate.setup)({
1426
- schemas: {
1427
- context: schemas.context,
1428
- events: schemas.events,
1429
- input: schemas.input,
1430
- output: schemas.output,
1431
- meta: schemas.meta,
1432
- ...schemas.emitted && Object.keys(schemas.emitted).length > 0 ? { emitted: schemas.emitted } : {}
1433
- },
1434
- ...config.states ? { states: resolveAgentStateSchemas(schemas.context, config.states) } : {},
1435
- actors,
1436
- actions: config.actions,
1437
- guards: config.guards,
1438
- delays: config.delays
1439
- });
1440
- const createBaseMachine = base.createMachine.bind(base);
1441
- const models = config.models ?? {};
1442
- const machineOptions = {
1443
- schemas,
1444
- actors,
1445
- models
1446
- };
1447
- return Object.assign(base, {
1448
- createMachine(machineConfig) {
1449
- assertStateSchemaKeysExist(config.states, machineConfig?.states);
1450
- const machine = createBaseMachine(withRootOutputFromSingleFinal(machineConfig));
1451
- require_decision.agentExecutionOptions.set(machine, machineOptions);
1452
- if (config.isIdle) {
1453
- const rootConfig = machine.config;
1454
- if (rootConfig) require_decision.machineIdlePredicates.set(rootConfig, config.isIdle);
1455
- }
1456
- return machine;
1457
- },
1458
- schemas,
1459
- models,
1460
- requests: requestActors,
1461
- appendMessages
1462
- });
1463
- }
1464
- function collectFinalStateOutputs(states, outputs = []) {
1465
- for (const state of Object.values(states ?? {})) {
1466
- if (state?.type === "final" && state.output !== void 0) outputs.push(state.output);
1467
- collectFinalStateOutputs(state?.states, outputs);
1468
- }
1469
- return outputs;
1470
- }
1471
- function withRootOutputFromSingleFinal(config) {
1472
- if (!config || typeof config !== "object" || "output" in config || !("states" in config)) return config;
1473
- const outputs = collectFinalStateOutputs(config.states);
1474
- return outputs.length === 1 ? {
1475
- ...config,
1476
- output: outputs[0]
1477
- } : config;
1478
- }
1479
- (function(_setupAgent) {
1480
- function fromConfig(config, options) {
1481
- return setupAgentFromConfig(config, options);
1482
- }
1483
- _setupAgent.fromConfig = fromConfig;
1484
- })(setupAgent || (setupAgent = {}));
1485
- /**
1486
- * The schema pack a machine was built with — `setupAgent(...).createMachine`
1487
- * and `setupAgent.fromConfig(...)` both register one, so hosts can read a
1488
- * machine's input/event schemas at runtime without knowing how it was
1489
- * authored. Returns `undefined` for machines not built by either (a plain
1490
- * xstate `createMachine`/`setup` machine).
1491
- *
1492
- * Registration is keyed on the machine object, so a machine returned by
1493
- * `machine.provide(...)` carries no pack — read it from the machine the setup
1494
- * returned.
1495
- */
1496
- function getAgentSchemas(machine) {
1497
- return require_decision.agentExecutionOptions.get(machine)?.schemas;
1498
- }
1499
- /** Builds one TextLogic actor per `setupAgent({ requests })` entry. @internal */
1500
- function createRequestActors(requests) {
1501
- return Object.fromEntries(Object.entries(requests).map(([key, request]) => {
1502
- return [key, require_decision.createTextLogic({
1503
- ...request,
1504
- name: key,
1505
- mode: request.mode ?? "generate"
1506
- })];
1507
- }));
1508
- }
1509
- function normalizeAgentSchemas(config) {
1510
- if ("schemas" in config && config.schemas) return {
1511
- ...config.schemas,
1512
- events: normalizeEventSchemas(withAgentUsageEventSchema(config.schemas.events))
1513
- };
1514
- const loose = config;
1515
- return createAgentSchemas({
1516
- ...loose,
1517
- context: loose.context
1518
- });
1519
- }
1520
- /**
1521
- * Runtime guard: every key of the `setupAgent({ states })` narrowing map must
1522
- * name a real state node in the machine config. A typo'd key is otherwise a
1523
- * silent no-op — the narrowing simply never applies — so fail fast at
1524
- * `createMachine` time. Walks nested `states` maps in parallel with the
1525
- * machine config, since narrowing entries nest the same way.
1526
- */
1527
- function assertStateSchemaKeysExist(stateSchemas, machineStates, path = []) {
1528
- if (!stateSchemas) return;
1529
- for (const [key, stateSchema] of Object.entries(stateSchemas)) {
1530
- const machineState = machineStates?.[key];
1531
- if (!machineState || typeof machineState !== "object") {
1532
- const validKeys = Object.keys(machineStates ?? {});
1533
- const parent = path.length > 0 ? ` of state '${path.join(".")}'` : "";
1534
- throw new Error(`setupAgent: 'states' key '${[...path, key].join(".")}' does not name a state in the machine config, so its context narrowing would silently never apply. Valid child states${parent}: ${validKeys.join(", ") || "(none)"}. Fix the key to match a state in createMachine({ states }), or remove it from setupAgent({ states }).`);
1535
- }
1536
- if (stateSchema && typeof stateSchema === "object" && "states" in stateSchema && stateSchema.states) assertStateSchemaKeysExist(stateSchema.states, machineState.states, [...path, key]);
1537
- }
1538
- }
1539
- const RESERVED_AGENT_ACTOR_KEYS = [
1540
- ...Object.keys(require_decision.builtinTextActors),
1541
- require_decision.USER_INPUT_ACTOR,
1542
- require_decision.DECIDE_ACTOR
1543
- ];
1544
- /**
1545
- * The whole `agent.` actor-source namespace is reserved for the library, not
1546
- * just the shipped builtins: a key starting with this prefix is rejected at
1547
- * setup time so a future builtin can never collide with (or be shadowed by) a
1548
- * user source. Name your own sources without it.
1549
- */
1550
- const RESERVED_AGENT_KEY_PREFIX = "agent.";
1551
- /**
1552
- * Runtime guards over the user-supplied `actors`/`requests` keys, in one walk
1553
- * of both groups:
1554
- *
1555
- * 1. A key in the reserved `agent.` namespace is rejected — every key with
1556
- * that prefix, not only today's builtins. Without this, the builtins-first
1557
- * spread in {@link createAgentActors} lets such a key overwrite the builtin
1558
- * (`agent.decide`, …) silently, and a builtin added later would start
1559
- * colliding with user code. Deliberate override of a builtin is still
1560
- * possible after the machine is created, via
1561
- * `machine.provide({ actors: { 'agent.decide': ... } })`.
1562
- * 2. A key appearing in BOTH groups is almost certainly a mistake (whichever
1563
- * spread applies last would silently win) — fail fast with a clear message
1564
- * rather than let one implementation shadow another.
1565
- */
1566
- function assertActorKeys(actors, requests) {
1567
- const seenIn = /* @__PURE__ */ new Map();
1568
- const groups = [["actors", actors], ["requests", requests]];
1569
- for (const [groupName, group] of groups) for (const key of Object.keys(group ?? {})) {
1570
- if (RESERVED_AGENT_ACTOR_KEYS.some((reserved) => reserved === key)) throw new Error(`setupAgent: '${groupName}' key '${key}' is a reserved builtin agent actor and cannot be redefined here (it would silently clobber the builtin). Reserved keys: ${RESERVED_AGENT_ACTOR_KEYS.join(", ")}. To deliberately override a builtin, do it on the created machine instead: machine.provide({ actors: { '${key}': ... } }).`);
1571
- if (key.startsWith(RESERVED_AGENT_KEY_PREFIX)) throw new Error(`setupAgent: '${groupName}' key '${key}' uses the reserved '${RESERVED_AGENT_KEY_PREFIX}' namespace, which belongs to the library (${RESERVED_AGENT_ACTOR_KEYS.join(", ")}, and anything added later). Rename it without the '${RESERVED_AGENT_KEY_PREFIX}' prefix.`);
1572
- const existingGroup = seenIn.get(key);
1573
- if (existingGroup) throw new Error(`setupAgent: key '${key}' is defined in both '${existingGroup}' and '${groupName}'. Each actor source key must be unique across 'actors' and 'requests'.`);
1574
- seenIn.set(key, groupName);
1575
- }
1576
- }
1577
- function createAgentActors(actors, requestActors) {
1578
- assertActorKeys(actors, requestActors);
1579
- return {
1580
- ...require_decision.builtinTextActors,
1581
- [require_decision.USER_INPUT_ACTOR]: require_decision.userInputActor,
1582
- [require_decision.DECIDE_ACTOR]: require_decision.createDecideActor(),
1583
- ...actors,
1584
- ...requestActors
1585
- };
1586
- }
1587
- //#endregion
1588
- Object.defineProperty(exports, "AGENT_INIT_EVENT_TYPE", {
1589
- enumerable: true,
1590
- get: function() {
1591
- return AGENT_INIT_EVENT_TYPE;
1592
- }
1593
- });
1594
- Object.defineProperty(exports, "AGENT_USAGE_EVENT_TYPE", {
1595
- enumerable: true,
1596
- get: function() {
1597
- return AGENT_USAGE_EVENT_TYPE;
1598
- }
1599
- });
1600
- Object.defineProperty(exports, "AgentReplayDivergenceError", {
1601
- enumerable: true,
1602
- get: function() {
1603
- return AgentReplayDivergenceError;
1604
- }
1605
- });
1606
- Object.defineProperty(exports, "AgentReplayMachineMismatchError", {
1607
- enumerable: true,
1608
- get: function() {
1609
- return AgentReplayMachineMismatchError;
1610
- }
1611
- });
1612
- Object.defineProperty(exports, "appendMessages", {
1613
- enumerable: true,
1614
- get: function() {
1615
- return appendMessages;
1616
- }
1617
- });
1618
- Object.defineProperty(exports, "createAgentSchemas", {
1619
- enumerable: true,
1620
- get: function() {
1621
- return createAgentSchemas;
1622
- }
1623
- });
1624
- Object.defineProperty(exports, "createReplayEntry", {
1625
- enumerable: true,
1626
- get: function() {
1627
- return createReplayEntry;
1628
- }
1629
- });
1630
- Object.defineProperty(exports, "diffEventLogs", {
1631
- enumerable: true,
1632
- get: function() {
1633
- return diffEventLogs;
1634
- }
1635
- });
1636
- Object.defineProperty(exports, "executeAgentRequest", {
1637
- enumerable: true,
1638
- get: function() {
1639
- return executeAgentRequest;
1640
- }
1641
- });
1642
- Object.defineProperty(exports, "getAgentEffects", {
1643
- enumerable: true,
1644
- get: function() {
1645
- return getAgentEffects;
1646
- }
1647
- });
1648
- Object.defineProperty(exports, "getAgentSchemas", {
1649
- enumerable: true,
1650
- get: function() {
1651
- return getAgentSchemas;
1652
- }
1653
- });
1654
- Object.defineProperty(exports, "getInvokeEffectMetadata", {
1655
- enumerable: true,
1656
- get: function() {
1657
- return getInvokeEffectMetadata;
1658
- }
1659
- });
1660
- Object.defineProperty(exports, "initEntry", {
1661
- enumerable: true,
1662
- get: function() {
1663
- return initEntry;
1664
- }
1665
- });
1666
- Object.defineProperty(exports, "initialAgentStep", {
1667
- enumerable: true,
1668
- get: function() {
1669
- return initialAgentStep;
1670
- }
1671
- });
1672
- Object.defineProperty(exports, "messagesSchema", {
1673
- enumerable: true,
1674
- get: function() {
1675
- return messagesSchema;
1676
- }
1677
- });
1678
- Object.defineProperty(exports, "replay", {
1679
- enumerable: true,
1680
- get: function() {
1681
- return replay;
1682
- }
1683
- });
1684
- Object.defineProperty(exports, "resolveAgentStep", {
1685
- enumerable: true,
1686
- get: function() {
1687
- return resolveAgentStep;
1688
- }
1689
- });
1690
- Object.defineProperty(exports, "setupAgent", {
1691
- enumerable: true,
1692
- get: function() {
1693
- return setupAgent;
1694
- }
1695
- });
1696
- Object.defineProperty(exports, "transitionAgentStep", {
1697
- enumerable: true,
1698
- get: function() {
1699
- return transitionAgentStep;
1700
- }
1701
- });
1702
- Object.defineProperty(exports, "validateReplayEntries", {
1703
- enumerable: true,
1704
- get: function() {
1705
- return validateReplayEntries;
1706
- }
1707
- });