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

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