@statelyai/agent 2.0.0-alpha.11 → 2.0.0-alpha.13

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 (66) hide show
  1. package/dist/ai-sdk.cjs +4 -5
  2. package/dist/ai-sdk.d.cts +7 -4
  3. package/dist/ai-sdk.d.mts +7 -4
  4. package/dist/ai-sdk.mjs +1 -2
  5. package/dist/{events-JiVPYrct.mjs → decision-BezSD_YC.mjs} +327 -20
  6. package/dist/{events-CRQj3VtP.cjs → decision-dWGhBh0P.cjs} +401 -28
  7. package/dist/errors-BQRk9eiZ.d.cts +19 -0
  8. package/dist/errors-C9rxnWbX.d.mts +19 -0
  9. package/dist/errors-CeSXQx0v.mjs +23 -0
  10. package/dist/errors-DUBBzRLP.cjs +28 -0
  11. package/dist/event-log-store-CNT_7F0V.cjs +452 -0
  12. package/dist/event-log-store-CriMgX1D.d.mts +144 -0
  13. package/dist/event-log-store-D7pWtIhb.mjs +411 -0
  14. package/dist/event-log-store-Ruq18mGp.d.cts +144 -0
  15. package/dist/index.cjs +1050 -705
  16. package/dist/index.d.cts +538 -565
  17. package/dist/index.d.mts +538 -565
  18. package/dist/index.mjs +950 -644
  19. package/dist/machines.cjs +752 -0
  20. package/dist/machines.d.cts +372 -0
  21. package/dist/machines.d.mts +372 -0
  22. package/dist/machines.mjs +741 -0
  23. package/dist/otel.cjs +268 -0
  24. package/dist/otel.d.cts +67 -0
  25. package/dist/otel.d.mts +67 -0
  26. package/dist/otel.mjs +267 -0
  27. package/dist/run-agent-C3mFDGTf.d.mts +1111 -0
  28. package/dist/run-agent-DnvtcnTZ.d.cts +1111 -0
  29. package/dist/setup-agent-DAZZSjDS.mjs +1711 -0
  30. package/dist/setup-agent-DP95MFrI.cjs +1836 -0
  31. package/dist/sqlite.cjs +135 -0
  32. package/dist/sqlite.d.cts +57 -0
  33. package/dist/sqlite.d.mts +57 -0
  34. package/dist/sqlite.mjs +133 -0
  35. package/dist/{text-logic-CaKqgX4Y.d.mts → text-logic-BDxwQNsD.d.cts} +155 -72
  36. package/dist/{text-logic-Ckhr2kKC.d.cts → text-logic-TkKPw8Aq.d.mts} +155 -72
  37. package/dist/{types-qm00QF91.d.mts → types-QbEfCVny.d.cts} +1 -1
  38. package/dist/{types-C9QiMjre.d.cts → types-_FXoFBGO.d.mts} +1 -1
  39. package/package.json +47 -39
  40. package/readme.md +49 -12
  41. package/schemas/agent-workflow.json +40 -21
  42. package/skills/generate-machine/SKILL.md +267 -0
  43. package/dist/adapter.cjs +0 -15
  44. package/dist/adapter.d.cts +0 -4
  45. package/dist/adapter.d.mts +0 -4
  46. package/dist/adapter.mjs +0 -2
  47. package/dist/decision-C3k4ve51.mjs +0 -227
  48. package/dist/decision-D8wJrM8W.cjs +0 -286
  49. package/dist/openai-compat.cjs +0 -309
  50. package/dist/openai-compat.d.cts +0 -59
  51. package/dist/openai-compat.d.mts +0 -59
  52. package/dist/openai-compat.mjs +0 -308
  53. package/dist/steps-BALp1eZo.d.mts +0 -198
  54. package/dist/steps-CVe54GPP.cjs +0 -420
  55. package/dist/steps-CkyyyuHd.mjs +0 -379
  56. package/dist/steps-MjnQI4aB.d.cts +0 -198
  57. package/dist/steps.cjs +0 -12
  58. package/dist/steps.d.cts +0 -3
  59. package/dist/steps.d.mts +0 -3
  60. package/dist/steps.mjs +0 -3
  61. package/dist/utils-BYqT_Dyv.d.cts +0 -108
  62. package/dist/utils-Do5wIJrh.d.mts +0 -108
  63. package/dist/zod.cjs +0 -31
  64. package/dist/zod.d.cts +0 -30
  65. package/dist/zod.d.mts +0 -30
  66. package/dist/zod.mjs +0 -30
package/dist/index.cjs CHANGED
@@ -1,571 +1,14 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_events = require("./events-CRQj3VtP.cjs");
3
- const require_decision = require("./decision-D8wJrM8W.cjs");
4
- const require_steps = require("./steps-CVe54GPP.cjs");
2
+ const require_errors = require("./errors-DUBBzRLP.cjs");
3
+ const require_setup_agent = require("./setup-agent-DP95MFrI.cjs");
4
+ const require_decision = require("./decision-dWGhBh0P.cjs");
5
+ const require_event_log_store = require("./event-log-store-CNT_7F0V.cjs");
5
6
  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
- function isKnownPart(part) {
41
- return !!part && typeof part === "object" && KNOWN_PART_TYPES.has(part.type);
42
- }
43
- function validatePartsArray(content) {
44
- if (!Array.isArray(content)) return "Expected content to be a string or an array of parts";
45
- for (const part of content) if (!isKnownPart(part)) {
46
- const type = part && typeof part === "object" ? part.type : void 0;
47
- return `Unknown message part type: ${JSON.stringify(type)}`;
48
- }
49
- }
50
- /**
51
- * A {@link StandardSchemaV1} validating an `AgentMessage[]` context field —
52
- * checks that every message has a known `role` (`system`/`user`/`assistant`/
53
- * `tool`) and that `content` is either a string (where the role allows it) or
54
- * an array of parts with a known `type`. Use it directly as a context
55
- * schema's `messages` field when authoring with `createAgentSchemas`.
56
- */
57
- const messagesSchema = { "~standard": {
58
- version: 1,
59
- vendor: "statelyai-agent",
60
- validate(value) {
61
- if (!Array.isArray(value)) return { issues: [{ message: "Expected an array of agent messages" }] };
62
- for (const message of value) {
63
- if (!message || typeof message !== "object") return { issues: [{ message: "Expected an array of agent messages" }] };
64
- const role = message.role;
65
- const content = message.content;
66
- if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") return { issues: [{ message: `Unknown message role: ${JSON.stringify(role)}` }] };
67
- if (role === "system") {
68
- if (typeof content !== "string") return { issues: [{ message: "system message content must be a string" }] };
69
- continue;
70
- }
71
- if (role === "tool") {
72
- const error = validatePartsArray(content) ?? (content.some((part) => part.type !== "tool-result") ? "tool message content must contain only tool-result parts" : void 0);
73
- if (error) return { issues: [{ message: error }] };
74
- continue;
75
- }
76
- if (typeof content === "string") continue;
77
- const error = validatePartsArray(content);
78
- if (error) return { issues: [{ message: error }] };
79
- }
80
- return { value };
81
- }
82
- } };
83
- //#endregion
84
- //#region src/workflow-config.ts
85
- const DECIDE_SRC$1 = "agent.decide";
86
- const workflowConfigWholeExpressionPattern = /^\{\{\s*([\s\S]*?)\s*\}\}$/;
87
- const workflowConfigTemplateExpressionPattern = /\{\{\s*([\s\S]*?)\s*\}\}/g;
88
- function evaluateWorkflowConfigPath(expression, scope) {
89
- const parts = expression.trim().split(".").filter(Boolean);
90
- let current = scope;
91
- for (const part of parts) {
92
- if (!current || typeof current !== "object") return;
93
- current = current[part];
94
- }
95
- return current;
96
- }
97
- function evaluateWorkflowConfigValue(value, scope) {
98
- if (typeof value === "string") {
99
- const wholeMatch = value.match(workflowConfigWholeExpressionPattern);
100
- if (wholeMatch?.[1]) return evaluateWorkflowConfigPath(wholeMatch[1], scope);
101
- return value.replace(workflowConfigTemplateExpressionPattern, (_match, expression) => {
102
- const resolved = evaluateWorkflowConfigPath(expression, scope);
103
- return resolved === void 0 || resolved === null ? "" : String(resolved);
104
- });
105
- }
106
- if (Array.isArray(value)) return value.map((item) => evaluateWorkflowConfigValue(item, scope));
107
- if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, evaluateWorkflowConfigValue(item, scope)]));
108
- return value;
109
- }
110
- function createSchemasFromWorkflowConfig(config, compileSchema) {
111
- return createAgentSchemas({
112
- context: compileSchema(config.schemas?.context ?? { type: "object" }, "context"),
113
- events: Object.fromEntries(Object.entries(config.schemas?.events ?? {}).map(([key, schema]) => [key, compileSchema(schema, `event.${key}`)])),
114
- emitted: Object.fromEntries(Object.entries(config.schemas?.emitted ?? {}).map(([key, schema]) => [key, compileSchema(schema, `emitted.${key}`)])),
115
- input: compileSchema(config.schemas?.input ?? {}, "input"),
116
- output: compileSchema(config.schemas?.output ?? {}, "output"),
117
- meta: compileSchema(config.schemas?.meta ?? {}, "meta")
118
- });
119
- }
120
- function createRequestsFromWorkflowConfig(config, compileSchema) {
121
- return Object.fromEntries(Object.entries(config.requests ?? {}).map(([key, request]) => [key, {
122
- mode: request.mode,
123
- description: request.description,
124
- schemas: {
125
- input: compileSchema(request.input, `${key}.input`),
126
- output: compileSchema(request.output, `${key}.output`)
127
- },
128
- model: ({ input }) => String(evaluateWorkflowConfigValue(request.model, { input }) ?? ""),
129
- system: request.system === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.system, { input }),
130
- prompt: request.prompt === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.prompt, { input }),
131
- messages: request.messages === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.messages, { input }),
132
- tools: request.tools,
133
- toolChoice: request.toolChoice,
134
- reasoning: request.reasoning,
135
- temperature: request.temperature === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.temperature, { input }),
136
- maxOutputTokens: request.maxOutputTokens === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.maxOutputTokens, { input }),
137
- topP: request.topP === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.topP, { input }),
138
- topK: request.topK === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.topK, { input }),
139
- seed: request.seed === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.seed, { input }),
140
- stopSequences: request.stopSequences === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.stopSequences, { input }),
141
- metadata: request.metadata === void 0 ? void 0 : ({ input }) => evaluateWorkflowConfigValue(request.metadata, { input })
142
- }]));
143
- }
144
- function createActorPlaceholdersFromWorkflowConfig(config) {
145
- return Object.fromEntries(Object.keys(config.actors ?? {}).map((key) => [key, require_events.missingActor(key)]));
146
- }
147
- function createAssignAction(assignConfig) {
148
- return ({ context, event }) => ({ context: Object.fromEntries(Object.entries(assignConfig).map(([key, value]) => [key, evaluateWorkflowConfigValue(value, {
149
- context,
150
- event
151
- })])) });
152
- }
153
- function lowerWorkflowActions(actionConfig) {
154
- if (!actionConfig) return;
155
- const lowered = (Array.isArray(actionConfig) ? actionConfig : [actionConfig]).map((action) => {
156
- if (action.assign !== void 0) return createAssignAction(action.assign);
157
- if (action.emit !== void 0) return ({ context, event }, enq) => {
158
- enq.emit(evaluateWorkflowConfigValue(action.emit, {
159
- context,
160
- event
161
- }));
162
- };
163
- if (!action.type) throw new Error("setupAgent.fromConfig: action must declare 'type', 'assign', or 'emit'.");
164
- return {
165
- type: action.type,
166
- params: ({ context, event }) => evaluateWorkflowConfigValue(action.params, {
167
- context,
168
- event
169
- })
170
- };
171
- });
172
- return Array.isArray(actionConfig) ? lowered : lowered[0];
173
- }
174
- function workflowTransitionMatches(transitionConfig, scope) {
175
- if (transitionConfig.guard === void 0) return true;
176
- if (typeof transitionConfig.guard === "string") return Boolean(evaluateWorkflowConfigValue(transitionConfig.guard, scope));
177
- return typeof transitionConfig.guard === "function" ? transitionConfig.guard(scope) : false;
178
- }
179
- function applyWorkflowTransitionActions(actionConfig, scope, enq) {
180
- if (!actionConfig) return;
181
- const actions = Array.isArray(actionConfig) ? actionConfig : [actionConfig];
182
- let assigned;
183
- for (const action of actions) if (action.assign !== void 0) assigned = {
184
- ...assigned ?? {},
185
- ...Object.fromEntries(Object.entries(action.assign).map(([key, value]) => [key, evaluateWorkflowConfigValue(value, scope)]))
186
- };
187
- else if (action.emit !== void 0) enq.emit(evaluateWorkflowConfigValue(action.emit, scope));
188
- else if (action.type) throw new Error(`setupAgent.fromConfig: named action type '${action.type}' is not supported in a transition 'actions' entry (config lowering wires no action implementations). Use 'emit' or 'assign', or move it to a state 'entry'/'exit'.`);
189
- else throw new Error("setupAgent.fromConfig: a transition action must declare 'emit', 'assign', or 'type'.");
190
- return assigned;
191
- }
192
- function lowerWorkflowTransitionResult(transitionConfig, scope, enq) {
193
- const actionAssign = applyWorkflowTransitionActions(transitionConfig.actions, scope, enq);
194
- const configAssign = transitionConfig.assign ? Object.fromEntries(Object.entries(transitionConfig.assign).map(([key, value]) => [key, evaluateWorkflowConfigValue(value, scope)])) : void 0;
195
- const context = configAssign || actionAssign ? {
196
- ...configAssign ?? {},
197
- ...actionAssign ?? {}
198
- } : void 0;
199
- return {
200
- ...transitionConfig.target !== void 0 ? { target: transitionConfig.target } : {},
201
- ...context ? { context } : {},
202
- ...transitionConfig.description !== void 0 ? { description: transitionConfig.description } : {},
203
- ...transitionConfig.reenter !== void 0 ? { reenter: transitionConfig.reenter } : {},
204
- ...transitionConfig.meta !== void 0 ? { meta: transitionConfig.meta } : {}
205
- };
206
- }
207
- function lowerWorkflowTransition(transitionConfig) {
208
- return ({ context, event }, enq) => {
209
- const scope = {
210
- context,
211
- event
212
- };
213
- if (!workflowTransitionMatches(transitionConfig, scope)) return;
214
- return lowerWorkflowTransitionResult(transitionConfig, scope, enq);
215
- };
216
- }
217
- function lowerWorkflowTransitionOrArray(transitionConfig) {
218
- if (!transitionConfig) return;
219
- return Array.isArray(transitionConfig) ? ({ context, event }, enq) => {
220
- const scope = {
221
- context,
222
- event
223
- };
224
- const transition = transitionConfig.find((candidate) => workflowTransitionMatches(candidate, scope));
225
- if (!transition) return;
226
- return lowerWorkflowTransitionResult(transition, scope, enq);
227
- } : lowerWorkflowTransition(transitionConfig);
228
- }
229
- function lowerWorkflowInvoke(invokeConfig, stateKey) {
230
- if (invokeConfig.src === DECIDE_SRC$1 && 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).`);
231
- return {
232
- ...invokeConfig.id !== void 0 ? { id: invokeConfig.id } : {},
233
- src: invokeConfig.src,
234
- ...invokeConfig.input !== void 0 ? { input: ({ context, event }) => evaluateWorkflowConfigValue(invokeConfig.input, {
235
- context,
236
- event
237
- }) } : {},
238
- ...invokeConfig.onDone !== void 0 ? { onDone: lowerWorkflowTransitionOrArray(invokeConfig.onDone) } : {},
239
- ...invokeConfig.onError !== void 0 ? { onError: lowerWorkflowTransitionOrArray(invokeConfig.onError) } : {},
240
- ...invokeConfig.meta !== void 0 ? { meta: invokeConfig.meta } : {}
241
- };
242
- }
243
- function assertValidTransitionTargets(stateKey, location, transitionConfig, siblingKeys) {
244
- if (!transitionConfig) return;
245
- const transitions = Array.isArray(transitionConfig) ? transitionConfig : [transitionConfig];
246
- for (const transition of transitions) {
247
- const targets = transition.target === void 0 ? [] : Array.isArray(transition.target) ? transition.target : [transition.target];
248
- for (const target of targets) {
249
- if (typeof target !== "string" || target.startsWith("#") || target.startsWith(".")) continue;
250
- const head = target.split(".")[0];
251
- 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)"}.`);
252
- }
253
- }
254
- }
255
- function assertStateTransitionTargets(stateKey, stateConfig, siblingKeys) {
256
- for (const [eventType, transition] of Object.entries(stateConfig.on ?? {})) assertValidTransitionTargets(stateKey, `'on.${eventType}'`, transition, siblingKeys);
257
- for (const [delay, transition] of Object.entries(stateConfig.after ?? {})) assertValidTransitionTargets(stateKey, `'after.${delay}'`, transition, siblingKeys);
258
- assertValidTransitionTargets(stateKey, "'always'", stateConfig.always, siblingKeys);
259
- assertValidTransitionTargets(stateKey, "'onDone'", stateConfig.onDone, siblingKeys);
260
- assertValidTransitionTargets(stateKey, "'choice'", stateConfig.choice, siblingKeys);
261
- const invokes = stateConfig.invoke ? Array.isArray(stateConfig.invoke) ? stateConfig.invoke : [stateConfig.invoke] : [];
262
- for (const invoke of invokes) {
263
- assertValidTransitionTargets(stateKey, "invoke 'onDone'", invoke.onDone, siblingKeys);
264
- assertValidTransitionTargets(stateKey, "invoke 'onError'", invoke.onError, siblingKeys);
265
- }
266
- }
267
- function lowerWorkflowState(stateKey, stateConfig, siblingKeys) {
268
- assertStateTransitionTargets(stateKey, stateConfig, siblingKeys);
269
- const childKeys = new Set(Object.keys(stateConfig.states ?? {}));
270
- return {
271
- ...stateConfig.description !== void 0 ? { description: stateConfig.description } : {},
272
- ...stateConfig.type !== void 0 ? { type: stateConfig.type } : {},
273
- ...stateConfig.initial !== void 0 ? { initial: stateConfig.initial } : {},
274
- ...stateConfig.states !== void 0 ? { states: Object.fromEntries(Object.entries(stateConfig.states).map(([key, child]) => [key, lowerWorkflowState(key, child, childKeys)])) } : {},
275
- ...stateConfig.choice !== void 0 ? { choice: lowerWorkflowTransitionOrArray(stateConfig.choice) } : {},
276
- ...stateConfig.invoke !== void 0 ? { invoke: Array.isArray(stateConfig.invoke) ? stateConfig.invoke.map((invoke) => lowerWorkflowInvoke(invoke, stateKey)) : lowerWorkflowInvoke(stateConfig.invoke, stateKey) } : {},
277
- ...stateConfig.on !== void 0 ? { on: Object.fromEntries(Object.entries(stateConfig.on).map(([eventType, transitionConfig]) => [eventType, lowerWorkflowTransitionOrArray(transitionConfig)])) } : {},
278
- ...stateConfig.always !== void 0 ? { always: lowerWorkflowTransitionOrArray(stateConfig.always) } : {},
279
- ...stateConfig.onDone !== void 0 ? { onDone: lowerWorkflowTransitionOrArray(stateConfig.onDone) } : {},
280
- ...stateConfig.after !== void 0 ? { after: Object.fromEntries(Object.entries(stateConfig.after).map(([delay, transitionConfig]) => [delay, lowerWorkflowTransitionOrArray(transitionConfig)])) } : {},
281
- ...stateConfig.entry !== void 0 ? { entry: lowerWorkflowActions(stateConfig.entry) } : {},
282
- ...stateConfig.exit !== void 0 ? { exit: lowerWorkflowActions(stateConfig.exit) } : {},
283
- ...stateConfig.tags !== void 0 ? { tags: stateConfig.tags } : {},
284
- ...stateConfig.output !== void 0 ? { output: ({ context, event }) => evaluateWorkflowConfigValue(stateConfig.output, {
285
- context,
286
- event
287
- }) } : {},
288
- ...stateConfig.meta !== void 0 ? { meta: stateConfig.meta } : {}
289
- };
290
- }
291
- function setupAgentFromConfig(config, options) {
292
- 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.");
293
- const { compileSchema } = options;
294
- const schemas = createSchemasFromWorkflowConfig(config, compileSchema);
295
- const requestActors = createRequestActors(createRequestsFromWorkflowConfig(config, compileSchema));
296
- return setupAgent({
297
- schemas,
298
- actorSources: {
299
- ...createActorPlaceholdersFromWorkflowConfig(config),
300
- ...requestActors
301
- }
302
- }).createMachine({
303
- ...config.id !== void 0 ? { id: config.id } : {},
304
- ...config.description !== void 0 ? { description: config.description } : {},
305
- ...config.context !== void 0 ? { context: ({ input }) => require_events.validateSchemaSync(schemas.context, evaluateWorkflowConfigValue(config.context, { input })) } : {},
306
- initial: config.initial,
307
- states: (() => {
308
- const rootKeys = new Set(Object.keys(config.states));
309
- return Object.fromEntries(Object.entries(config.states).map(([key, state]) => [key, lowerWorkflowState(key, state, rootKeys)]));
310
- })(),
311
- ...config.meta !== void 0 ? { meta: config.meta } : {}
312
- });
313
- }
314
- //#endregion
315
- //#region src/setup-agent.ts
316
- const emptyEventSchema = { "~standard": {
317
- version: 1,
318
- vendor: "statelyai-agent",
319
- validate(value) {
320
- return value !== null && typeof value === "object" && Object.keys(value).length === 0 ? { value: {} } : { issues: [{ message: "Expected an empty event payload" }] };
321
- }
322
- } };
323
- function normalizeEventSchemas(events) {
324
- return Object.fromEntries(Object.entries(events).map(([type, schema]) => [type, schema && typeof schema === "object" && "~standard" in schema ? schema : emptyEventSchema]));
325
- }
326
- /**
327
- * Builds a machine's {@link AgentSchemaPack} from a partial schema
328
- * declaration — only `context` is required; `events`/`input`/`output`/`meta`
329
- * default to empty/unknown schemas when omitted. Pass the result as
330
- * `setupAgent({ schemas })`'s `schemas` (or spread the individual fields
331
- * directly into `setupAgent({ context, events, ... })` — both forms are
332
- * accepted).
333
- */
334
- function createAgentSchemas(schemas) {
335
- return {
336
- context: schemas.context,
337
- events: normalizeEventSchemas(schemas.events ?? {}),
338
- input: schemas.input,
339
- output: schemas.output,
340
- meta: schemas.meta,
341
- emitted: schemas.emitted
342
- };
343
- }
344
- function mergeContextSchema(base, fields) {
345
- return { "~standard": {
346
- version: 1,
347
- vendor: "statelyai-agent",
348
- validate(value) {
349
- const baseResult = base["~standard"].validate(value);
350
- if (baseResult instanceof Promise) throw new Error("setupAgent: async context schemas are not supported.");
351
- if (baseResult.issues) return baseResult;
352
- const merged = { ...baseResult.value };
353
- const issues = [];
354
- for (const [key, fieldSchema] of Object.entries(fields)) {
355
- const fieldResult = fieldSchema["~standard"].validate(value[key]);
356
- if (fieldResult instanceof Promise) throw new Error("setupAgent: async context schemas are not supported.");
357
- if (fieldResult.issues) issues.push(...fieldResult.issues.map((issue) => ({
358
- ...issue,
359
- path: [key, ...issue.path ?? []]
360
- })));
361
- else merged[key] = fieldResult.value;
362
- }
363
- return issues.length > 0 ? { issues } : { value: merged };
364
- }
365
- } };
366
- }
367
- function resolveAgentStateSchemas(contextSchema, states) {
368
- return Object.fromEntries(Object.entries(states).map(([key, state]) => {
369
- if (!state || typeof state !== "object") return [key, state];
370
- const children = "states" in state && state.states ? resolveAgentStateSchemas(contextSchema, state.states) : void 0;
371
- if ("context" in state && state.context) return [key, {
372
- schemas: { context: mergeContextSchema(contextSchema, state.context) },
373
- ...children ? { states: children } : {}
374
- }];
375
- return [key, children ? {
376
- ...state,
377
- states: children
378
- } : state];
379
- }));
380
- }
381
- /**
382
- * Schema-first `setup(...)` for agent machines — the standard entry point
383
- * for authoring a machine (the blueprint) that this library then runs (via
384
- * {@link runAgent} or the step helpers) against host-supplied model/decision
385
- * executors. Context, events, machine input, machine output, and
386
- * state/transition meta are all standard schemas — no `{} as Type` casts —
387
- * and are retained on `result.schemas` for runtime validation. Also
388
- * registers the `agent.generateText`/`agent.streamText`/`agent.userInput`/
389
- * `agent.decide`/`agent.plan` builtin actors and lowers `requests`/`actorSources` into the
390
- * machine's actor sources. The result is the xstate `setup(...)` object with
391
- * a wrapped `result.createMachine(...)` plus `result.schemas`/`models`/
392
- * `requests`/`appendMessages` attached. Also has a
393
- * `setupAgent.fromConfig(...)` namespace member for building a machine from
394
- * a serializable {@link AgentWorkflowConfig} instead of this TS API.
395
- *
396
- * @example
397
- * ```ts
398
- * const schemas = createAgentSchemas({
399
- * context: z.object({ topic: z.string(), joke: z.string().nullable() }),
400
- * input: z.object({ topic: z.string() }),
401
- * output: z.object({ joke: z.string() }),
402
- * });
403
- *
404
- * const agent = setupAgent({
405
- * schemas,
406
- * actorSources: { tellJoke },
407
- * });
408
- *
409
- * const jokeMachine = agent.createMachine({
410
- * context: ({ input }) => ({ topic: input.topic, joke: null }),
411
- * initial: 'telling',
412
- * states: {
413
- * telling: {
414
- * invoke: {
415
- * id: 'joke',
416
- * src: 'tellJoke',
417
- * input: ({ context }) => ({ topic: context.topic }),
418
- * onDone: ({ output }) => ({ target: 'done', context: { joke: output } }),
419
- * },
420
- * },
421
- * done: { type: 'final', output: ({ context }) => ({ joke: context.joke ?? '' }) },
422
- * },
423
- * });
424
- * ```
425
- */
426
- function setupAgent(config) {
427
- return createSetupAgent(config);
428
- }
429
- function collectFinalStateOutputs(states, outputs = []) {
430
- for (const state of Object.values(states ?? {})) {
431
- if (state?.type === "final" && state.output !== void 0) outputs.push(state.output);
432
- collectFinalStateOutputs(state?.states, outputs);
433
- }
434
- return outputs;
435
- }
436
- function withRootOutputFromSingleFinal(config) {
437
- if (!config || typeof config !== "object" || "output" in config || !("states" in config)) return config;
438
- const outputs = collectFinalStateOutputs(config.states);
439
- return outputs.length === 1 ? {
440
- ...config,
441
- output: outputs[0]
442
- } : config;
443
- }
444
- (function(_setupAgent) {
445
- function fromConfig(config, options) {
446
- return setupAgentFromConfig(config, options);
447
- }
448
- _setupAgent.fromConfig = fromConfig;
449
- })(setupAgent || (setupAgent = {}));
450
- /** Builds one TextLogic actor per `setupAgent({ requests })` entry. @internal */
451
- function createRequestActors(requests) {
452
- return Object.fromEntries(Object.entries(requests).map(([key, request]) => {
453
- return [key, require_events.createTextLogic({
454
- ...request,
455
- name: key,
456
- mode: request.mode ?? "generate"
457
- })];
458
- }));
459
- }
460
- function normalizeAgentSchemas(config) {
461
- if ("schemas" in config && config.schemas) return config.schemas;
462
- const loose = config;
463
- return createAgentSchemas({
464
- ...loose,
465
- context: loose.context
466
- });
467
- }
468
- function normalizeAgentRequestInput(requests) {
469
- return requests ?? {};
470
- }
471
- /**
472
- * Runtime guard: a key appearing in both `actorSources`/`requests` is almost
473
- * certainly a mistake (whichever spread applies last would silently win) —
474
- * fail fast with a clear message rather than let one implementation shadow
475
- * another.
476
- */
477
- function assertNoActorKeyCollisions(actorSources, requests) {
478
- const seenIn = /* @__PURE__ */ new Map();
479
- const groups = [["actorSources", actorSources], ["requests", requests]];
480
- for (const [groupName, group] of groups) for (const key of Object.keys(group ?? {})) {
481
- const existingGroup = seenIn.get(key);
482
- if (existingGroup) throw new Error(`setupAgent: key '${key}' is defined in both '${existingGroup}' and '${groupName}'. Each actor source key must be unique across 'actorSources' and 'requests'.`);
483
- seenIn.set(key, groupName);
484
- }
485
- }
486
- const RESERVED_AGENT_ACTOR_KEYS = [
487
- ...Object.keys(require_events.builtinTextActors),
488
- require_events.USER_INPUT_ACTOR,
489
- require_events.DECIDE_ACTOR,
490
- require_events.PLAN_ACTOR
491
- ];
492
- /**
493
- * Rejects a user-supplied `actorSources`/`requests` key in the reserved
494
- * `agent.*` builtin namespace. Without this, the builtins-first spread in
495
- * {@link createAgentActorSources} lets such a key overwrite the builtin
496
- * (`agent.decide`, `agent.plan`, …) silently. Deliberate override of a builtin
497
- * is still possible after the machine is created, via
498
- * `machine.provide({ actorSources: { 'agent.decide': ... } })`.
499
- */
500
- function assertNoReservedAgentKeys(actorSources, requests) {
501
- const groups = [["actorSources", actorSources], ["requests", requests]];
502
- for (const [groupName, group] of groups) for (const key of Object.keys(group ?? {})) if (RESERVED_AGENT_ACTOR_KEYS.includes(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({ actorSources: { '${key}': ... } }).`);
503
- }
504
- function createAgentActorSources(actorSources, requestActors) {
505
- assertNoActorKeyCollisions(actorSources, requestActors);
506
- assertNoReservedAgentKeys(actorSources, requestActors);
507
- return {
508
- ...require_events.builtinTextActors,
509
- [require_events.USER_INPUT_ACTOR]: require_events.userInputActor,
510
- [require_events.DECIDE_ACTOR]: require_decision.createDecideActor(),
511
- [require_events.PLAN_ACTOR]: require_decision.createPlanActor(),
512
- ...actorSources,
513
- ...requestActors
514
- };
515
- }
516
- function createAgentSetupConfig(schemas, actorSources, config) {
517
- return {
518
- schemas: {
519
- context: schemas.context,
520
- events: schemas.events,
521
- input: schemas.input,
522
- output: schemas.output,
523
- meta: schemas.meta,
524
- ...schemas.emitted && Object.keys(schemas.emitted).length > 0 ? { emitted: schemas.emitted } : {}
525
- },
526
- ...config.states ? { states: resolveAgentStateSchemas(schemas.context, config.states) } : {},
527
- actorSources,
528
- actions: config.actions,
529
- guards: config.guards,
530
- delays: config.delays
531
- };
532
- }
533
- function createSetupAgent(config) {
534
- const schemas = normalizeAgentSchemas(config);
535
- const requestActors = createRequestActors(normalizeAgentRequestInput(config.requests));
536
- const actorSources = createAgentActorSources(config.actorSources, requestActors);
537
- const base = (0, xstate.setup)(createAgentSetupConfig(schemas, actorSources, config));
538
- const createBaseMachine = base.createMachine.bind(base);
539
- const models = config.models ?? {};
540
- const machineOptions = {
541
- schemas,
542
- actorSources,
543
- models
544
- };
545
- return Object.assign(base, {
546
- createMachine(machineConfig) {
547
- const machine = createBaseMachine(withRootOutputFromSingleFinal(machineConfig));
548
- require_events.agentExecutionOptions.set(machine, machineOptions);
549
- if (config.isSuspended) {
550
- const rootConfig = machine.config;
551
- if (rootConfig) require_events.machineSuspensionPredicates.set(rootConfig, config.isSuspended);
552
- }
553
- return machine;
554
- },
555
- schemas,
556
- models,
557
- requests: requestActors,
558
- appendMessages(resolve) {
559
- return appendMessages(resolve);
560
- }
561
- });
562
- }
563
- //#endregion
564
7
  //#region src/internal/state-request-pass.ts
565
8
  async function runTextPhase(stateRequest, baseMessages, deps) {
566
9
  const { model, system } = stateRequest;
567
10
  const id = stateRequest.id ?? deps.nextRequestId();
568
- const promptMessage = require_events.userMessage(stateRequest.prompt);
11
+ const promptMessage = require_decision.userMessage(stateRequest.prompt);
569
12
  if (stateRequest.kind === "decision") return {
570
13
  stateRequest,
571
14
  id,
@@ -582,7 +25,7 @@ async function runTextPhase(stateRequest, baseMessages, deps) {
582
25
  const agentRequest = {
583
26
  kind: "text",
584
27
  id,
585
- src: require_events.INTERPRET_SOURCE,
28
+ src: require_decision.INTERPRET_SOURCE,
586
29
  mode: "generate",
587
30
  input: request,
588
31
  tools: {},
@@ -596,9 +39,11 @@ async function runTextPhase(stateRequest, baseMessages, deps) {
596
39
  let output;
597
40
  try {
598
41
  const raw = await deps.generateText(request, { signal: deps.signal });
599
- output = await require_events.normalizeGeneratorResult(raw, id, { request });
42
+ output = await require_decision.normalizeGeneratorResult(raw, id, { request });
600
43
  const rawReasoning = raw?.reasoning;
601
44
  const reasoning = typeof rawReasoning === "string" ? rawReasoning : void 0;
45
+ const usage = require_decision.extractCallUsage(raw);
46
+ if (usage) deps.recordUsage?.(usage);
602
47
  deps.onResult?.(agentRequest, {
603
48
  output,
604
49
  raw
@@ -608,7 +53,8 @@ async function runTextPhase(stateRequest, baseMessages, deps) {
608
53
  request: agentRequest,
609
54
  output,
610
55
  raw,
611
- ...reasoning !== void 0 ? { reasoning } : {}
56
+ ...reasoning !== void 0 ? { reasoning } : {},
57
+ ...usage !== void 0 ? { usage } : {}
612
58
  });
613
59
  } catch (error) {
614
60
  deps.onTrace?.({
@@ -622,7 +68,7 @@ async function runTextPhase(stateRequest, baseMessages, deps) {
622
68
  stateRequest,
623
69
  id,
624
70
  output,
625
- appended: [promptMessage, require_events.assistantMessage(typeof output === "string" ? output : JSON.stringify(output))]
71
+ appended: [promptMessage, require_decision.assistantMessage(typeof output === "string" ? output : JSON.stringify(output))]
626
72
  };
627
73
  }
628
74
  async function runAdvancePhase(plan, deps) {
@@ -637,13 +83,13 @@ async function runAdvancePhase(plan, deps) {
637
83
  messages: deps.messages
638
84
  }) : stateRequest.onDone;
639
85
  if (!resolved) return false;
640
- const acceptedTypes = require_events.getAcceptedEvents(snapshot, { schemas: deps.schemas }).map((descriptor) => descriptor.type);
86
+ const acceptedTypes = require_decision.getAcceptedEvents(snapshot, { schemas: deps.schemas }).map((descriptor) => descriptor.type);
641
87
  if (!acceptedTypes.includes(resolved.type)) throw new Error(`runAgent: getRequests request '${id}' resolved onDone to event '${resolved.type}', which the current state does not accept. Accepted: ${acceptedTypes.join(", ") || "(none)"}.`);
642
88
  if (!snapshot.can(resolved)) return false;
643
89
  deps.send(resolved);
644
90
  return true;
645
91
  }
646
- const events = require_events.getAcceptedEvents(deps.getSnapshot(), {
92
+ const events = require_decision.getAcceptedEvents(deps.getSnapshot(), {
647
93
  schemas: deps.schemas,
648
94
  ...stateRequest.allowedEvents ? { eventTypes: stateRequest.allowedEvents } : {}
649
95
  });
@@ -662,7 +108,7 @@ async function runAdvancePhase(plan, deps) {
662
108
  canTake: (event) => deps.getSnapshot().can(event)
663
109
  });
664
110
  if (deps.isSettled()) return false;
665
- deps.appendToLog(require_events.assistantMessage(`[chose: ${chosen.type}]`));
111
+ deps.appendToLog(require_decision.assistantMessage(`[chose: ${chosen.type}]`));
666
112
  deps.send(chosen);
667
113
  return true;
668
114
  }
@@ -696,12 +142,12 @@ async function runStateRequestPass(requests, deps) {
696
142
  * machine simply takes no transition). Opt out with
697
143
  * {@link RunAgentOptions.onIllegalResumeEvent} `'ignore'`.
698
144
  */
699
- var IllegalResumeEventError = class extends Error {
145
+ var AgentIllegalResumeEventError = class extends require_errors.AgentError {
700
146
  eventType;
701
147
  acceptedTypes;
702
148
  constructor(eventType, acceptedTypes) {
703
- super(`runAgent: cannot resume with event '${eventType}' — the restored state does not accept it. Accepted event types: ${acceptedTypes.length > 0 ? acceptedTypes.join(", ") : "(none)"}.`);
704
- this.name = "IllegalResumeEventError";
149
+ super("illegal-resume-event", `runAgent: cannot resume with event '${eventType}' — the restored state does not accept it. Accepted event types: ${acceptedTypes.length > 0 ? acceptedTypes.join(", ") : "(none)"}.`);
150
+ this.name = "AgentIllegalResumeEventError";
705
151
  this.eventType = eventType;
706
152
  this.acceptedTypes = acceptedTypes;
707
153
  }
@@ -715,30 +161,30 @@ var IllegalResumeEventError = class extends Error {
715
161
  * the snapshot may no longer resume cleanly. `from` is the snapshot's version,
716
162
  * `to` the current machine's.
717
163
  */
718
- var SnapshotVersionMismatchError = class extends Error {
164
+ var AgentSnapshotVersionMismatchError = class extends require_errors.AgentError {
719
165
  from;
720
166
  to;
721
167
  machineId;
722
168
  constructor(from, to, machineId) {
723
- super(`runAgent: cannot resume snapshot stamped with machine version '${from}' against machine '${machineId}' at version '${to}' — the machine's structure changed since the snapshot was persisted. Provide options.migrateSnapshot to adapt it, or set options.onVersionMismatch to 'warn'/'ignore' to proceed anyway.`);
724
- this.name = "SnapshotVersionMismatchError";
169
+ super("snapshot-version-mismatch", `runAgent: cannot resume snapshot stamped with machine version '${from}' against machine '${machineId}' at version '${to}' — the machine's structure changed since the snapshot was persisted. Provide options.migrateSnapshot to adapt it, or set options.onVersionMismatch to 'warn'/'ignore' to proceed anyway.`);
170
+ this.name = "AgentSnapshotVersionMismatchError";
725
171
  this.from = from;
726
172
  this.to = to;
727
173
  this.machineId = machineId;
728
174
  }
729
175
  };
730
176
  /**
731
- * Thrown by {@link runAgentToCompletion} when the run settles `idle` instead of
177
+ * Thrown by {@link generateResult} when the run settles `idle` instead of
732
178
  * `done`: the machine paused for external input. Carries the idle `snapshot`
733
179
  * and `acceptedTypes` (the event types that could resume it, via
734
180
  * {@link getAcceptedEvents}). Use {@link runAgent} directly when idle is an
735
181
  * expected outcome you handle.
736
182
  */
737
- var AgentIdleError = class extends Error {
183
+ var AgentIdleError = class extends require_errors.AgentError {
738
184
  snapshot;
739
185
  acceptedTypes;
740
186
  constructor(snapshot, acceptedTypes) {
741
- super(`runAgentToCompletion: the machine paused (idle) instead of completing. Resume it by calling runAgent with one of these events: ${acceptedTypes.length > 0 ? acceptedTypes.join(", ") : "(none)"}.`);
187
+ super("agent-idle", `generateResult: the machine paused (idle) instead of completing. Resume it by calling runAgent with one of these events: ${acceptedTypes.length > 0 ? acceptedTypes.join(", ") : "(none)"}.`);
742
188
  this.name = "AgentIdleError";
743
189
  this.snapshot = snapshot;
744
190
  this.acceptedTypes = acceptedTypes;
@@ -752,17 +198,104 @@ var AgentIdleError = class extends Error {
752
198
  * {@link traceTransitions}.
753
199
  */
754
200
  const AGENT_TRACE_SCHEMA_VERSION = 1;
201
+ const TRACE_ENVELOPE_KEYS = [
202
+ "schemaVersion",
203
+ "runId",
204
+ "seq",
205
+ "timestamp",
206
+ "machineId",
207
+ "machineVersion",
208
+ "type",
209
+ "status",
210
+ "cause",
211
+ "eventId",
212
+ "reasoning",
213
+ "chunk"
214
+ ];
215
+ /**
216
+ * Best-effort JSON projection of an arbitrary value. Never throws: functions,
217
+ * symbols, `undefined`, and cyclic back-references are DROPPED (array holes
218
+ * become `null`), non-finite numbers become `null`, `bigint`s become strings,
219
+ * `Error`s become `{ name, message, stack?, code? }`, and anything with a
220
+ * `toJSON()` (e.g. `Date`) is projected through it — the same losses a
221
+ * `JSON.parse(JSON.stringify(...))` round-trip incurs, minus the throws.
222
+ */
223
+ function toJsonValue(value, ancestors) {
224
+ if (value === null) return null;
225
+ const type = typeof value;
226
+ if (type === "string" || type === "boolean") return value;
227
+ if (type === "number") return Number.isFinite(value) ? value : null;
228
+ if (type === "bigint") return value.toString();
229
+ if (type !== "object") return;
230
+ const object = value;
231
+ if (ancestors.includes(object)) return;
232
+ const nextAncestors = [...ancestors, object];
233
+ if (object instanceof Error) {
234
+ const serialized = {
235
+ name: object.name,
236
+ message: object.message
237
+ };
238
+ if (typeof object.stack === "string") serialized.stack = object.stack;
239
+ const code = object.code;
240
+ if (typeof code === "string") serialized.code = code;
241
+ const cause = toJsonValue(object.cause, nextAncestors);
242
+ if (cause !== void 0) serialized.cause = cause;
243
+ return serialized;
244
+ }
245
+ const toJSON = object.toJSON;
246
+ if (typeof toJSON === "function") return toJsonValue(toJSON.call(object), nextAncestors);
247
+ if (Array.isArray(object)) return object.map((item) => toJsonValue(item, nextAncestors) ?? null);
248
+ const out = {};
249
+ for (const [key, item] of Object.entries(object)) {
250
+ const serializedItem = toJsonValue(item, nextAncestors);
251
+ if (serializedItem !== void 0) out[key] = serializedItem;
252
+ }
253
+ return out;
254
+ }
255
+ /**
256
+ * Projects an {@link AgentTraceEvent} into a guaranteed JSON-safe envelope —
257
+ * the form the trace stream is actually sold for (one `JSON.stringify` per line
258
+ * in a JSONL file). Live values are sanitized rather than trusted:
259
+ *
260
+ * - Snapshots (`run.start`, `machine.transition`, `run.end`) go through the
261
+ * same JSON round-trip as {@link persistSnapshot}, so what lands on disk is
262
+ * what a resume would see.
263
+ * - `request.end`'s `raw` (a provider SDK object, frequently cyclic) is DROPPED
264
+ * unless `includeRaw` is set, in which case it is sanitized like everything
265
+ * else.
266
+ * - Non-serializable values anywhere (functions, symbols, `undefined`, cyclic
267
+ * back-references) are dropped; `Error`s become `{ name, message, stack?,
268
+ * code? }` instead of `{}`. Nothing throws.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * await appendFile('trace.jsonl', JSON.stringify(serializeTraceEvent(event)) + '\n');
273
+ * ```
274
+ */
275
+ function serializeTraceEvent(event, options = {}) {
276
+ const out = {};
277
+ for (const [key, value] of Object.entries(event)) {
278
+ if (key === "raw" && !options.includeRaw) continue;
279
+ if (TRACE_ENVELOPE_KEYS.includes(key)) {
280
+ if (value !== void 0) out[key] = value;
281
+ continue;
282
+ }
283
+ const serialized = toJsonValue(value, []);
284
+ if (serialized !== void 0) out[key] = serialized;
285
+ }
286
+ return out;
287
+ }
755
288
  let nextRunAgentTraceId = 1;
756
- var MaxModelCallsExceededError = class extends Error {
289
+ var AgentMaxModelCallsExceededError = class extends require_errors.AgentError {
757
290
  constructor() {
758
- super("runAgent exceeded maxModelCalls.");
759
- this.name = "MaxModelCallsExceededError";
291
+ super("max-model-calls-exceeded", "runAgent exceeded maxModelCalls.");
292
+ this.name = "AgentMaxModelCallsExceededError";
760
293
  }
761
294
  };
762
295
  function wrapsDecisionExhausted(error) {
763
296
  let current = error;
764
297
  for (let depth = 0; depth < 10 && current != null; depth++) {
765
- if (current instanceof require_decision.DecisionExhaustedError) return true;
298
+ if (current instanceof require_decision.AgentDecisionExhaustedError) return true;
766
299
  current = current.cause;
767
300
  }
768
301
  return false;
@@ -790,14 +323,14 @@ function collectConfiguredInvokeSrcs(stateConfig, stateName, out) {
790
323
  /**
791
324
  * Duck-types a state machine actor logic (an invoked child machine) vs. any
792
325
  * other actor logic. xstate's `StateMachine` carries `.config`, `.root`, and
793
- * a `.provide(...)` method plus an `implementations.actorSources` map — this
326
+ * a `.provide(...)` method plus a `sources.actors` map — this
794
327
  * combination is unique to machines and survives the dual-package/version
795
328
  * boundary an `instanceof` check would not. Used to descend the bind-time
796
329
  * walk into invoked child machines (their internal agent requests are opaque
797
330
  * to the parent-level source walk otherwise).
798
331
  */
799
332
  function isStateMachine(logic) {
800
- return !!logic && typeof logic === "object" && "config" in logic && "root" in logic && typeof logic.provide === "function" && typeof logic.implementations === "object" && !!logic.implementations?.actorSources;
333
+ return !!logic && typeof logic === "object" && "config" in logic && "root" in logic && typeof logic.provide === "function" && typeof logic.sources === "object" && !!logic.sources?.actors;
801
334
  }
802
335
  /**
803
336
  * Fails fast (throws) at bind time — before any actor runs — when the
@@ -832,18 +365,18 @@ function assertMachineBindable(machine, effectiveSources, options, ctx) {
832
365
  assertChildMachineBindable(src, src, stateName, options, ctx);
833
366
  continue;
834
367
  }
835
- if ((require_events.isTextLogic(src) || require_decision.isDecisionLogic(src) || require_decision.isPlanLogic(src)) && !require_events.executorBoundLogics.has(src)) throw new Error(`runAgent: ${where} '${stateName}' invokes a direct-object actor logic (kind: '${src.kind}'). Direct-object invoke srcs cannot be rebound by runAgent — either call '.withExecutor(...)' on the logic before invoking it, or register it as a string-keyed actor source instead (machine.provide({ actorSources: { name: logic } })) and invoke it by name.`);
368
+ if ((require_decision.isTextLogic(src) || require_decision.isDecisionLogic(src) || require_decision.isPlanLogic(src)) && !require_decision.executorBoundLogics.has(src)) throw new Error(`runAgent: ${where} '${stateName}' invokes a direct-object actor logic (kind: '${src.kind}'). Direct-object invoke srcs cannot be rebound by runAgent — either call '.withExecutor(...)' on the logic before invoking it, or register it as a string-keyed actor source instead (machine.provide({ actors: { name: logic } })) and invoke it by name.`);
836
369
  continue;
837
370
  }
838
371
  const logic = effectiveSources[src];
839
- if (logic === void 0) throw new Error(`runAgent: ${where} '${stateName}' invokes unregistered actor source '${src}'. Provide it via machine.provide({ actorSources: { '${src}': ... } }) or runAgent(machine, { actorSources: { '${src}': ... } }).`);
372
+ if (logic === void 0) throw new Error(`runAgent: ${where} '${stateName}' invokes unregistered actor source '${src}'. Provide it via machine.provide({ actors: { '${src}': ... } }) or runAgent(machine, { actors: { '${src}': ... } }).`);
840
373
  if (isStateMachine(logic)) {
841
374
  assertChildMachineBindable(logic, src, stateName, options, ctx);
842
375
  continue;
843
376
  }
844
377
  if (src === "agent.userInput") continue;
845
378
  if (require_decision.isDecisionLogic(logic)) {
846
- if (require_events.executorBoundLogics.has(logic)) continue;
379
+ if (require_decision.executorBoundLogics.has(logic)) continue;
847
380
  if (!ctx.rebindable) throw unrebindableChildRequestError(ctx.childPath, stateName, src, "decision");
848
381
  if (!options.hasDecide) throw new Error(`runAgent: ${where} '${stateName}' invokes decision source '${src}' but no 'decide' executor was provided to runAgent(...).`);
849
382
  continue;
@@ -853,14 +386,14 @@ function assertMachineBindable(machine, effectiveSources, options, ctx) {
853
386
  if (!options.hasDecide) throw new Error(`runAgent: ${where} '${stateName}' invokes plan source '${src}' but no 'decide' executor was provided to runAgent(...).`);
854
387
  continue;
855
388
  }
856
- if (require_events.isTextLogic(logic)) {
857
- if (require_events.executorBoundLogics.has(logic)) continue;
389
+ if (require_decision.isTextLogic(logic)) {
390
+ if (require_decision.executorBoundLogics.has(logic)) continue;
858
391
  if (!ctx.rebindable) throw unrebindableChildRequestError(ctx.childPath, stateName, src, logic.mode === "stream" ? "streaming text" : "text");
859
392
  if (logic.mode === "stream" && !options.hasStreamText) throw new Error(`runAgent: ${where} '${stateName}' invokes streaming text source '${src}' but no 'streamText' executor was provided to runAgent(...).`);
860
393
  if (logic.mode !== "stream" && !options.hasGenerateText) throw new Error(`runAgent: ${where} '${stateName}' invokes text source '${src}' but no 'generateText' executor was provided to runAgent(...).`);
861
394
  continue;
862
395
  }
863
- if (require_events.isUnboundPlaceholder(logic)) throw new Error(`runAgent: ${where} '${stateName}' invokes actor source '${src}', which has no host execution. Provide it via machine.provide({ actorSources: { '${src}': ... } }) or runAgent(machine, { actorSources: { '${src}': ... } }).`);
396
+ if (require_decision.isUnboundPlaceholder(logic)) throw new Error(`runAgent: ${where} '${stateName}' invokes actor source '${src}', which has no host execution. Provide it via machine.provide({ actors: { '${src}': ... } }) or runAgent(machine, { actors: { '${src}': ... } }).`);
864
397
  }
865
398
  }
866
399
  /** Descends the bind-time walk into an invoked child state machine, guarding
@@ -869,7 +402,7 @@ function assertChildMachineBindable(childMachine, childSrc, stateName, options,
869
402
  if (ctx.visited.has(childMachine)) return;
870
403
  const childName = typeof childSrc === "string" ? childSrc : childMachine.config.id ?? "(child machine)";
871
404
  const childPath = ctx.childPath ? `${ctx.childPath} > ${childName}` : childName;
872
- const childSources = childMachine.implementations.actorSources;
405
+ const childSources = childMachine.sources.actors;
873
406
  assertMachineBindable(childMachine, childSources, options, {
874
407
  isChild: true,
875
408
  childPath,
@@ -885,7 +418,21 @@ function assertChildMachineBindable(childMachine, childSrc, stateName, options,
885
418
  * runAgent's executors — this error is only for the unrebindable direct-object
886
419
  * case. */
887
420
  function unrebindableChildRequestError(childPath, stateName, requestSrc, kind) {
888
- return /* @__PURE__ */ new Error(`runAgent: child machine '${childPath}' (state '${stateName}') invokes ${kind} source '${requestSrc}', which has no host execution and is reached through a direct-object invoke src that runAgent cannot rebind. Requests reached through string-keyed actor sources inherit runAgent's generateText/streamText/decide executors automatically; a direct-object child machine does not. Either bind the request with its own executor (requestLogic.withExecutor(...)), or register the child as a string-keyed actor source (machine.provide({ actorSources: { <child>: childMachine } })) and invoke it by name.`);
421
+ return /* @__PURE__ */ new Error(`runAgent: child machine '${childPath}' (state '${stateName}') invokes ${kind} source '${requestSrc}', which has no host execution and is reached through a direct-object invoke src that runAgent cannot rebind. Requests reached through string-keyed actor sources inherit runAgent's generateText/streamText/decide executors automatically; a direct-object child machine does not. Either bind the request with its own executor (requestLogic.withExecutor(...)), or register the child as a string-keyed actor source (machine.provide({ actors: { <child>: childMachine } })) and invoke it by name.`);
422
+ }
423
+ /**
424
+ * True when the snapshot's active states declare a transition for the reserved
425
+ * `'@agent.usage'` type EXPLICITLY. A catch-all `on: { '*': … }` deliberately
426
+ * does not count: a wildcard is a machine's own event vocabulary, not an
427
+ * opt-in to a library-reserved event, and `snapshot.can(event)` alone cannot
428
+ * tell the two apart (it answers "would this event be taken?", which a
429
+ * wildcard makes true for everything). Gating delivery on the explicit
430
+ * declaration is what keeps `@agent.usage` opt-in by construction — and keeps
431
+ * a wildcard machine's context and event log byte-identical to a run without
432
+ * the feature. @internal
433
+ */
434
+ function declaresUsageTransition(snapshot) {
435
+ return (0, xstate.getNextTransitions)(snapshot).some((transition) => transition.eventType === require_setup_agent.AGENT_USAGE_EVENT_TYPE);
889
436
  }
890
437
  /** Reads the durable invoke id/src off the async actor's own ref (`self`). */
891
438
  function selfIdAndSrc(self) {
@@ -947,9 +494,11 @@ function wrapTextLogicForRunAgent(logic, runCtx) {
947
494
  }, self);
948
495
  runCtx.onChunk?.(chunk, { request: agentRequest });
949
496
  },
950
- signal
497
+ signal,
498
+ ...runCtx.runId !== void 0 ? { runId: runCtx.runId } : {},
499
+ ...id !== "" ? { requestId: id } : {}
951
500
  });
952
- const output = await require_events.normalizeGeneratorResult(raw, id, {
501
+ const output = await require_decision.normalizeGeneratorResult(raw, id, {
953
502
  request,
954
503
  onChunk: (chunk) => {
955
504
  runCtx.onTrace?.({
@@ -962,6 +511,14 @@ function wrapTextLogicForRunAgent(logic, runCtx) {
962
511
  });
963
512
  const rawReasoning = raw?.reasoning;
964
513
  const reasoning = typeof rawReasoning === "string" ? rawReasoning : void 0;
514
+ const usage = require_decision.extractCallUsage(raw);
515
+ if (usage) runCtx.recordUsage?.(usage, {
516
+ kind: "text",
517
+ ...id !== "" ? { id } : {},
518
+ ...src !== "" ? { src } : {},
519
+ model: request.model,
520
+ ...request.name !== void 0 ? { name: request.name } : {}
521
+ }, self);
965
522
  runCtx.onResult?.(agentRequest, {
966
523
  output,
967
524
  raw
@@ -971,7 +528,8 @@ function wrapTextLogicForRunAgent(logic, runCtx) {
971
528
  request: agentRequest,
972
529
  output,
973
530
  raw,
974
- ...reasoning !== void 0 ? { reasoning } : {}
531
+ ...reasoning !== void 0 ? { reasoning } : {},
532
+ ...usage !== void 0 ? { usage } : {}
975
533
  }, self);
976
534
  return { output };
977
535
  } catch (error) {
@@ -984,7 +542,7 @@ function wrapTextLogicForRunAgent(logic, runCtx) {
984
542
  }
985
543
  });
986
544
  }
987
- function createCountingDecide(runCtx, self) {
545
+ function createCountingDecide(runCtx, self, kind = "decision") {
988
546
  return async (attemptRequest) => {
989
547
  runCtx.consumeModelCall();
990
548
  runCtx.onTrace?.({
@@ -992,7 +550,20 @@ function createCountingDecide(runCtx, self) {
992
550
  request: attemptRequest
993
551
  }, self);
994
552
  try {
995
- const result = await runCtx.decide(attemptRequest);
553
+ const result = await runCtx.decide(runCtx.runId !== void 0 ? {
554
+ ...attemptRequest,
555
+ runId: runCtx.runId
556
+ } : attemptRequest);
557
+ const usage = require_decision.extractCallUsage(result);
558
+ if (usage) {
559
+ const { src } = selfIdAndSrc(self);
560
+ runCtx.recordUsage?.(usage, {
561
+ kind,
562
+ ...attemptRequest.id ? { id: attemptRequest.id } : {},
563
+ ...src !== "" ? { src } : {},
564
+ model: attemptRequest.model
565
+ }, self);
566
+ }
996
567
  runCtx.onResult?.(attemptRequest, {
997
568
  output: result.event,
998
569
  raw: result
@@ -1001,7 +572,8 @@ function createCountingDecide(runCtx, self) {
1001
572
  type: "request.end",
1002
573
  request: attemptRequest,
1003
574
  output: result.event,
1004
- raw: result
575
+ raw: result,
576
+ ...usage !== void 0 ? { usage } : {}
1005
577
  }, self);
1006
578
  return result;
1007
579
  } catch (error) {
@@ -1036,7 +608,7 @@ function createRunAgentDecisionLogic(logic, runCtx) {
1036
608
  const declaredEventTypes = logic.allowedEventTypes?.(input);
1037
609
  await Promise.resolve();
1038
610
  const actorRef = invokingActorOf(self, runCtx);
1039
- const events = actorRef ? require_events.getAcceptedEvents(actorRef.getSnapshot(), {
611
+ const events = actorRef ? require_decision.getAcceptedEvents(actorRef.getSnapshot(), {
1040
612
  schemas: runCtx.schemas,
1041
613
  eventTypes: declaredEventTypes
1042
614
  }) : [];
@@ -1079,7 +651,7 @@ function createRunAgentPlanLogic(logic, runCtx) {
1079
651
  if (!runCtx.decide) throw new Error("runAgent: no 'decide' executor provided.");
1080
652
  const { id } = selfIdAndSrc(self);
1081
653
  const stopOn = new Set(input.stopOn ?? []);
1082
- const countingDecide = createCountingDecide(runCtx, self);
654
+ const countingDecide = createCountingDecide(runCtx, self, "plan");
1083
655
  const base = logic.request(input);
1084
656
  let ledger = require_decision.initialPlanLedger(logic, input);
1085
657
  const end = (stopped) => {
@@ -1094,14 +666,14 @@ function createRunAgentPlanLogic(logic, runCtx) {
1094
666
  while (ledger.context.stepsRemaining > 0) {
1095
667
  const actorRef = invokingActor;
1096
668
  if (!actorRef || signal.aborted) break;
1097
- const machineEvents = require_events.getAcceptedEvents(actorRef.getSnapshot(), {
669
+ const machineEvents = require_decision.getAcceptedEvents(actorRef.getSnapshot(), {
1098
670
  schemas: runCtx.schemas,
1099
671
  eventTypes: logic.allowedEventTypes(input)
1100
672
  });
1101
673
  if (machineEvents.length === 0) return end("no-legal-events");
1102
674
  const doneDescriptor = {
1103
675
  type: require_decision.PLAN_DONE_EVENT_TYPE,
1104
- toolName: require_events.sanitizeEventToolName(require_decision.PLAN_DONE_EVENT_TYPE)
676
+ toolName: require_decision.sanitizeEventToolName(require_decision.PLAN_DONE_EVENT_TYPE)
1105
677
  };
1106
678
  const events = machineEvents.some((event) => event.type === "agent.plan.done") ? machineEvents : [...machineEvents, doneDescriptor];
1107
679
  const applied = ledger.context.applied;
@@ -1169,7 +741,7 @@ function rootTraceState(root) {
1169
741
  if (!state) {
1170
742
  const logic = root.logic;
1171
743
  const machineId = (logic?.config)?.id ?? logic?.id ?? "(machine)";
1172
- const machineVersion = logic ? require_events.getMachineStructuralHash(logic) : "";
744
+ const machineVersion = logic ? require_decision.getMachineStructuralHash(logic) : "";
1173
745
  state = {
1174
746
  runId: `run_${nextProvideRunId++}`,
1175
747
  seq: 0,
@@ -1209,6 +781,10 @@ function provideTraceSink(onTrace) {
1209
781
  * actor off `self._parent`, always present under a live `createActor` tree.
1210
782
  * `onTrace` (when given) mints a per-root-actor envelope. `schemas` come from
1211
783
  * the machine's registered `setupAgent` execution options.
784
+ *
785
+ * `recordUsage` has no run-level aggregate to fold into here (there is no
786
+ * run), so it does one thing: deliver the reserved `@agent.usage` event, gated
787
+ * exactly like runAgent's — see {@link deliverUsageToInvokingActor}.
1212
788
  */
1213
789
  function provideBindContext(machine, executors, options) {
1214
790
  return {
@@ -1218,9 +794,43 @@ function provideBindContext(machine, executors, options) {
1218
794
  onChunk: options.onChunk ? (chunk) => options.onChunk(chunk) : void 0,
1219
795
  onTrace: provideTraceSink(options.onTrace),
1220
796
  consumeModelCall: () => {},
797
+ recordUsage: (usage, source, self) => {
798
+ deliverUsageToInvokingActor(usage, source ?? {}, self);
799
+ },
1221
800
  actorHolder: { actorRef: void 0 },
1222
- schemas: require_events.getRegisteredAgentExecutionOptions(machine).schemas
801
+ schemas: require_decision.getRegisteredAgentExecutionOptions(machine).schemas
802
+ };
803
+ }
804
+ /**
805
+ * `provideExecutors`' counterpart to runAgent's `deliverUsageEvent`: after a
806
+ * bound call settles with reported usage, send the reserved
807
+ * `@agent.usage` event to the machine actor that INVOKED the request — read
808
+ * off the settling request actor's `self._parent`, which under a live
809
+ * `createActor` tree is always the invoking machine (there is no run-scoped
810
+ * root actor on this path).
811
+ *
812
+ * Gated identically to runAgent: the invoking snapshot must be active, must
813
+ * declare an `'@agent.usage'` transition EXPLICITLY (see
814
+ * {@link declaresUsageTransition} — a catch-all `on: { '*' }` is not an opt-in),
815
+ * and must be able to take the event. There is no cycle to settle in
816
+ * uncontrolled mode, so there are no dropped stragglers.
817
+ *
818
+ * Delivery follows `provideExecutors`' binding boundary: only sources IT bound
819
+ * report here, so an invoked child machine that was not itself passed through
820
+ * `provideExecutors` reports nothing. @internal
821
+ */
822
+ function deliverUsageToInvokingActor(usage, source, self) {
823
+ const actorRef = self?._parent;
824
+ if (!actorRef) return;
825
+ const snapshot = actorRef.getSnapshot();
826
+ if (snapshot?.status !== "active" || !declaresUsageTransition(snapshot)) return;
827
+ const event = {
828
+ type: require_setup_agent.AGENT_USAGE_EVENT_TYPE,
829
+ ...source,
830
+ usage
1223
831
  };
832
+ if (!snapshot.can(event)) return;
833
+ actorRef.send(event);
1224
834
  }
1225
835
  /**
1226
836
  * Host-binds one text/stream source for {@link provideExecutors} using the SAME
@@ -1265,19 +875,19 @@ function bindPlanForProvide(machine, logic, executors, options) {
1265
875
  function rebindChildMachine(childMachine, runCtx, visited) {
1266
876
  if (visited.has(childMachine)) return childMachine;
1267
877
  const childVisited = new Set([...visited, childMachine]);
1268
- const sources = childMachine.implementations.actorSources;
878
+ const sources = childMachine.sources.actors;
1269
879
  const wrapped = {};
1270
880
  for (const [key, logic] of Object.entries(sources)) {
1271
881
  if (require_decision.isDecisionLogic(logic)) {
1272
- if (!require_events.executorBoundLogics.has(logic)) wrapped[key] = createRunAgentDecisionLogic(logic, runCtx);
882
+ if (!require_decision.executorBoundLogics.has(logic)) wrapped[key] = createRunAgentDecisionLogic(logic, runCtx);
1273
883
  continue;
1274
884
  }
1275
885
  if (require_decision.isPlanLogic(logic)) {
1276
886
  wrapped[key] = createRunAgentPlanLogic(logic, runCtx);
1277
887
  continue;
1278
888
  }
1279
- if (require_events.isTextLogic(logic)) {
1280
- if (!require_events.executorBoundLogics.has(logic)) wrapped[key] = wrapTextLogicForRunAgent(logic, runCtx);
889
+ if (require_decision.isTextLogic(logic)) {
890
+ if (!require_decision.executorBoundLogics.has(logic)) wrapped[key] = wrapTextLogicForRunAgent(logic, runCtx);
1281
891
  continue;
1282
892
  }
1283
893
  if (isStateMachine(logic)) {
@@ -1286,7 +896,7 @@ function rebindChildMachine(childMachine, runCtx, visited) {
1286
896
  continue;
1287
897
  }
1288
898
  }
1289
- return Object.keys(wrapped).length > 0 ? childMachine.provide({ actorSources: wrapped }) : childMachine;
899
+ return Object.keys(wrapped).length > 0 ? childMachine.provide({ actors: wrapped }) : childMachine;
1290
900
  }
1291
901
  /**
1292
902
  * Runs an agent machine to completion or idle: a `createActor` host that
@@ -1303,7 +913,7 @@ function rebindChildMachine(childMachine, runCtx, visited) {
1303
913
  *
1304
914
  * Binding happens **before** the actor starts: every invoke the machine
1305
915
  * could reach is walked and checked against the effective actor sources
1306
- * (`options.actorSources` merged onto the machine), so a missing
916
+ * (`options.actors` merged onto the machine), so a missing
1307
917
  * `streamText`/`decide` executor or any other unbound actor source throws
1308
918
  * immediately — a bind-time error, not a mid-run failure. The one exception
1309
919
  * is `agent.userInput`: unhandled, it binds as a pending placeholder that
@@ -1328,14 +938,42 @@ function rebindChildMachine(childMachine, runCtx, visited) {
1328
938
  * lives in an adapter — use `createAiSdkExecutors` from '@statelyai/agent/ai-sdk'.
1329
939
  */
1330
940
  async function runAgent(machine, options) {
941
+ return createAgentSession(machine, options, { oneShot: true }).settled();
942
+ }
943
+ /**
944
+ * Session mode: {@link runAgent}'s engine with a long-lived actor. Use it when
945
+ * the agent is a *session* fed by external events (chat turns, device or
946
+ * timer events, a socket) rather than a one-shot job — you keep the log,
947
+ * budget, traces, and idle semantics that bare `provideExecutors` +
948
+ * `createActor` would forfeit.
949
+ *
950
+ * ```ts
951
+ * const session = createAgentActor(machine, { input, executors });
952
+ * let result = await session.settled(); // first quiescence
953
+ * while (result.status === "idle") {
954
+ * session.actor.send(await nextUserEvent(result.snapshot));
955
+ * result = await session.settled(); // next quiescence, same log
956
+ * }
957
+ * session.stop();
958
+ * ```
959
+ *
960
+ * Accepts the same options as {@link runAgent} (including `snapshot`/`events`
961
+ * resume). Not yet supported in session mode: `getRequests` re-interpretation
962
+ * across cycles behaves per-cycle exactly as in `runAgent`.
963
+ */
964
+ function createAgentActor(machine, options) {
965
+ return createAgentSession(machine, options, { oneShot: false });
966
+ }
967
+ function createAgentSession(machine, options, lifecycle) {
1331
968
  const maxModelCalls = options.maxModelCalls ?? 100;
1332
969
  let modelCallCount = 0;
1333
970
  let budgetExceeded = false;
1334
971
  let warnedNonSerializable = false;
972
+ let warnedHeuristicIdle = false;
1335
973
  const runId = `run_${nextRunAgentTraceId++}`;
1336
974
  let traceSeq = 0;
1337
975
  const machineId = machine.config.id ?? machine.id ?? "(machine)";
1338
- const machineVersion = options.machineVersion ?? require_events.getMachineStructuralHash(machine);
976
+ const machineVersion = options.machineVersion ?? machine.version ?? require_decision.getMachineStructuralHash(machine);
1339
977
  const agentMeta = {
1340
978
  machineId,
1341
979
  version: machineVersion
@@ -1355,18 +993,52 @@ async function runAgent(machine, options) {
1355
993
  });
1356
994
  };
1357
995
  const consumeModelCall = () => {
1358
- if (budgetExceeded) throw new MaxModelCallsExceededError();
1359
- modelCallCount += 1;
1360
- if (modelCallCount > maxModelCalls) {
996
+ if (budgetExceeded) throw new AgentMaxModelCallsExceededError();
997
+ if (modelCallCount + 1 > maxModelCalls) {
1361
998
  budgetExceeded = true;
1362
- throw new MaxModelCallsExceededError();
999
+ throw new AgentMaxModelCallsExceededError();
1363
1000
  }
1001
+ modelCallCount += 1;
1002
+ };
1003
+ const tokenTotals = {};
1004
+ const cycleGate = { isResolved: () => false };
1005
+ const deliverUsageEvent = (usage, source) => {
1006
+ const actorRef = actorHolder.actorRef;
1007
+ if (!actorRef) return;
1008
+ const event = {
1009
+ type: require_setup_agent.AGENT_USAGE_EVENT_TYPE,
1010
+ ...source,
1011
+ usage
1012
+ };
1013
+ const snapshot = actorRef.getSnapshot();
1014
+ if (snapshot?.status !== "active" || !declaresUsageTransition(snapshot)) return;
1015
+ if (cycleGate.isResolved()) {
1016
+ onTrace({
1017
+ type: "usage.dropped",
1018
+ event,
1019
+ reason: "settled"
1020
+ });
1021
+ return;
1022
+ }
1023
+ if (!snapshot.can(event)) return;
1024
+ actorRef.send(event);
1364
1025
  };
1026
+ const recordUsage = (usage, source = {}) => {
1027
+ for (const field of require_decision.AGENT_USAGE_TOKEN_FIELDS) {
1028
+ const value = usage[field];
1029
+ if (typeof value === "number" && Number.isFinite(value)) tokenTotals[field] = (tokenTotals[field] ?? 0) + value;
1030
+ }
1031
+ deliverUsageEvent(usage, source);
1032
+ };
1033
+ const runUsage = () => ({
1034
+ ...tokenTotals,
1035
+ modelCalls: modelCallCount
1036
+ });
1365
1037
  const warnNonSerializableContext = (snapshot) => {
1366
1038
  if (warnedNonSerializable || process.env.NODE_ENV === "production") return;
1367
1039
  let offending = [];
1368
1040
  try {
1369
- offending = require_events.findNonSerializableContextPaths(snapshot.context);
1041
+ offending = require_decision.findNonSerializableContextPaths(snapshot.context);
1370
1042
  } catch {
1371
1043
  return;
1372
1044
  }
@@ -1374,8 +1046,8 @@ async function runAgent(machine, options) {
1374
1046
  warnedNonSerializable = true;
1375
1047
  console.warn(`runAgent: context holds value(s) that will not survive snapshot persist/resume (JSON round-trip): ${offending.join(", ")}. Persist only JSON-serializable context, or convert these before the run settles.`);
1376
1048
  };
1377
- const provided = machine.provide({ actorSources: options.actorSources });
1378
- const effectiveSources = provided.implementations.actorSources;
1049
+ const provided = machine.provide({ actors: options.actors });
1050
+ const effectiveSources = provided.sources.actors;
1379
1051
  assertBindable(provided, effectiveSources, {
1380
1052
  hasGenerateText: !!options.executors?.generateText,
1381
1053
  hasDecide: !!options.executors?.decide,
@@ -1391,8 +1063,10 @@ async function runAgent(machine, options) {
1391
1063
  onResult: options.onResult,
1392
1064
  onTrace,
1393
1065
  consumeModelCall,
1066
+ recordUsage,
1394
1067
  actorHolder,
1395
- schemas: require_events.getRegisteredAgentExecutionOptions(machine).schemas
1068
+ runId,
1069
+ schemas: require_decision.getRegisteredAgentExecutionOptions(machine).schemas
1396
1070
  };
1397
1071
  let userInputIsPlaceholder = false;
1398
1072
  const wrappedSources = {};
@@ -1401,7 +1075,7 @@ async function runAgent(machine, options) {
1401
1075
  if (options.userInput) {
1402
1076
  const userInput = options.userInput;
1403
1077
  wrappedSources[key] = (0, xstate.createAsyncLogic)({ run: async ({ input }) => await userInput(input) });
1404
- } else if (require_events.isUnboundPlaceholder(logic)) {
1078
+ } else if (require_decision.isUnboundPlaceholder(logic)) {
1405
1079
  userInputIsPlaceholder = true;
1406
1080
  wrappedSources[key] = (0, xstate.createAsyncLogic)({ run: () => new Promise(() => {}) });
1407
1081
  }
@@ -1415,8 +1089,8 @@ async function runAgent(machine, options) {
1415
1089
  wrappedSources[key] = createRunAgentPlanLogic(logic, runCtx);
1416
1090
  continue;
1417
1091
  }
1418
- if (require_events.isTextLogic(logic)) {
1419
- if (!require_events.executorBoundLogics.has(logic)) wrappedSources[key] = wrapTextLogicForRunAgent(logic, runCtx);
1092
+ if (require_decision.isTextLogic(logic)) {
1093
+ if (!require_decision.executorBoundLogics.has(logic)) wrappedSources[key] = wrapTextLogicForRunAgent(logic, runCtx);
1420
1094
  continue;
1421
1095
  }
1422
1096
  if (isStateMachine(logic)) {
@@ -1425,11 +1099,12 @@ async function runAgent(machine, options) {
1425
1099
  continue;
1426
1100
  }
1427
1101
  }
1428
- const boundMachine = provided.provide({ actorSources: wrappedSources });
1429
- const isSuspended = options.isSuspended ?? require_events.getMachineSuspensionPredicate(machine) ?? (() => false);
1102
+ const boundMachine = provided.provide({ actors: wrappedSources });
1103
+ const declaredSuspensionPredicate = options.isSuspended ?? require_decision.getMachineSuspensionPredicate(machine);
1104
+ const isSuspended = declaredSuspensionPredicate ?? (() => false);
1430
1105
  let effectiveSnapshot = options.snapshot;
1431
1106
  if (effectiveSnapshot !== void 0) {
1432
- const from = effectiveSnapshot.agentMeta?.version;
1107
+ const from = effectiveSnapshot.agentMeta?.version ?? effectiveSnapshot.version;
1433
1108
  if (from !== void 0 && from !== machineVersion) {
1434
1109
  const info = {
1435
1110
  from,
@@ -1438,32 +1113,86 @@ async function runAgent(machine, options) {
1438
1113
  if (options.migrateSnapshot) effectiveSnapshot = options.migrateSnapshot(effectiveSnapshot, info);
1439
1114
  else {
1440
1115
  const mode = options.onVersionMismatch ?? "throw";
1441
- if (mode === "throw") throw new SnapshotVersionMismatchError(from, machineVersion, machineId);
1116
+ if (mode === "throw") throw new AgentSnapshotVersionMismatchError(from, machineVersion, machineId);
1442
1117
  if (mode === "warn") console.warn(`runAgent: resuming a snapshot stamped with machine version '${from}' against machine '${machineId}' at version '${machineVersion}'. Structural changes may not resume cleanly.`);
1443
1118
  }
1444
1119
  }
1445
1120
  }
1446
- const priorMessages = require_events.getAgentMessages(effectiveSnapshot);
1121
+ const resumeEvents = options.events;
1122
+ if (effectiveSnapshot === void 0 && options.events !== void 0 && options.events[0]?.event.type === "@agent.init") {
1123
+ const { snapshot: replayedSnapshot } = require_setup_agent.replay(machine, resumeEvents, { machineVersion });
1124
+ effectiveSnapshot = machine.getPersistedSnapshot(replayedSnapshot);
1125
+ }
1126
+ const machineOwnVersion = machine.version;
1127
+ if (effectiveSnapshot !== void 0 && effectiveSnapshot.version !== machineOwnVersion) {
1128
+ const aligned = Object.assign(Object.create(Object.getPrototypeOf(effectiveSnapshot)), effectiveSnapshot);
1129
+ if (machineOwnVersion === void 0) delete aligned.version;
1130
+ else aligned.version = machineOwnVersion;
1131
+ effectiveSnapshot = aligned;
1132
+ }
1133
+ const priorMessages = require_decision.getAgentMessages(effectiveSnapshot);
1447
1134
  const messages = typeof options.messages === "function" ? [...options.messages([...priorMessages])] : [...priorMessages, ...options.messages ?? []];
1448
1135
  const stampMessages = (snapshot) => {
1449
1136
  if (!options.getRequests && !options.messages && messages.length === 0) return;
1450
1137
  if (snapshot && typeof snapshot === "object") snapshot.messages = [...messages];
1451
1138
  };
1452
1139
  if (effectiveSnapshot !== void 0 && options.event !== void 0 && (options.onIllegalResumeEvent ?? "throw") === "throw") {
1453
- const acceptedTypes = require_events.getAcceptedEvents((0, xstate.createActor)(boundMachine, { snapshot: effectiveSnapshot }).getSnapshot(), { schemas: runCtx.schemas }).map((descriptor) => descriptor.type);
1140
+ const acceptedTypes = require_decision.getAcceptedEvents((0, xstate.createActor)(boundMachine, { snapshot: effectiveSnapshot }).getSnapshot(), { schemas: runCtx.schemas }).map((descriptor) => descriptor.type);
1454
1141
  const eventType = options.event.type;
1455
- if (!acceptedTypes.includes(eventType)) throw new IllegalResumeEventError(eventType, acceptedTypes);
1142
+ if (!acceptedTypes.includes(eventType)) throw new AgentIllegalResumeEventError(eventType, acceptedTypes);
1143
+ }
1144
+ const replayEvents = [...resumeEvents ?? []];
1145
+ const replayEventIds = /* @__PURE__ */ new Set();
1146
+ for (let index = 0; index < replayEvents.length; index++) {
1147
+ const entry = replayEvents[index];
1148
+ require_event_log_store.assertAgentLogEntry(entry);
1149
+ if (entry.index !== index) throw new Error(`runAgent events must be contiguous from index 0; found entry.index ${entry.index} at position ${index}.`);
1150
+ if (entry.machineId !== machineId || entry.machineVersion !== machineVersion) throw new require_setup_agent.AgentReplayMachineMismatchError(entry.id, entry.index, {
1151
+ machineId,
1152
+ machineVersion
1153
+ }, {
1154
+ machineId: entry.machineId,
1155
+ machineVersion: entry.machineVersion
1156
+ });
1157
+ if (replayEventIds.has(entry.id)) throw new Error(`runAgent events contain duplicate event id '${entry.id}'.`);
1158
+ replayEventIds.add(entry.id);
1456
1159
  }
1457
- return new Promise((resolvePromise) => {
1160
+ const hasCompleteReplayHistory = replayEvents[0]?.event.type === "@agent.init" || effectiveSnapshot === void 0 && replayEvents.length === 0;
1161
+ const appendReplayEvent = (event) => {
1162
+ const entry = require_setup_agent.createReplayEntry(machine, replayEvents, event, {
1163
+ machineVersion,
1164
+ verification: hasCompleteReplayHistory
1165
+ });
1166
+ if (replayEventIds.has(entry.id)) throw new Error(`runAgent generated duplicate event id '${entry.id}'.`);
1167
+ replayEventIds.add(entry.id);
1168
+ replayEvents.push(entry);
1169
+ options.onEvent?.(entry);
1170
+ return entry;
1171
+ };
1172
+ if (replayEvents.length === 0 && effectiveSnapshot === void 0) {
1173
+ const entry = require_setup_agent.initEntry(machine, options.input, { machineVersion });
1174
+ replayEventIds.add(entry.id);
1175
+ replayEvents.push(entry);
1176
+ options.onEvent?.(entry);
1177
+ }
1178
+ return (() => {
1458
1179
  let settled = false;
1180
+ let finalized = false;
1181
+ cycleGate.isResolved = () => settled;
1182
+ let lastResult;
1183
+ const waiters = [];
1459
1184
  let idleTimer;
1460
1185
  let actor;
1461
1186
  let deliveringResumeEvent = options.event !== void 0;
1462
- const settle = (result) => {
1187
+ const settle = (outcome) => {
1463
1188
  if (settled) return;
1464
1189
  settled = true;
1190
+ const result = {
1191
+ ...outcome,
1192
+ events: [...replayEvents],
1193
+ usage: runUsage()
1194
+ };
1465
1195
  if (idleTimer !== void 0) clearTimeout(idleTimer);
1466
- if (options.signal) options.signal.removeEventListener("abort", onAbort);
1467
1196
  stampAgentMeta(result.snapshot);
1468
1197
  stampMessages(result.snapshot);
1469
1198
  if ("persistedSnapshot" in result) {
@@ -1472,10 +1201,15 @@ async function runAgent(machine, options) {
1472
1201
  }
1473
1202
  onTrace({
1474
1203
  type: "run.end",
1475
- ...result
1204
+ ...outcome
1476
1205
  });
1477
- actor.stop();
1478
- resolvePromise(result);
1206
+ if (lifecycle.oneShot || result.status !== "idle") {
1207
+ finalized = true;
1208
+ if (options.signal) options.signal.removeEventListener("abort", onAbort);
1209
+ actor.stop();
1210
+ }
1211
+ lastResult = result;
1212
+ for (const resolve of waiters.splice(0)) resolve(result);
1479
1213
  };
1480
1214
  const onAbort = () => {
1481
1215
  settle({
@@ -1528,6 +1262,7 @@ async function runAgent(machine, options) {
1528
1262
  generateText: runCtx.generateText,
1529
1263
  decide: runCtx.decide ? createCountingDecide(runCtx, void 0) : void 0,
1530
1264
  consumeModelCall,
1265
+ recordUsage,
1531
1266
  nextRequestId: () => `interpret_${++interpretSeq}`,
1532
1267
  onTrace,
1533
1268
  onResult: runCtx.onResult,
@@ -1564,7 +1299,13 @@ async function runAgent(machine, options) {
1564
1299
  if (settled) return;
1565
1300
  const current = actor.getSnapshot();
1566
1301
  if (isIdleSnapshot(current, { ignoreUserInputChildren: userInputIsPlaceholder })) {
1567
- if (!maybeInterpret(current)) settleIdle(current);
1302
+ if (!maybeInterpret(current)) {
1303
+ if (!declaredSuspensionPredicate && current.status === "active" && !warnedHeuristicIdle && process.env.NODE_ENV !== "production") {
1304
+ warnedHeuristicIdle = true;
1305
+ console.warn("[@statelyai/agent] runAgent settled idle via the timing heuristic (no suspension predicate declared). This is best-effort; for deterministic idle detection, declare setupAgent({ isSuspended }) or pass runAgent(machine, { isSuspended }), e.g. (s) => s.hasTag('waiting').");
1306
+ }
1307
+ settleIdle(current);
1308
+ }
1568
1309
  }
1569
1310
  }, 0);
1570
1311
  };
@@ -1574,12 +1315,21 @@ async function runAgent(machine, options) {
1574
1315
  inspect: (event) => {
1575
1316
  if (typeof options.inspect === "function") options.inspect(event);
1576
1317
  else options.inspect?.next?.(event);
1577
- if (settled || event.type !== "@xstate.transition" || event.actorRef !== actor.ref) return;
1318
+ if (event.type !== "@xstate.transition" || event.actorRef !== actor.ref) return;
1319
+ if (settled) {
1320
+ if (finalized) return;
1321
+ settled = false;
1322
+ lastResult = void 0;
1323
+ }
1578
1324
  const snapshot = event.snapshot;
1325
+ let eventId;
1326
+ if (event.event.type !== "@xstate.init" && (event.sourceRef !== event.actorRef || event.event.type === "xstate.timer")) eventId = appendReplayEvent(event.event).id;
1327
+ else if (event.event.type === "@xstate.init") eventId = replayEvents[0]?.event.type === "@agent.init" ? replayEvents[0].id : void 0;
1579
1328
  onTrace({
1580
1329
  type: "machine.transition",
1581
1330
  snapshot,
1582
- event: event.event
1331
+ event: event.event,
1332
+ ...eventId !== void 0 ? { eventId } : {}
1583
1333
  });
1584
1334
  options.onTransition?.(snapshot, event.event);
1585
1335
  if (snapshot.status === "done") {
@@ -1624,6 +1374,19 @@ async function runAgent(machine, options) {
1624
1374
  });
1625
1375
  });
1626
1376
  for (const [type, handler] of Object.entries(options.on ?? {})) if (typeof handler === "function") actor.on(type, handler);
1377
+ const sessionApi = {
1378
+ actor,
1379
+ get events() {
1380
+ return replayEvents;
1381
+ },
1382
+ usage: runUsage,
1383
+ settled: () => settled && lastResult !== void 0 ? Promise.resolve(lastResult) : new Promise((resolve) => {
1384
+ waiters.push(resolve);
1385
+ }),
1386
+ stop: () => {
1387
+ actor.stop();
1388
+ }
1389
+ };
1627
1390
  if (options.signal) {
1628
1391
  if (options.signal.aborted) {
1629
1392
  settle({
@@ -1632,7 +1395,7 @@ async function runAgent(machine, options) {
1632
1395
  error: options.signal.reason ?? /* @__PURE__ */ new Error("Aborted"),
1633
1396
  snapshot: actor.getSnapshot()
1634
1397
  });
1635
- return;
1398
+ return sessionApi;
1636
1399
  }
1637
1400
  options.signal.addEventListener("abort", onAbort);
1638
1401
  }
@@ -1647,13 +1410,17 @@ async function runAgent(machine, options) {
1647
1410
  deliveringResumeEvent = false;
1648
1411
  actor.send(options.event);
1649
1412
  }
1650
- });
1413
+ return sessionApi;
1414
+ })();
1651
1415
  }
1652
1416
  /**
1653
- * Runs an agent machine to a **final state** and returns its output, for
1654
- * run-to-done flows where an idle pause is unexpected. Wraps {@link runAgent}:
1417
+ * Runs an agent machine to a **final state**, for run-to-done flows where an
1418
+ * idle pause is unexpected. Wraps {@link runAgent}:
1655
1419
  *
1656
- * - `done` → resolves with `result.output` (the machine's `OutputFrom`).
1420
+ * - `done` → resolves with the done result: `result.output` (the machine's
1421
+ * `OutputFrom`) plus metadata — `result.snapshot`, the replayable
1422
+ * `result.events`, and the aggregated `result.usage` — the same shape
1423
+ * `generateText` users expect (`text` + call metadata).
1657
1424
  * - `idle` → throws {@link AgentIdleError} carrying the idle snapshot and the
1658
1425
  * event types that could resume it.
1659
1426
  * - `error` → throws `result.error` when it is an `Error`; otherwise wraps it
@@ -1661,18 +1428,18 @@ async function runAgent(machine, options) {
1661
1428
  * `.error` is the raw thrown value.
1662
1429
  *
1663
1430
  * Use {@link runAgent} directly when idle is an expected outcome you handle
1664
- * (human-in-the-loop, resumable flows); use `runAgentToCompletion` when the
1431
+ * (human-in-the-loop, resumable flows); use `generateResult` when the
1665
1432
  * machine is meant to run straight through to a final state.
1666
1433
  */
1667
- async function runAgentToCompletion(machine, options) {
1434
+ async function generateResult(machine, options) {
1668
1435
  const result = await runAgent(machine, options);
1669
- if (result.status === "done") return result.output;
1436
+ if (result.status === "done") return result;
1670
1437
  if (result.status === "idle") {
1671
- const acceptedTypes = require_events.getAcceptedEvents(result.snapshot, { schemas: require_events.getRegisteredAgentExecutionOptions(machine).schemas }).map((descriptor) => descriptor.type);
1438
+ const acceptedTypes = require_decision.getAcceptedEvents(result.snapshot, { schemas: require_decision.getRegisteredAgentExecutionOptions(machine).schemas }).map((descriptor) => descriptor.type);
1672
1439
  throw new AgentIdleError(result.snapshot, acceptedTypes);
1673
1440
  }
1674
1441
  if (result.error instanceof Error) throw result.error;
1675
- const wrapped = /* @__PURE__ */ new Error(`runAgentToCompletion: run failed with cause '${result.cause}'.`);
1442
+ const wrapped = /* @__PURE__ */ new Error(`generateResult: run failed with cause '${result.cause}'.`);
1676
1443
  wrapped.cause = result.cause;
1677
1444
  wrapped.error = result.error;
1678
1445
  throw wrapped;
@@ -1751,6 +1518,105 @@ function collectPendingUserInputs(snapshot) {
1751
1518
  return pending;
1752
1519
  }
1753
1520
  //#endregion
1521
+ //#region src/agent-run.ts
1522
+ /**
1523
+ * Wraps {@link runAgent} in a canonical run-stream handle: the run's trace
1524
+ * events as a pull-based {@link AgentRun.events} async iterator, alongside the
1525
+ * unchanged {@link AgentRun.result} promise.
1526
+ *
1527
+ * Semantics:
1528
+ *
1529
+ * 1. **Starts immediately.** The underlying `runAgent` is invoked on this call,
1530
+ * not on the first `events` iteration — work is already in flight when
1531
+ * `createAgentRun` returns.
1532
+ * 2. **Buffer, don't stall.** Events are queued in an unbounded in-memory
1533
+ * buffer as the run emits them; a slow or absent consumer never applies
1534
+ * backpressure to the run. Once `run.end` is delivered, the iterator
1535
+ * completes.
1536
+ * 3. **Composes `onTrace`.** If `options.onTrace` is passed, it still fires for
1537
+ * every event — the wrapper adds queue delivery, it does not replace the
1538
+ * caller's sink.
1539
+ * 4. **`result` mirrors `runAgent`.** Same resolution and rejection behavior
1540
+ * (see {@link AgentRun.result}). A no-op rejection handler is attached
1541
+ * internally so a caller who reads only `events` never trips an unhandled
1542
+ * rejection on a bind-time throw; the returned promise still rejects for a
1543
+ * caller who awaits it.
1544
+ * 5. **Resume is identical.** Options pass straight through, so starting from a
1545
+ * persisted `snapshot` (+ resume `event`) streams that run's events from its
1546
+ * own `run.start`, exactly as `runAgent` would run it.
1547
+ * 6. **Early termination does not cancel the run.** Breaking out of a
1548
+ * `for await` (or calling `events.return()`) stops delivery, but the run
1549
+ * keeps going and `result` still settles. Cancelling the run itself is
1550
+ * future work — pass `options.signal` to abort it.
1551
+ * 7. **`events` is single-consumer.** See {@link AgentRun.events}.
1552
+ */
1553
+ function createAgentRun(machine, options) {
1554
+ const buffer = [];
1555
+ const resolvers = [];
1556
+ let closed = false;
1557
+ let stopped = false;
1558
+ const push = (event) => {
1559
+ if (closed || stopped) return;
1560
+ const resolve = resolvers.shift();
1561
+ if (resolve) resolve({
1562
+ value: event,
1563
+ done: false
1564
+ });
1565
+ else buffer.push(event);
1566
+ };
1567
+ const close = () => {
1568
+ if (closed) return;
1569
+ closed = true;
1570
+ while (resolvers.length > 0) resolvers.shift()({
1571
+ value: void 0,
1572
+ done: true
1573
+ });
1574
+ };
1575
+ const userOnTrace = options.onTrace;
1576
+ const result = runAgent(machine, {
1577
+ ...options,
1578
+ onTrace: (event) => {
1579
+ push(event);
1580
+ userOnTrace?.(event);
1581
+ if (event.type === "run.end") close();
1582
+ }
1583
+ });
1584
+ result.then(close, close);
1585
+ return {
1586
+ events: {
1587
+ next() {
1588
+ if (buffer.length > 0) return Promise.resolve({
1589
+ value: buffer.shift(),
1590
+ done: false
1591
+ });
1592
+ if (closed || stopped) return Promise.resolve({
1593
+ value: void 0,
1594
+ done: true
1595
+ });
1596
+ return new Promise((resolve) => {
1597
+ resolvers.push(resolve);
1598
+ });
1599
+ },
1600
+ return() {
1601
+ stopped = true;
1602
+ buffer.length = 0;
1603
+ while (resolvers.length > 0) resolvers.shift()({
1604
+ value: void 0,
1605
+ done: true
1606
+ });
1607
+ return Promise.resolve({
1608
+ value: void 0,
1609
+ done: true
1610
+ });
1611
+ },
1612
+ [Symbol.asyncIterator]() {
1613
+ return this;
1614
+ }
1615
+ },
1616
+ result
1617
+ };
1618
+ }
1619
+ //#endregion
1754
1620
  //#region src/provide-executors.ts
1755
1621
  /**
1756
1622
  * Binds a machine's agent actor sources to a set of host `executors` in one
@@ -1765,8 +1631,8 @@ function collectPendingUserInputs(snapshot) {
1765
1631
  *
1766
1632
  * behaves like a normal XState actor whose agent invokes now reach real models.
1767
1633
  *
1768
- * Binding pass over `machine.implementations.actorSources` (after merging
1769
- * `options.actorSources`):
1634
+ * Binding pass over `machine.sources.actors` (after merging
1635
+ * `options.actors`):
1770
1636
  * - `mode: 'generate'` text source → `executors.generateText`
1771
1637
  * - `mode: 'stream'` text source → `executors.streamText`
1772
1638
  * - decision / `agent.decide` source → `executors.decide` (snapshot-driven
@@ -1780,7 +1646,7 @@ function collectPendingUserInputs(snapshot) {
1780
1646
  *
1781
1647
  * A source that already carries its own executor (`.withExecutor(...)`) is left
1782
1648
  * as-is. `agent.userInput` is left UNBOUND — an uncontrolled host handles idle
1783
- * itself, so supply a handler via `options.actorSources` if the machine uses it.
1649
+ * itself, so supply a handler via `options.actors` if the machine uses it.
1784
1650
  * Non-agent actors are untouched.
1785
1651
  *
1786
1652
  * Throws at bind time if a source needs an executor kind that `executors` does
@@ -1795,14 +1661,14 @@ function provideExecutors(machine, executors, options = {}) {
1795
1661
  onChunk: options.onChunk,
1796
1662
  onTrace: options.onTrace
1797
1663
  };
1798
- const provided = options.actorSources ? machine.provide({ actorSources: options.actorSources }) : machine;
1799
- const effectiveSources = provided.implementations.actorSources;
1664
+ const provided = options.actors ? machine.provide({ actors: options.actors }) : machine;
1665
+ const effectiveSources = provided.sources.actors;
1800
1666
  const wrappedSources = {};
1801
1667
  const invokedSrcs = getConfiguredInvokeSrcs(provided);
1802
1668
  for (const [key, logic] of Object.entries(effectiveSources)) {
1803
1669
  if (key === "agent.userInput") continue;
1804
1670
  if (require_decision.isDecisionLogic(logic)) {
1805
- if (require_events.executorBoundLogics.has(logic)) continue;
1671
+ if (require_decision.executorBoundLogics.has(logic)) continue;
1806
1672
  if (!executors.decide) {
1807
1673
  if (invokedSrcs.has(key)) throw missingExecutorError(key, "decision", "decide");
1808
1674
  continue;
@@ -1818,8 +1684,8 @@ function provideExecutors(machine, executors, options = {}) {
1818
1684
  wrappedSources[key] = bindPlanForProvide(provided, logic, executors, bindOptions);
1819
1685
  continue;
1820
1686
  }
1821
- if (require_events.isTextLogic(logic)) {
1822
- if (require_events.executorBoundLogics.has(logic)) continue;
1687
+ if (require_decision.isTextLogic(logic)) {
1688
+ if (require_decision.executorBoundLogics.has(logic)) continue;
1823
1689
  if (!(logic.mode === "stream" ? executors.streamText : executors.generateText)) {
1824
1690
  if (invokedSrcs.has(key)) throw logic.mode === "stream" ? missingExecutorError(key, "streaming text", "streamText") : missingExecutorError(key, "text", "generateText");
1825
1691
  continue;
@@ -1828,7 +1694,7 @@ function provideExecutors(machine, executors, options = {}) {
1828
1694
  continue;
1829
1695
  }
1830
1696
  }
1831
- return provided.provide({ actorSources: wrappedSources });
1697
+ return provided.provide({ actors: wrappedSources });
1832
1698
  }
1833
1699
  function missingExecutorError(src, kind, executor) {
1834
1700
  return /* @__PURE__ */ new Error(`provideExecutors: actor source '${src}' is a ${kind} source but no '${executor}' executor was provided. Add it to the executors object, or bind the source with its own executor (logic.withExecutor(...)) before calling provideExecutors.`);
@@ -1884,8 +1750,15 @@ function collectTransitionTargets(value, fromNode, index, out) {
1884
1750
  return;
1885
1751
  }
1886
1752
  if (typeof value === "object") {
1887
- const target = value.target;
1888
- if (target !== void 0) collectTransitionTargets(target, fromNode, index, out);
1753
+ const { target, to } = value;
1754
+ if (target !== void 0) {
1755
+ collectTransitionTargets(target, fromNode, index, out);
1756
+ return;
1757
+ }
1758
+ if (to !== void 0) {
1759
+ collectTransitionTargets(to, fromNode, index, out);
1760
+ return;
1761
+ }
1889
1762
  }
1890
1763
  }
1891
1764
  function resolveTargetString(target, fromNode, index, out) {
@@ -1897,12 +1770,16 @@ function resolveTargetString(target, fromNode, index, out) {
1897
1770
  if (index.has(resolved)) out.targets.push(resolved);
1898
1771
  else out.opaque = true;
1899
1772
  }
1900
- function outgoingTargets(node, index) {
1773
+ function outgoingTargets(node, index, staticTargets) {
1901
1774
  const out = {
1902
1775
  targets: [],
1903
1776
  opaque: false
1904
1777
  };
1905
1778
  const { config } = node;
1779
+ if (staticTargets) {
1780
+ for (const target of staticTargets[node.path] ?? []) resolveTargetString(target, node, index, out);
1781
+ return out;
1782
+ }
1906
1783
  for (const value of Object.values(config.on ?? {})) collectTransitionTargets(value, node, index, out);
1907
1784
  collectTransitionTargets(config.always, node, index, out);
1908
1785
  collectTransitionTargets(config.choice, node, index, out);
@@ -1914,7 +1791,7 @@ function outgoingTargets(node, index) {
1914
1791
  }
1915
1792
  return out;
1916
1793
  }
1917
- function computeReachable(rootConfig, index) {
1794
+ function computeReachable(rootConfig, index, staticTargets) {
1918
1795
  const reachable = /* @__PURE__ */ new Set();
1919
1796
  const queue = [];
1920
1797
  const markAncestors = (path) => {
@@ -1939,7 +1816,7 @@ function computeReachable(rootConfig, index) {
1939
1816
  while (queue.length > 0) {
1940
1817
  const node = index.get(queue.shift());
1941
1818
  if (!node) continue;
1942
- const { targets, opaque } = outgoingTargets(node, index);
1819
+ const { targets, opaque } = outgoingTargets(node, index, staticTargets);
1943
1820
  for (const target of targets) enter(target);
1944
1821
  if (opaque) for (const sibling of childrenOf(index, node.parentPath)) enter(sibling.path);
1945
1822
  }
@@ -1959,11 +1836,11 @@ function ancestorChain(node, index) {
1959
1836
  function hasNonEmptyOn(config) {
1960
1837
  return !!config.on && Object.keys(config.on).length > 0;
1961
1838
  }
1962
- function decisionKindOf(src, actorSources) {
1839
+ function decisionKindOf(src, actors) {
1963
1840
  if (typeof src === "string") {
1964
1841
  if (src === DECIDE_SRC) return "decision";
1965
1842
  if (src === PLAN_SRC) return "plan";
1966
- const logic = actorSources[src];
1843
+ const logic = actors[src];
1967
1844
  if (require_decision.isDecisionLogic(logic)) return "decision";
1968
1845
  if (require_decision.isPlanLogic(logic)) return "plan";
1969
1846
  return;
@@ -1972,12 +1849,12 @@ function decisionKindOf(src, actorSources) {
1972
1849
  if (require_decision.isPlanLogic(src)) return "plan";
1973
1850
  }
1974
1851
  function isAgentLogicNeedingBinding(src) {
1975
- return (require_events.isTextLogic(src) || require_decision.isDecisionLogic(src) || require_decision.isPlanLogic(src)) && !require_events.executorBoundLogics.has(src);
1852
+ return (require_decision.isTextLogic(src) || require_decision.isDecisionLogic(src) || require_decision.isPlanLogic(src)) && !require_decision.executorBoundLogics.has(src);
1976
1853
  }
1977
1854
  function schemaExposesJson(schema) {
1978
1855
  if (!schema) return false;
1979
1856
  try {
1980
- return require_events.getJsonSchemaSync(schema) !== void 0;
1857
+ return require_decision.getJsonSchemaSync(schema) !== void 0;
1981
1858
  } catch {
1982
1859
  return false;
1983
1860
  }
@@ -1986,7 +1863,7 @@ function isDeclaredOutputSchema(schema) {
1986
1863
  if (!schema) return false;
1987
1864
  let json;
1988
1865
  try {
1989
- json = require_events.getJsonSchemaSync(schema);
1866
+ json = require_decision.getJsonSchemaSync(schema);
1990
1867
  } catch {
1991
1868
  return false;
1992
1869
  }
@@ -2008,7 +1885,7 @@ function checkUnreachableStates(ctx) {
2008
1885
  function checkDecideWithoutEvents(ctx) {
2009
1886
  const out = [];
2010
1887
  for (const node of ctx.index.values()) for (const invoke of node.invokes) {
2011
- const kind = decisionKindOf(invoke.src, ctx.actorSources);
1888
+ const kind = decisionKindOf(invoke.src, ctx.actors);
2012
1889
  if (!kind) continue;
2013
1890
  const selfHandles = hasNonEmptyOn(node.config);
2014
1891
  const ancestorHandles = ancestorChain(node, ctx.index).some((ancestor) => hasNonEmptyOn(ancestor.config));
@@ -2045,7 +1922,7 @@ function checkDirectObjectSrc(ctx) {
2045
1922
  code: "direct-object-src",
2046
1923
  severity: "warning",
2047
1924
  path: node.path,
2048
- message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by runAgent, so they inherit no host executors — call '.withExecutor(...)' on the logic, or register it as a string-keyed actor source (machine.provide({ actorSources: { name: logic } })) and invoke it by name.`
1925
+ message: `State '${node.path}' invokes a direct-object agent logic. Direct-object invoke srcs cannot be rebound by runAgent, so they inherit no host executors — call '.withExecutor(...)' on the logic, or register it as a string-keyed actor source (machine.provide({ actors: { name: logic } })) and invoke it by name.`
2049
1926
  });
2050
1927
  }
2051
1928
  return out;
@@ -2089,8 +1966,8 @@ function isBuiltinOrWildcardEvent(eventType) {
2089
1966
  }
2090
1967
  function checkUndeclaredEvents(ctx) {
2091
1968
  const declared = ctx.schemas?.events;
2092
- if (!declared || Object.keys(declared).length === 0) return [];
2093
- const declaredTypes = new Set(Object.keys(declared));
1969
+ const declaredTypes = new Set(Object.keys(declared ?? {}));
1970
+ if ([...declaredTypes].every((type) => type.startsWith("@agent."))) return [];
2094
1971
  const out = [];
2095
1972
  for (const node of ctx.index.values()) for (const eventType of Object.keys(node.config.on ?? {})) {
2096
1973
  if (declaredTypes.has(eventType) || isBuiltinOrWildcardEvent(eventType)) continue;
@@ -2142,15 +2019,15 @@ const LINT_CHECKS = [
2142
2019
  function lintAgentMachine(machine, options = {}) {
2143
2020
  const config = machine.config ?? {};
2144
2021
  const index = buildStateIndex(config);
2145
- const reachable = computeReachable(config, index);
2146
- const registered = require_events.getRegisteredAgentExecutionOptions(machine);
2022
+ const reachable = computeReachable(config, index, require_decision.getMachineStaticTransitionTargets(machine));
2023
+ const registered = require_decision.getRegisteredAgentExecutionOptions(machine);
2147
2024
  const ctx = {
2148
2025
  machine,
2149
2026
  config,
2150
2027
  index,
2151
2028
  reachable,
2152
2029
  schemas: registered.schemas,
2153
- actorSources: registered.actorSources ?? machine.implementations?.actorSources ?? {}
2030
+ actors: registered.actors ?? machine.sources?.actors ?? {}
2154
2031
  };
2155
2032
  const disabled = new Set(options.disable ?? []);
2156
2033
  return LINT_CHECKS.flatMap((check) => check(ctx)).filter((d) => !disabled.has(d.code));
@@ -2160,11 +2037,11 @@ function lintAgentMachine(machine, options = {}) {
2160
2037
  * `diagnostics` holds the findings; the message lists them one per finding,
2161
2038
  * so a test runner's failure output reads like the CLI's lint report.
2162
2039
  */
2163
- var AgentLintError = class extends Error {
2040
+ var AgentLintError = class extends require_errors.AgentError {
2164
2041
  diagnostics;
2165
2042
  constructor(machineId, diagnostics) {
2166
2043
  const lines = diagnostics.map((d) => ` ${d.severity === "error" ? "error" : "warn "} ${d.code} ${d.path}\n ${d.message}`);
2167
- super(`Agent machine '${machineId}' failed lint (${diagnostics.length} finding(s)):\n${lines.join("\n")}`);
2044
+ super("lint-failed", `Agent machine '${machineId}' failed lint (${diagnostics.length} finding(s)):\n${lines.join("\n")}`);
2168
2045
  this.name = "AgentLintError";
2169
2046
  this.diagnostics = diagnostics;
2170
2047
  }
@@ -2190,7 +2067,7 @@ function assertAgentMachine(machine, options = {}) {
2190
2067
  function pendingInvokes(step) {
2191
2068
  const out = [];
2192
2069
  for (const action of step.actions) {
2193
- const metadata = require_steps.getInvokeEffectMetadata(action);
2070
+ const metadata = require_setup_agent.getInvokeEffectMetadata(action);
2194
2071
  if (typeof metadata?.src === "string" && typeof metadata.id === "string") out.push({
2195
2072
  id: metadata.id,
2196
2073
  src: metadata.src
@@ -2232,7 +2109,7 @@ async function simulateAgent(machine, options) {
2232
2109
  decisions: mapValues(options.script.decisions ?? {}, (arr) => [...arr]),
2233
2110
  invokes: mapValues(options.script.invokes ?? {}, (arr) => [...arr])
2234
2111
  };
2235
- let step = require_steps.initialAgentStep(machine, options.input);
2112
+ let step = require_setup_agent.initialAgentStep(machine, options.input);
2236
2113
  const trail = [];
2237
2114
  for (let i = 0; i < maxSteps; i++) {
2238
2115
  if (step.done) return {
@@ -2246,7 +2123,7 @@ async function simulateAgent(machine, options) {
2246
2123
  const decisionSrc = new Map(pendingInvokes(step).map((invoke) => [invoke.id, invoke.src])).get(request.id) ?? request.id;
2247
2124
  const taken = takeFromQueue(script.decisions, decisionSrc);
2248
2125
  if (!taken.found) throw scriptDryError("decision", decisionSrc, request.id, request);
2249
- step = require_steps.transitionAgentStep(machine, step, taken.value);
2126
+ step = require_setup_agent.transitionAgentStep(machine, step, taken.value);
2250
2127
  trail.push({
2251
2128
  state: step.snapshot.value,
2252
2129
  appliedEvent: taken.value
@@ -2257,7 +2134,7 @@ async function simulateAgent(machine, options) {
2257
2134
  const taken = takeFromQueue(script.decisions, request.src);
2258
2135
  if (!taken.found) throw scriptDryError("decision", request.src, request.id, request);
2259
2136
  const event = taken.value;
2260
- step = await require_steps.resolveAgentRequests(machine, step, { decide: async () => ({ event }) });
2137
+ step = await require_setup_agent.resolveAgentRequests(machine, step, { decide: async () => ({ event }) });
2261
2138
  trail.push({
2262
2139
  state: step.snapshot.value,
2263
2140
  appliedEvent: taken.value
@@ -2266,7 +2143,7 @@ async function simulateAgent(machine, options) {
2266
2143
  }
2267
2144
  const taken = takeFromQueue(script.text, request.src);
2268
2145
  if (!taken.found) throw scriptDryError("text", request.src, request.id);
2269
- step = require_steps.resolveAgentStep(machine, step, request, taken.value);
2146
+ step = require_setup_agent.resolveAgentStep(machine, step, request, taken.value);
2270
2147
  trail.push({
2271
2148
  state: step.snapshot.value,
2272
2149
  resolvedRequest: {
@@ -2281,7 +2158,7 @@ async function simulateAgent(machine, options) {
2281
2158
  if (invoke) {
2282
2159
  const taken = takeFromQueue(script.invokes, invoke.src);
2283
2160
  if (!taken.found) throw scriptDryError("userInput", invoke.src, invoke.id);
2284
- step = require_steps.resolveAgentStep(machine, step, invoke.id, taken.value);
2161
+ step = require_setup_agent.resolveAgentStep(machine, step, invoke.id, taken.value);
2285
2162
  trail.push({
2286
2163
  state: step.snapshot.value,
2287
2164
  resolvedRequest: {
@@ -2330,7 +2207,7 @@ async function explore(machine, options, stopWhen) {
2330
2207
  reachedValues.push(snapshot.value);
2331
2208
  }
2332
2209
  };
2333
- const initial = require_steps.initialAgentStep(machine, options.input);
2210
+ const initial = require_setup_agent.initialAgentStep(machine, options.input);
2334
2211
  recordState(initial.snapshot);
2335
2212
  if (stopWhen?.(initial.snapshot)) witness = [];
2336
2213
  const advance = (step) => {
@@ -2343,7 +2220,7 @@ async function explore(machine, options, stopWhen) {
2343
2220
  step: current,
2344
2221
  blockedSrc: request.src
2345
2222
  };
2346
- current = require_steps.resolveAgentStep(machine, current, request, textOutputs[request.src]);
2223
+ current = require_setup_agent.resolveAgentStep(machine, current, request, textOutputs[request.src]);
2347
2224
  recordState(current.snapshot);
2348
2225
  continue;
2349
2226
  }
@@ -2354,7 +2231,7 @@ async function explore(machine, options, stopWhen) {
2354
2231
  step: current,
2355
2232
  blockedSrc: invoke.src
2356
2233
  };
2357
- current = require_steps.resolveAgentStep(machine, current, invoke.id, textOutputs[invoke.src]);
2234
+ current = require_setup_agent.resolveAgentStep(machine, current, invoke.id, textOutputs[invoke.src]);
2358
2235
  recordState(current.snapshot);
2359
2236
  continue;
2360
2237
  }
@@ -2395,7 +2272,7 @@ async function explore(machine, options, stopWhen) {
2395
2272
  }
2396
2273
  const request = settled.requests[0];
2397
2274
  const isPlan = request?.kind === "plan";
2398
- const branchEvents = request?.kind === "decision" || request?.kind === "plan" ? request.events.map((descriptor) => ({ type: descriptor.type })) : require_events.getAcceptedEvents(settled.snapshot).map((descriptor) => ({ type: descriptor.type }));
2275
+ const branchEvents = request?.kind === "decision" || request?.kind === "plan" ? request.events.map((descriptor) => ({ type: descriptor.type })) : require_decision.getAcceptedEvents(settled.snapshot).map((descriptor) => ({ type: descriptor.type }));
2399
2276
  if (branchEvents.length === 0) {
2400
2277
  pathsExplored++;
2401
2278
  terminals.push({
@@ -2424,7 +2301,7 @@ async function explore(machine, options, stopWhen) {
2424
2301
  prunedByGuard++;
2425
2302
  continue;
2426
2303
  }
2427
- const next = isPlan ? await require_steps.resolveAgentRequests(machine, settled, { decide: async () => ({ event }) }) : require_steps.transitionAgentStep(machine, settled, event);
2304
+ const next = isPlan ? await require_setup_agent.resolveAgentRequests(machine, settled, { decide: async () => ({ event }) }) : require_setup_agent.transitionAgentStep(machine, settled, event);
2428
2305
  recordState(next.snapshot);
2429
2306
  await visit(next, [...path, event], depth + 1);
2430
2307
  }
@@ -2491,38 +2368,506 @@ async function canReach(machine, statePath, options = {}) {
2491
2368
  } : { canReach: false };
2492
2369
  }
2493
2370
  //#endregion
2371
+ //#region src/trajectory.ts
2372
+ function isRecord$2(value) {
2373
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2374
+ }
2375
+ /**
2376
+ * Classifies one item. Event-shaped things (log entries, events) become events;
2377
+ * everything else is a state value. A snapshot is unwrapped to its `value`.
2378
+ * @internal
2379
+ */
2380
+ function normalize(item) {
2381
+ if (isRecord$2(item)) {
2382
+ const nested = item["event"];
2383
+ if (isRecord$2(nested) && typeof nested["type"] === "string") return {
2384
+ kind: "event",
2385
+ event: nested
2386
+ };
2387
+ if (typeof item["type"] === "string") return {
2388
+ kind: "event",
2389
+ event: item
2390
+ };
2391
+ if ("value" in item && "status" in item) return {
2392
+ kind: "state",
2393
+ value: item["value"]
2394
+ };
2395
+ }
2396
+ return {
2397
+ kind: "state",
2398
+ value: item
2399
+ };
2400
+ }
2401
+ /** Every leaf of a state value as a dot path (`{ review: 'editing' }` → `review.editing`). @internal */
2402
+ function statePaths(value) {
2403
+ if (typeof value === "string") return [value];
2404
+ if (!isRecord$2(value)) return [];
2405
+ return Object.entries(value).flatMap(([key, child]) => statePaths(child).map((path) => `${key}.${path}`));
2406
+ }
2407
+ /**
2408
+ * A string expectation matches a state value when it names one of its leaves or
2409
+ * an ancestor of one, so `'review'` matches `{ review: 'editing' }`. @internal
2410
+ */
2411
+ function stateMatchesPath(path, value) {
2412
+ return statePaths(value).some((leaf) => leaf === path || leaf.startsWith(`${path}.`));
2413
+ }
2414
+ function deepEqual(a, b) {
2415
+ if (Object.is(a, b)) return true;
2416
+ if (Array.isArray(a) || Array.isArray(b)) return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => deepEqual(item, b[index]));
2417
+ if (!isRecord$2(a) || !isRecord$2(b)) return false;
2418
+ const keys = Object.keys(a);
2419
+ return keys.length === Object.keys(b).length && keys.every((key) => deepEqual(a[key], b[key]));
2420
+ }
2421
+ /**
2422
+ * Does one actual item satisfy one expected item?
2423
+ *
2424
+ * - A string expectation matches an event's `type`, or a state value it names.
2425
+ * - An event expectation matches an event with the same `type` whose other
2426
+ * declared keys deep-equal, so payload details are opt-in.
2427
+ * - A state-value expectation matches structurally.
2428
+ * @internal
2429
+ */
2430
+ function matchesItem(expected, actual) {
2431
+ const actualItem = normalize(actual);
2432
+ if (typeof expected === "string") return actualItem.kind === "event" ? actualItem.event.type === expected : stateMatchesPath(expected, actualItem.value);
2433
+ const expectedItem = normalize(expected);
2434
+ if (expectedItem.kind !== actualItem.kind) return false;
2435
+ if (expectedItem.kind === "event" && actualItem.kind === "event") return Object.keys(expectedItem.event).every((key) => deepEqual(expectedItem.event[key], actualItem.event[key]));
2436
+ return deepEqual(expectedItem.value, actualItem.value);
2437
+ }
2438
+ /**
2439
+ * Matches a run's trajectory against an expected one.
2440
+ *
2441
+ * Both trajectories may be state values (collected from `onTransition`) or
2442
+ * events (`result.events`, whose {@link AgentLogEntry} envelopes are unwrapped);
2443
+ * items are compared by shape, so `['prompting', 'drafting']` scores a state
2444
+ * path and `['PROMPT_SUBMITTED', 'SEND']` scores an event log with the same
2445
+ * call.
2446
+ *
2447
+ * Semantics are an ordered subsequence: every expected item must appear in
2448
+ * `actual`, in order, with gaps allowed. Pass `{ exact: true }` to require
2449
+ * equality instead.
2450
+ *
2451
+ * @example Trajectory assertions in a test
2452
+ * ```ts
2453
+ * const statePath: unknown[] = [];
2454
+ * const result = await runAgent(machine, {
2455
+ * input,
2456
+ * executors,
2457
+ * onTransition: (snapshot) => statePath.push(snapshot.value),
2458
+ * });
2459
+ *
2460
+ * const path = matchesTrajectory(statePath, ['prompting', 'drafting', 'sent']);
2461
+ * expect(path.matched, JSON.stringify(path.firstMiss)).toBe(true);
2462
+ * expect(matchesTrajectory(result.events, ['PROMPT_SUBMITTED', 'SEND']).matched).toBe(true);
2463
+ * ```
2464
+ *
2465
+ * @example Partial credit in an eval scorer
2466
+ * ```ts
2467
+ * const match = matchesTrajectory(output.statePath, expected.statePath);
2468
+ * return { name: 'state_path', score: match.score, metadata: { ...match.firstMiss } };
2469
+ * ```
2470
+ */
2471
+ function matchesTrajectory(actual, expected, options = {}) {
2472
+ const expectedCount = expected.length;
2473
+ if (options.exact) {
2474
+ let matchedCount = 0;
2475
+ let firstMiss;
2476
+ for (let index = 0; index < expectedCount; index++) {
2477
+ if (index < actual.length && matchesItem(expected[index], actual[index])) {
2478
+ matchedCount++;
2479
+ continue;
2480
+ }
2481
+ firstMiss = {
2482
+ index,
2483
+ expected: expected[index],
2484
+ searchedFrom: index
2485
+ };
2486
+ break;
2487
+ }
2488
+ const matched = !firstMiss && actual.length === expectedCount;
2489
+ const denominator = Math.max(expectedCount, actual.length);
2490
+ return {
2491
+ matched,
2492
+ matchedCount,
2493
+ expectedCount,
2494
+ score: matched ? 1 : denominator === 0 ? 1 : matchedCount / denominator,
2495
+ ...firstMiss ? { firstMiss } : {}
2496
+ };
2497
+ }
2498
+ let cursor = 0;
2499
+ let matchedCount = 0;
2500
+ for (let index = 0; index < expectedCount; index++) {
2501
+ const want = expected[index];
2502
+ let found = -1;
2503
+ for (let at = cursor; at < actual.length; at++) if (matchesItem(want, actual[at])) {
2504
+ found = at;
2505
+ break;
2506
+ }
2507
+ if (found === -1) return {
2508
+ matched: false,
2509
+ matchedCount,
2510
+ expectedCount,
2511
+ score: matchedCount / expectedCount,
2512
+ firstMiss: {
2513
+ index,
2514
+ expected: want,
2515
+ searchedFrom: cursor
2516
+ }
2517
+ };
2518
+ matchedCount++;
2519
+ cursor = found + 1;
2520
+ }
2521
+ return {
2522
+ matched: true,
2523
+ matchedCount,
2524
+ expectedCount,
2525
+ score: 1
2526
+ };
2527
+ }
2528
+ //#endregion
2529
+ //#region src/scripted-executors.ts
2530
+ /**
2531
+ * Scripted executors — a keyless, deterministic stand-in for a model host.
2532
+ *
2533
+ * `createScriptedExecutors` builds a full `{ generateText, streamText, decide }`
2534
+ * set that plays back canned answers from FIFO queues, so `runAgent` (or
2535
+ * `provideExecutors`, or a bare `TextLogic.execute`) runs with no API key and no
2536
+ * network. It is the fastest way to see a machine run, and the least ceremonial
2537
+ * way to test one: same machine, same executor contract, scripted answers.
2538
+ */
2539
+ /** Thrown when a scripted queue runs dry on a pending request. */
2540
+ var ScriptedExecutorsError = class extends require_errors.AgentError {
2541
+ constructor(message) {
2542
+ super("scripted-executors-exhausted", message);
2543
+ this.name = "ScriptedExecutorsError";
2544
+ }
2545
+ };
2546
+ function isRecord$1(value) {
2547
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2548
+ }
2549
+ /** The only own keys an executor-result envelope carries. @internal */
2550
+ const TEXT_ENVELOPE_KEYS = new Set([
2551
+ "output",
2552
+ "usage",
2553
+ "raw"
2554
+ ]);
2555
+ /**
2556
+ * True when a scripted entry is the executor envelope rather than the output
2557
+ * value: it owns an `output` key and owns NOTHING outside the envelope's own
2558
+ * vocabulary. Bare `'output' in value` would swallow an output object's
2559
+ * siblings (`{ output: 'draft', confidence: 0.9 }` would lose `confidence`)
2560
+ * and would also match an inherited `output`. @internal
2561
+ */
2562
+ function isTextEnvelope(value) {
2563
+ return Object.hasOwn(value, "output") && Object.keys(value).every((key) => TEXT_ENVELOPE_KEYS.has(key));
2564
+ }
2565
+ /**
2566
+ * Resolves ONE scripted text entry to an executor result: a function entry is
2567
+ * called with the request, and the value is taken as the raw envelope only when
2568
+ * it is one (see {@link isTextEnvelope}). Shared with `runSeam`, whose routed
2569
+ * queues follow the same entry conventions. @internal
2570
+ */
2571
+ async function resolveScriptedTextEntry(entry, request, info) {
2572
+ const value = typeof entry === "function" ? await entry(request, info) : entry;
2573
+ return isRecord$1(value) && isTextEnvelope(value) ? value : { output: value };
2574
+ }
2575
+ /**
2576
+ * Keyless executors that replay a script instead of calling a model. Every
2577
+ * slot is provided, so any machine binds; a request with no entry left throws a
2578
+ * descriptive error naming what was pending.
2579
+ *
2580
+ * Queues are consumed FIFO and are copied on creation, so the caller's arrays
2581
+ * are never mutated and each call builds a fresh, independent playthrough.
2582
+ * Entries may be plain values or functions of the request, which is how one
2583
+ * script serves a machine that loops or branches: route on `request.name` (the
2584
+ * `setupAgent({ requests })` key) or on the decision's candidate `events`.
2585
+ *
2586
+ * @example
2587
+ * ```ts
2588
+ * const result = await runAgent(moderationMachine, {
2589
+ * input: { comment: 'honestly this update is terrible', trust: 20 },
2590
+ * executors: createScriptedExecutors({
2591
+ * decisions: [{ type: 'FLAG', reason: 'Borderline tone.' }],
2592
+ * }),
2593
+ * });
2594
+ * ```
2595
+ *
2596
+ * @example Dynamic entries
2597
+ * ```ts
2598
+ * createScriptedExecutors({
2599
+ * text: [(request) => `Draft about ${request.prompt}`],
2600
+ * decisions: [(request) => ({ type: request.events[0]!.type })],
2601
+ * });
2602
+ * ```
2603
+ */
2604
+ function createScriptedExecutors(script = {}) {
2605
+ const decisions = [...script.decisions ?? []];
2606
+ const text = [...script.text ?? []];
2607
+ const nextText = async (request, info) => {
2608
+ if (text.length === 0) throw new ScriptedExecutorsError(`createScriptedExecutors: script ran dry on a pending text request ${describeText(request)}. Add another entry to the script's \`text\` queue.`);
2609
+ return resolveScriptedTextEntry(text.shift(), request, info);
2610
+ };
2611
+ return {
2612
+ generateText: nextText,
2613
+ streamText: async (request, info) => {
2614
+ const result = await nextText(request, info);
2615
+ if (typeof result.output === "string") info?.onChunk?.(result.output);
2616
+ return result;
2617
+ },
2618
+ decide: async (request) => {
2619
+ if (decisions.length === 0) throw new ScriptedExecutorsError(`createScriptedExecutors: script ran dry on a pending decision request (id '${request.id}'). Add another entry to the script's \`decisions\` queue. Candidate events: ${request.events.map((event) => event.type).join(", ") || "(none)"}.`);
2620
+ const entry = decisions.shift();
2621
+ const value = typeof entry === "function" ? await entry(request) : entry;
2622
+ return typeof value.type === "string" ? { event: value } : value;
2623
+ }
2624
+ };
2625
+ }
2626
+ function describeText(request) {
2627
+ return request.name ? `'${request.name}'` : `(model '${request.model}')`;
2628
+ }
2629
+ //#endregion
2630
+ //#region src/seam.ts
2631
+ /** Thrown when the call plan runs dry on a request that is not the live seam. */
2632
+ var SeamScriptError = class extends require_errors.AgentError {
2633
+ constructor(message) {
2634
+ super("seam-script-exhausted", message);
2635
+ this.name = "SeamScriptError";
2636
+ }
2637
+ };
2638
+ function seamKeyOf(seam) {
2639
+ return seam.request ?? seam.model;
2640
+ }
2641
+ function describeRequest(request) {
2642
+ return request.name ? `'${request.name}' (model '${request.model}')` : `(model '${request.model}')`;
2643
+ }
2644
+ function isRecord(value) {
2645
+ return typeof value === "object" && value !== null;
2646
+ }
2647
+ /**
2648
+ * The seam's own answer, for scoring. Our `{ output }` envelope is read
2649
+ * directly; a raw AI SDK `generateText` result contributes its `text`. A raw
2650
+ * STREAM result is left alone — the machine consumes that stream, and reading
2651
+ * it here would steal the chunks — so `seamOutput` is `undefined` for a
2652
+ * streaming candidate that returns one. Score its trajectory instead.
2653
+ * @internal
2654
+ */
2655
+ async function seamOutputOf(result, request) {
2656
+ if (!isRecord(result)) return;
2657
+ if ("output" in result) return await result["output"];
2658
+ if ("textStream" in result) return;
2659
+ return await require_decision.normalizeGeneratorResult(result, `seam '${request.name ?? request.model}'`, { request });
2660
+ }
2661
+ /**
2662
+ * Runs a machine end to end with one model call under test, and slices the run
2663
+ * at that call.
2664
+ *
2665
+ * The `before`/`after` slices are the point: `after.statePath` is the branch
2666
+ * the seam chose and `after.events` is the same question against the durable
2667
+ * log, both ready for `matchesTrajectory`. The state slice splits where the
2668
+ * state path stood when the seam answered; the event slice splits at the
2669
+ * seam's own effect completion (the first `xstate.done.*`/`xstate.error.*`
2670
+ * entry appended after the call was made).
2671
+ *
2672
+ * @example Keyless: the seam is scripted too, so the whole thing runs offline.
2673
+ * ```ts
2674
+ * const run = await runSeam(emailDrafter, {
2675
+ * scripts: { promptEvaluator: [vague, complete], emailDrafter: [draft] },
2676
+ * seam: { request: 'evaluatePrompt' },
2677
+ * respond: ({ state }) => (state === 'prompting' ? { type: 'PROMPT_SUBMITTED', prompt } : null),
2678
+ * });
2679
+ *
2680
+ * matchesTrajectory(run.after.statePath, ['needsMoreInfo', 'drafting']);
2681
+ * ```
2682
+ *
2683
+ * @example A candidate prompt at the seam, scored against the same rows.
2684
+ * ```ts
2685
+ * const { generateText } = createAiSdkExecutors({ models });
2686
+ * const run = await runSeam(emailDrafter, { ...row, candidate: generateText });
2687
+ * ```
2688
+ */
2689
+ async function runSeam(machine, options) {
2690
+ const { seam, candidate } = options;
2691
+ const queues = new Map(Object.entries(options.scripts ?? {}).map(([key, answers]) => [key, [...answers]]));
2692
+ const statePath = [];
2693
+ let calls = 0;
2694
+ let seamMatches = 0;
2695
+ let seamOutput;
2696
+ let seamReached = false;
2697
+ let callsBeforeSeam = -1;
2698
+ let seamStateAt = 0;
2699
+ let seamEventAt = 0;
2700
+ let liveEvents = 0;
2701
+ const scriptedAnswer = async (request, info, optional) => {
2702
+ const key = request.name !== void 0 && queues.has(request.name) ? request.name : request.model;
2703
+ const queue = queues.get(key);
2704
+ if (!queue?.length) {
2705
+ if (optional) return;
2706
+ throw new SeamScriptError(`runSeam: no scripted answer left for request ${describeRequest(request)}. Add an entry to \`scripts.${key}\` — its last entry repeats, so one extra answer covers a longer branch.`);
2707
+ }
2708
+ return resolveScriptedTextEntry(queue.length === 1 ? queue[0] : queue.shift(), request, info);
2709
+ };
2710
+ const route = async (request, info) => {
2711
+ const callIndex = calls++;
2712
+ const isSeam = (seam.request !== void 0 ? request.name === seam.request : request.model === seam.model) && seamMatches++ === (seam.occurrence ?? 0);
2713
+ const scripted = await scriptedAnswer(request, info, isSeam && candidate !== void 0);
2714
+ if (!isSeam) return scripted;
2715
+ seamReached = true;
2716
+ callsBeforeSeam = callIndex;
2717
+ seamStateAt = statePath.length;
2718
+ seamEventAt = liveEvents;
2719
+ if (!candidate) {
2720
+ if (!scripted) throw new SeamScriptError(`runSeam: the seam '${seamKeyOf(seam)}' has no candidate and no scripted answer. Pass \`candidate\`, or script the seam's key.`);
2721
+ seamOutput = scripted.output;
2722
+ return scripted;
2723
+ }
2724
+ const result = await candidate(request, info);
2725
+ seamOutput = await seamOutputOf(result, request);
2726
+ return result;
2727
+ };
2728
+ const executors = {
2729
+ ...options.executors,
2730
+ generateText: route,
2731
+ streamText: async (request, info) => {
2732
+ const result = await route(request, info);
2733
+ const output = isRecord(result) ? result["output"] : void 0;
2734
+ if (typeof output === "string") info?.onChunk?.(output);
2735
+ return result;
2736
+ }
2737
+ };
2738
+ let snapshot;
2739
+ let event;
2740
+ let events = [];
2741
+ let result;
2742
+ const maxTurns = options.maxTurns ?? 12;
2743
+ for (let turn = 0; turn <= maxTurns; turn++) {
2744
+ result = await runAgent(machine, {
2745
+ ...snapshot ? { snapshot } : { input: options.input },
2746
+ ...event ? { event } : {},
2747
+ ...options.isSuspended ? { isSuspended: options.isSuspended } : {},
2748
+ ...options.actors ? { actors: options.actors } : {},
2749
+ events,
2750
+ executors,
2751
+ onEvent: () => {
2752
+ liveEvents++;
2753
+ },
2754
+ onTransition: (next, causedBy) => {
2755
+ if (turn > 0 && causedBy.type === "@xstate.init") return;
2756
+ statePath.push(next.value);
2757
+ }
2758
+ });
2759
+ events = result.events;
2760
+ liveEvents = events.length;
2761
+ if (result.status !== "idle" || turn === maxTurns) break;
2762
+ const next = options.respond?.({
2763
+ snapshot: result.snapshot,
2764
+ state: result.snapshot.value,
2765
+ meta: require_decision.getStateMeta(result.snapshot),
2766
+ turn,
2767
+ result
2768
+ });
2769
+ if (!next) break;
2770
+ event = next;
2771
+ snapshot = result.snapshot;
2772
+ }
2773
+ let splitAt = events.length;
2774
+ if (seamReached) {
2775
+ splitAt = seamEventAt;
2776
+ for (let index = seamEventAt; index < events.length; index++) {
2777
+ const type = events[index].event.type;
2778
+ if (type.startsWith("xstate.done") || type.startsWith("xstate.error")) {
2779
+ splitAt = index;
2780
+ break;
2781
+ }
2782
+ }
2783
+ }
2784
+ const splitStateAt = seamReached ? seamStateAt : statePath.length;
2785
+ return {
2786
+ result,
2787
+ seamOutput,
2788
+ callsBeforeSeam,
2789
+ before: {
2790
+ statePath: statePath.slice(0, splitStateAt),
2791
+ events: events.slice(0, splitAt)
2792
+ },
2793
+ after: {
2794
+ statePath: statePath.slice(splitStateAt),
2795
+ events: events.slice(splitAt)
2796
+ }
2797
+ };
2798
+ }
2799
+ //#endregion
2800
+ exports.AGENT_EVENT_SCHEMA_VERSION = require_event_log_store.AGENT_EVENT_SCHEMA_VERSION;
2801
+ exports.AGENT_INIT_EVENT_TYPE = require_setup_agent.AGENT_INIT_EVENT_TYPE;
2494
2802
  exports.AGENT_TRACE_SCHEMA_VERSION = AGENT_TRACE_SCHEMA_VERSION;
2803
+ exports.AGENT_USAGE_EVENT_TYPE = require_setup_agent.AGENT_USAGE_EVENT_TYPE;
2804
+ exports.AgentDecisionExhaustedError = require_decision.AgentDecisionExhaustedError;
2805
+ exports.AgentError = require_errors.AgentError;
2806
+ exports.AgentEventLogConflictError = require_event_log_store.AgentEventLogConflictError;
2495
2807
  exports.AgentIdleError = AgentIdleError;
2808
+ exports.AgentIllegalResumeEventError = AgentIllegalResumeEventError;
2496
2809
  exports.AgentLintError = AgentLintError;
2497
- exports.DecisionExhaustedError = require_decision.DecisionExhaustedError;
2498
- exports.IllegalResumeEventError = IllegalResumeEventError;
2499
- exports.SnapshotVersionMismatchError = SnapshotVersionMismatchError;
2500
- exports.appendMessages = appendMessages;
2810
+ exports.AgentReplayDivergenceError = require_setup_agent.AgentReplayDivergenceError;
2811
+ exports.AgentReplayMachineMismatchError = require_setup_agent.AgentReplayMachineMismatchError;
2812
+ exports.AgentSnapshotVersionMismatchError = AgentSnapshotVersionMismatchError;
2813
+ exports.NonSerializableAgentEventError = require_event_log_store.NonSerializableAgentEventError;
2814
+ exports.PLAN_DONE_EVENT_TYPE = require_decision.PLAN_DONE_EVENT_TYPE;
2815
+ exports.appendMessages = require_setup_agent.appendMessages;
2816
+ exports.assertAgentLogEntry = require_event_log_store.assertAgentLogEntry;
2501
2817
  exports.assertAgentMachine = assertAgentMachine;
2502
- exports.assistantMessage = require_events.assistantMessage;
2818
+ exports.assertEventLogStoreConformance = require_event_log_store.assertEventLogStoreConformance;
2819
+ exports.assertJsonSerializable = require_event_log_store.assertJsonSerializable;
2820
+ exports.assistantMessage = require_decision.assistantMessage;
2821
+ exports.bindRequestExecutor = require_decision.bindRequestExecutor;
2822
+ exports.buildEnvelopeSchema = require_decision.buildEnvelopeSchema;
2503
2823
  exports.canReach = canReach;
2504
- exports.createAgentSchemas = createAgentSchemas;
2505
- exports.createTextLogic = require_events.createTextLogic;
2824
+ exports.createAgentActor = createAgentActor;
2825
+ exports.createAgentRun = createAgentRun;
2826
+ exports.createAgentSchemas = require_setup_agent.createAgentSchemas;
2827
+ exports.createInMemoryEventLogStore = require_event_log_store.createInMemoryEventLogStore;
2828
+ exports.createReplayEntry = require_setup_agent.createReplayEntry;
2829
+ exports.createScriptedExecutors = createScriptedExecutors;
2830
+ exports.createTextLogic = require_decision.createTextLogic;
2831
+ exports.diffEventLogs = require_setup_agent.diffEventLogs;
2832
+ exports.executeAgentRequest = require_setup_agent.executeAgentRequest;
2506
2833
  exports.explorePaths = explorePaths;
2507
- exports.getAcceptedEvents = require_events.getAcceptedEvents;
2508
- exports.getAgentMessages = require_events.getAgentMessages;
2509
- exports.getStateMeta = require_events.getStateMeta;
2834
+ exports.generateResult = generateResult;
2835
+ exports.getAcceptedEvents = require_decision.getAcceptedEvents;
2836
+ exports.getAgentEffects = require_setup_agent.getAgentEffects;
2837
+ exports.getAgentMessages = require_decision.getAgentMessages;
2838
+ exports.getAgentOutputMode = require_decision.getAgentOutputMode;
2839
+ exports.getCallUsage = require_setup_agent.getCallUsage;
2840
+ exports.getJsonSchema = require_decision.getJsonSchema;
2841
+ exports.getJsonSchemaSync = require_decision.getJsonSchemaSync;
2842
+ exports.getMachineStructuralHash = require_decision.getMachineStructuralHash;
2843
+ exports.getStateMeta = require_decision.getStateMeta;
2844
+ exports.initEntry = require_setup_agent.initEntry;
2510
2845
  exports.inspectTransitions = inspectTransitions;
2846
+ exports.isStandardSchema = require_decision.isStandardSchema;
2511
2847
  exports.lintAgentMachine = lintAgentMachine;
2512
- exports.messagesSchema = messagesSchema;
2513
- exports.parseAgentEvent = require_events.parseAgentEvent;
2514
- exports.persistSnapshot = require_events.persistSnapshot;
2848
+ exports.matchesTrajectory = matchesTrajectory;
2849
+ exports.messagesSchema = require_setup_agent.messagesSchema;
2850
+ exports.parseAgentEvent = require_decision.parseAgentEvent;
2851
+ exports.parseModelRef = require_decision.parseModelRef;
2852
+ exports.parseOutput = require_decision.parseOutput;
2853
+ exports.parseStructuredEnvelope = require_decision.parseStructuredEnvelope;
2854
+ exports.persistSnapshot = require_decision.persistSnapshot;
2515
2855
  exports.provideExecutors = provideExecutors;
2856
+ exports.renderDecisionAttempts = require_decision.renderDecisionAttempts;
2857
+ exports.replay = require_setup_agent.replay;
2858
+ exports.resolveDecision = require_decision.resolveDecision;
2516
2859
  exports.runAgent = runAgent;
2517
- exports.runAgentToCompletion = runAgentToCompletion;
2860
+ exports.runSeam = runSeam;
2861
+ exports.serializeTraceEvent = serializeTraceEvent;
2518
2862
  Object.defineProperty(exports, "setupAgent", {
2519
2863
  enumerable: true,
2520
2864
  get: function() {
2521
- return setupAgent;
2865
+ return require_setup_agent.setupAgent;
2522
2866
  }
2523
2867
  });
2524
2868
  exports.simulateAgent = simulateAgent;
2525
- exports.systemMessage = require_events.systemMessage;
2526
- exports.toolMessage = require_events.toolMessage;
2869
+ exports.systemMessage = require_decision.systemMessage;
2870
+ exports.toolMessage = require_decision.toolMessage;
2527
2871
  exports.traceTransitions = traceTransitions;
2528
- exports.userMessage = require_events.userMessage;
2872
+ exports.userMessage = require_decision.userMessage;
2873
+ exports.verifyReplay = require_setup_agent.verifyReplay;