@statelyai/agent 2.0.0-alpha.27 → 2.0.0-alpha.29

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 (75) hide show
  1. package/dist/ai-sdk.cjs +149 -26
  2. package/dist/ai-sdk.d.cts +57 -34
  3. package/dist/ai-sdk.d.mts +57 -34
  4. package/dist/ai-sdk.mjs +147 -23
  5. package/dist/{decision-RQ0iv0oZ.mjs → decision-Bl_rKQnU.mjs} +73 -7
  6. package/dist/{decision-CmM8-HHz.cjs → decision-oevZnkBe.cjs} +112 -29
  7. package/dist/{text-logic-f5F5sCdc.cjs → event-log-Dtb16p1p.cjs} +813 -6
  8. package/dist/{text-logic-CIqHAmBi.mjs → event-log-GPZO9p7_.mjs} +679 -3
  9. package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CN9ygggF.d.cts} +90 -5
  10. package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-DS836wch.d.mts} +90 -5
  11. package/dist/event-log-store-bCdskPNS.cjs +95 -0
  12. package/dist/event-log-store-yalq8C77.mjs +84 -0
  13. package/dist/index.cjs +571 -58
  14. package/dist/index.d.cts +104 -510
  15. package/dist/index.d.mts +104 -510
  16. package/dist/index.mjs +542 -32
  17. package/dist/log.cjs +77 -20
  18. package/dist/log.d.cts +5 -5
  19. package/dist/log.d.mts +5 -5
  20. package/dist/log.mjs +58 -3
  21. package/dist/machines.cjs +1 -1
  22. package/dist/machines.d.cts +15 -15
  23. package/dist/machines.d.mts +15 -15
  24. package/dist/machines.mjs +1 -1
  25. package/dist/model-source-C7FFP2Sj.mjs +39 -0
  26. package/dist/model-source-DjJPjGSP.cjs +44 -0
  27. package/dist/openai.cjs +10 -10
  28. package/dist/openai.d.cts +27 -25
  29. package/dist/openai.d.mts +27 -25
  30. package/dist/openai.mjs +2 -2
  31. package/dist/otel.cjs +5 -4
  32. package/dist/otel.d.cts +5 -7
  33. package/dist/otel.d.mts +5 -7
  34. package/dist/otel.mjs +5 -3
  35. package/dist/{run-agent-COXXc0RW.d.mts → run-agent-C9ubX8Tx.d.cts} +87 -30
  36. package/dist/{run-agent-xNTxpt82.mjs → run-agent-CuQT1xSS.mjs} +170 -42
  37. package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CxSyZmIE.d.mts} +87 -30
  38. package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-cToZ3K42.cjs} +219 -86
  39. package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DTF3NQg3.cjs} +31 -20
  40. package/dist/setup-agent-DlbgwsIJ.d.cts +577 -0
  41. package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-DniLvDu7.mjs} +15 -9
  42. package/dist/setup-agent-tsc3vm5I.d.mts +577 -0
  43. package/dist/tanstack-ai.cjs +627 -0
  44. package/dist/tanstack-ai.d.cts +204 -0
  45. package/dist/tanstack-ai.d.mts +204 -0
  46. package/dist/tanstack-ai.mjs +618 -0
  47. package/dist/testing.cjs +98 -985
  48. package/dist/testing.d.cts +51 -347
  49. package/dist/testing.d.mts +51 -347
  50. package/dist/testing.mjs +86 -973
  51. package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-9fv1i9aK.d.cts} +50 -10
  52. package/dist/{text-logic-D-czVPYp.d.mts → text-logic-BDbGjYMh.d.mts} +50 -10
  53. package/dist/{types-BgNbNKnJ.d.mts → types-Dp5_Ru4R.d.cts} +2 -2
  54. package/dist/{types-hR5f5YXa.d.cts → types-Dp5_Ru4R.d.mts} +2 -2
  55. package/dist/usage-D8UphgEA.d.mts +17 -0
  56. package/dist/usage-R1NTmLxY.d.cts +17 -0
  57. package/dist/{utils-_q7F-AZu.d.mts → utils-BnXdWih3.d.mts} +1 -2
  58. package/dist/{utils-o4YRdHJX.d.cts → utils-ebVpCYoF.d.cts} +1 -2
  59. package/dist/validate.cjs +239 -8
  60. package/dist/validate.d.cts +110 -3
  61. package/dist/validate.d.mts +110 -3
  62. package/dist/validate.mjs +238 -7
  63. package/dist/verify-B5qGC5WN.mjs +1181 -0
  64. package/dist/verify-CBuYIpsa.d.mts +414 -0
  65. package/dist/verify-Cqghu4gz.cjs +1222 -0
  66. package/dist/verify-DYH_2VzU.d.cts +414 -0
  67. package/package.json +44 -31
  68. package/readme.md +23 -5
  69. package/skills/generate-machine/SKILL.md +136 -64
  70. package/skills/improve-machine/SKILL.md +190 -0
  71. package/skills/improve-machine/reference.md +96 -0
  72. package/dist/event-log-store-2pAFiWuc.mjs +0 -576
  73. package/dist/event-log-store-C-COhIXP.cjs +0 -702
  74. package/dist/usage-BdEjFuIi.mjs +0 -5
  75. package/dist/usage-efOQPdf_.cjs +0 -10
package/dist/ai-sdk.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { B as AgentTruncatedError, N as isStandardSchema, c as getAgentOutputMode, h as providerOutputSchema } from "./text-logic-CIqHAmBi.mjs";
2
- import { o as renderDecisionAttempts } from "./decision-RQ0iv0oZ.mjs";
3
- import { NoObjectGeneratedError, Output, generateText, stepCountIs, streamText, tool } from "ai";
1
+ import { R as providerOutputSchema, ct as AgentTruncatedError, j as getAgentOutputMode, tt as isStandardSchema } from "./event-log-GPZO9p7_.mjs";
2
+ import { c as renderDecisionAttempts, m as isRecord, o as decisionOptionIds } from "./decision-Bl_rKQnU.mjs";
3
+ import { t as resolveModelSource } from "./model-source-C7FFP2Sj.mjs";
4
+ import { InvalidResponseDataError, NoObjectGeneratedError, Output, experimental_decide, generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
4
5
  //#region src/ai-sdk/mappers.ts
5
6
  /**
6
7
  * Maps an {@link AgentTools} map onto AI SDK `tool()` definitions. A tool that
@@ -23,7 +24,7 @@ function toAiSdkTools(tools) {
23
24
  })]);
24
25
  continue;
25
26
  }
26
- if (isStandardSchema(descriptor.inputSchema)) {
27
+ if (isStandardSchema(descriptor.inputSchema) || descriptor.type === "provider") {
27
28
  entries.push([name, descriptor]);
28
29
  continue;
29
30
  }
@@ -173,6 +174,53 @@ function toAiSdkEventTools(events) {
173
174
  inputSchema: event.inputSchema ? event.inputSchema : unknownSchema
174
175
  })]));
175
176
  }
177
+ /** Tool name of the single closed-choice tool {@link toAiSdkOptionTool} builds. */
178
+ const OPTION_TOOL_NAME = "choose_option";
179
+ /**
180
+ * A closed choice as ONE AI SDK tool: its input is `{ option }`, an enum of
181
+ * option ids (see core's `decisionOptionIds`), with each id's description in
182
+ * the tool description. Map the answer back with `ids.indexOf(input.option)`.
183
+ */
184
+ function toAiSdkOptionTool(options) {
185
+ const ids = decisionOptionIds(options);
186
+ const lines = options.map((option, index) => option.description ? `- ${ids[index]}: ${option.description}` : `- ${ids[index]}`);
187
+ return {
188
+ ids,
189
+ tools: { [OPTION_TOOL_NAME]: tool({
190
+ description: `Choose exactly one option:\n${lines.join("\n")}`,
191
+ inputSchema: jsonSchema({
192
+ type: "object",
193
+ properties: { option: {
194
+ type: "string",
195
+ enum: ids,
196
+ description: "The id of the chosen option."
197
+ } },
198
+ required: ["option"],
199
+ additionalProperties: false
200
+ })
201
+ }) }
202
+ };
203
+ }
204
+ /**
205
+ * The event a closed-choice tool call names: the offered option whose id is
206
+ * `input.option`. A call to another tool, or an id that is not offered, maps
207
+ * to {@link unofferedOption}, so the choice is retried instead of thrown.
208
+ */
209
+ function fromAiSdkOptionCall(toolCall, options, ids) {
210
+ if (toolCall.toolName !== OPTION_TOOL_NAME) return unofferedOption(`tool '${toolCall.toolName}'`, ids);
211
+ const id = isRecord(toolCall.input) ? toolCall.input.option : void 0;
212
+ const option = typeof id === "string" ? options[ids.indexOf(id)] : void 0;
213
+ return option ? option.event : unofferedOption(`option ${JSON.stringify(id)}`, ids);
214
+ }
215
+ /**
216
+ * A stand-in event for a closed-choice answer that names no offered option.
217
+ * Its `type` is no candidate event, so `resolveDecision` records it as a
218
+ * failed `'unknown-event'` attempt (the type, which names the answer and the
219
+ * valid ids, is the attempt's reason) and asks again.
220
+ */
221
+ function unofferedOption(answer, ids) {
222
+ return { type: `${answer} was not offered (valid ids: ${ids.join(", ") || "none"})` };
223
+ }
176
224
  /**
177
225
  * Messages for a decision request, with prior failed `attempts` (§2.6)
178
226
  * rendered as appended user messages (via core's {@link renderDecisionAttempts})
@@ -196,6 +244,9 @@ function maxStepsSetting(request) {
196
244
  const maxSteps = request.maxSteps;
197
245
  return typeof maxSteps === "number" ? { stopWhen: stepCountIs(maxSteps) } : {};
198
246
  }
247
+ function isDecisionModel(model) {
248
+ return isRecord(model) && (typeof model.doDecide === "function" || typeof model.doEvaluate === "function");
249
+ }
199
250
  /**
200
251
  * Splits a portable `"provider/model-id"` model ref (the convention JSON
201
252
  * workflows and registry-less hosts use, e.g. `"openai/gpt-5.4-mini"`) into
@@ -230,12 +281,22 @@ function resolveAiSdkModel(options, modelRef) {
230
281
  if (!entry) throw new Error(`createAiSdkExecutors: unknown model '${modelRef}'.`);
231
282
  return isModelWithSettings(entry) ? entry : { model: entry };
232
283
  }
284
+ function resolveLanguageModel(options, request) {
285
+ const { model, settings } = resolveAiSdkModel(options, request.model);
286
+ if (isDecisionModel(model)) throw new Error(`createAiSdkExecutors: model '${request.model}' is a decision model, which only answers decide requests with options; text request '${request.name ?? "(unnamed)"}' needs a language model.`);
287
+ return {
288
+ model,
289
+ ...settings ? { settings } : {}
290
+ };
291
+ }
233
292
  /**
234
293
  * The `LanguageModel` inside a map entry, whichever form it takes. `AiSdkModelMap`
235
294
  * widens its return to {@link AiSdkModelMap}, so reading an entry out of the map
236
- * to hand to a raw AI SDK call needs this to get past the union.
295
+ * to hand to a raw AI SDK call needs this to get past the union. Throws for a
296
+ * decision model.
237
297
  */
238
298
  function toLanguageModel(entry) {
299
+ if (isDecisionModel(entry)) throw new TypeError("toLanguageModel: the entry is a decision model, not a language model.");
239
300
  return isModelWithSettings(entry) ? entry.model : entry;
240
301
  }
241
302
  function isModelWithSettings(entry) {
@@ -266,23 +327,10 @@ function truncated(request, info, partialOutput, cause) {
266
327
  ...cause !== void 0 ? { cause } : {}
267
328
  });
268
329
  }
269
- /**
270
- * The canonical Vercel AI SDK adapter: builds the `{ generateText, streamText,
271
- * decide }` executor set consumed by `createAgentRuntime`/`executeAgentRequest`. `ai`
272
- * must not become a dependency of core `src/` files — this subpath is the one
273
- * place it's imported, and callers must supply their own model resolver so no
274
- * concrete provider package (e.g. `@ai-sdk/openai`) becomes a dependency here
275
- * either.
276
- *
277
- * @example
278
- * ```ts
279
- * const executors = createAiSdkExecutors({ models: { quick: openai('gpt-5.4-mini') } });
280
- * const result = await runToQuiescence(createAgentRuntime(machine, { executors }), { input });
281
- * ```
282
- */
283
- function createAiSdkExecutors(options) {
330
+ function createAiSdkExecutors(source) {
331
+ const options = resolveModelSource("createAiSdkExecutors", source);
284
332
  const generateText$1 = async (request, info) => {
285
- const { model, settings: modelSettings } = resolveAiSdkModel(options, request.model);
333
+ const { model, settings: modelSettings } = resolveLanguageModel(options, request);
286
334
  const common = {
287
335
  model,
288
336
  abortSignal: info?.signal,
@@ -331,7 +379,7 @@ function createAiSdkExecutors(options) {
331
379
  };
332
380
  };
333
381
  const streamText$1 = async (request, info) => {
334
- const { model, settings: modelSettings } = resolveAiSdkModel(options, request.model);
382
+ const { model, settings: modelSettings } = resolveLanguageModel(options, request);
335
383
  const result = streamText({
336
384
  model,
337
385
  abortSignal: info?.signal,
@@ -351,7 +399,9 @@ function createAiSdkExecutors(options) {
351
399
  };
352
400
  const decide = async (request, info) => {
353
401
  const { model, settings: modelSettings } = resolveAiSdkModel(options, request.model);
354
- const tools = toAiSdkEventTools(request.events);
402
+ if (isDecisionModel(model)) return decideWithDecisionModel(model, request, info);
403
+ const choice = request.options ? toAiSdkOptionTool(request.options) : void 0;
404
+ const tools = choice ? choice.tools : toAiSdkEventTools(request.events);
355
405
  const messages = toDecisionMessages(request);
356
406
  const result = await generateText({
357
407
  model,
@@ -370,6 +420,11 @@ function createAiSdkExecutors(options) {
370
420
  });
371
421
  const toolCall = result.toolCalls[0];
372
422
  if (!toolCall) throw new Error("createAiSdkExecutors: decide — model did not call an event tool.");
423
+ if (choice) return {
424
+ event: fromAiSdkOptionCall(toolCall, request.options, choice.ids),
425
+ usage: toAgentCallUsage(result.usage),
426
+ finishReason: result.finishReason
427
+ };
373
428
  const chosenEvent = request.events.find((event) => event.toolName === toolCall.toolName);
374
429
  if (!chosenEvent) throw new Error(`createAiSdkExecutors: decide — model called unknown tool '${toolCall.toolName}'.`);
375
430
  return {
@@ -387,5 +442,74 @@ function createAiSdkExecutors(options) {
387
442
  decide
388
443
  };
389
444
  }
445
+ /** The question id a decision model's closed choice is asked under. */
446
+ const DECISION_QUESTION_ID = "next";
447
+ async function decideWithDecisionModel(model, request, info) {
448
+ const offered = request.options;
449
+ if (!offered?.length) throw new Error(`createAiSdkExecutors: decision '${request.name ?? "(unnamed)"}' uses decision model '${request.model}', which needs a closed choice: pass \`payloads\` on agent.decide, or use decideNext.`);
450
+ const ids = decisionOptionIds(offered);
451
+ const criteria = Object.fromEntries(offered.map((option, index) => [ids[index], option.description ?? JSON.stringify(option.event)]));
452
+ const instructions = [[request.system, request.prompt].filter(Boolean).join("\n\n") || "Which option should happen next?", ...renderDecisionAttempts(request).map((message) => message.content)].join("\n\n");
453
+ const ask = () => experimental_decide({
454
+ model,
455
+ state: toDecisionState(request.input),
456
+ questions: { [DECISION_QUESTION_ID]: {
457
+ type: "choice",
458
+ instructions,
459
+ criteria
460
+ } },
461
+ ...info?.signal ? { abortSignal: info.signal } : {}
462
+ });
463
+ let result;
464
+ try {
465
+ result = await ask();
466
+ } catch (error) {
467
+ const chosen = unofferedChoice(error, ids);
468
+ if (chosen === void 0) throw error;
469
+ return decisionResult(unofferedOption(`option ${JSON.stringify(chosen)}`, ids));
470
+ }
471
+ const answer = result.answers[DECISION_QUESTION_ID];
472
+ const option = offered[ids.indexOf(answer.choice)];
473
+ if (!option) return decisionResult(unofferedOption(`option ${JSON.stringify(answer.choice)}`, ids));
474
+ const reported = (result.providerMetadata?.typesafe?.confidence)?.[DECISION_QUESTION_ID];
475
+ const confidence = typeof reported === "number" ? reported : answer.probabilities?.[answer.choice];
476
+ return decisionResult(option.event, result.usage, confidence);
477
+ }
478
+ function decisionResult(event, usage = {}, confidence) {
479
+ return {
480
+ event,
481
+ usage: toAgentCallUsage({
482
+ inputTokens: usage.inputTokens,
483
+ outputTokens: usage.outputTokens,
484
+ totalTokens: usage.totalTokens,
485
+ inputTokenDetails: {
486
+ noCacheTokens: void 0,
487
+ cacheReadTokens: void 0,
488
+ cacheWriteTokens: void 0
489
+ },
490
+ outputTokenDetails: {
491
+ textTokens: void 0,
492
+ reasoningTokens: void 0
493
+ }
494
+ }),
495
+ finishReason: "stop",
496
+ ...confidence !== void 0 ? { confidence } : {}
497
+ };
498
+ }
499
+ function unofferedChoice(error, ids) {
500
+ if (!InvalidResponseDataError.isInstance(error) || !isRecord(error.data)) return void 0;
501
+ const answer = error.data[DECISION_QUESTION_ID];
502
+ const choice = isRecord(answer) ? answer.choice : void 0;
503
+ return typeof choice === "string" && !ids.includes(choice) ? choice : void 0;
504
+ }
505
+ function toDecisionState(value) {
506
+ if (typeof value === "string") return value;
507
+ try {
508
+ const json = JSON.parse(JSON.stringify(value ?? {}));
509
+ return isRecord(json) ? json : { value: json };
510
+ } catch {
511
+ return String(value);
512
+ }
513
+ }
390
514
  //#endregion
391
515
  export { createAiSdkExecutors, parseModelRef, toLanguageModel };
@@ -1,4 +1,4 @@
1
- import { L as userMessage, R as validateSchemaSync, g as resolveTextLogicValue, n as DECIDE_ACTOR, v as executorBoundLogics, y as getAgentExecutionOptions, z as AgentError } from "./text-logic-CIqHAmBi.mjs";
1
+ import { H as getAgentExecutionOptions, T as DECIDE_ACTOR, V as executorBoundLogics, at as userMessage, ot as validateSchemaSync, st as AgentError, u as canonicalEventJson, z as resolveTextLogicValue } from "./event-log-GPZO9p7_.mjs";
2
2
  import { createAsyncLogic, getNextTransitions } from "xstate";
3
3
  //#region src/internal/is-record.ts
4
4
  /** True for a plain object-like value (arrays excluded). @internal */
@@ -33,7 +33,8 @@ function hashString(value) {
33
33
  return (hash >>> 0).toString(36);
34
34
  }
35
35
  function sanitizeEventToolName(eventType) {
36
- const base = `${EVENT_TOOL_PREFIX}${eventType.replace(/[^a-zA-Z0-9_-]/g, "_") || "event"}`;
36
+ const sanitizedType = eventType.replace(/[^a-zA-Z0-9_-]/g, "_") || "event";
37
+ const base = `${EVENT_TOOL_PREFIX}${sanitizedType}`;
37
38
  if (base.length <= 64) return base;
38
39
  const hash = hashString(eventType);
39
40
  const prefixLength = 64 - hash.length - 1;
@@ -105,7 +106,8 @@ function parseAgentEvent(source, payload, options = {}) {
105
106
  if (!isRecord(payload) || typeof payload.type !== "string") throw new AgentInvalidEventPayloadError("(unknown)", "expected an object with a string `type`.");
106
107
  const event = payload;
107
108
  if (isReservedAgentEvent(event.type)) throw new AgentInvalidEventPayloadError(event.type, "this event type is reserved for the library and cannot be sent from outside.");
108
- const registered = getAgentExecutionOptions(typeof source.provide === "function" ? source : source.machine)?.schemas;
109
+ const machine = typeof source.provide === "function" ? source : source.machine;
110
+ const registered = getAgentExecutionOptions(machine)?.schemas;
109
111
  const inputSchema = (options.events ?? options.schemas?.events ?? registered?.events)?.[event.type];
110
112
  if (!inputSchema) return event;
111
113
  const { type, ...fields } = event;
@@ -153,6 +155,53 @@ function getAcceptedEvents(snapshot, options = {}) {
153
155
  }
154
156
  //#endregion
155
157
  //#region src/decision.ts
158
+ function optionsFromPayloads(eventTypes, payloads) {
159
+ return eventTypes.filter((type) => !isEventPattern(type)).flatMap((type) => (payloads[type] ?? [{}]).map((payload) => ({ event: {
160
+ ...payload,
161
+ type
162
+ } })));
163
+ }
164
+ /**
165
+ * The options a snapshot-aware host offers for a closed-choice request: rebuilt
166
+ * over the request's snapshot-resolved candidate `events` when the decide input
167
+ * carried `payloads` (so wildcard or omitted `allowedEvents` still get theirs),
168
+ * then narrowed to the events `can` accepts. `undefined` when the request is
169
+ * not a closed choice. Pass the result as the request's `options`;
170
+ * {@link resolveDecision} then rejects a choice that matches none of them.
171
+ * @internal
172
+ */
173
+ function acceptedDecisionOptions(request, can) {
174
+ if (!request.options) return void 0;
175
+ const payloads = isRecord(request.input) ? request.input.payloads : void 0;
176
+ return (isRecord(payloads) ? optionsFromPayloads(request.events.map((event) => event.type), payloads) : request.options).filter((option) => {
177
+ try {
178
+ return can(option.event);
179
+ } catch {
180
+ return false;
181
+ }
182
+ });
183
+ }
184
+ /** The offered option equal to `event` (type and payload, key order ignored). @internal */
185
+ function findDecisionOption(options, event) {
186
+ const key = canonicalEventJson(event);
187
+ return options.find((option) => canonicalEventJson(option.event) === key);
188
+ }
189
+ /**
190
+ * Stable, readable ids for options, one per option in order: `type`, then each
191
+ * payload value (`refund:40`). Duplicates get a `#n` suffix. Adapters use them
192
+ * as the labels of a single closed choice and map the answer back by index.
193
+ * @internal
194
+ */
195
+ function decisionOptionIds(options) {
196
+ const taken = /* @__PURE__ */ new Set();
197
+ return options.map(({ event }) => {
198
+ const base = Object.entries(event).filter(([key]) => key !== "type").map(([, value]) => typeof value === "object" ? JSON.stringify(value) : String(value)).reduce((id, value) => `${id}:${value}`, event.type);
199
+ let id = base;
200
+ for (let n = 2; taken.has(id); n++) id = `${base}#${n}`;
201
+ taken.add(id);
202
+ return id;
203
+ });
204
+ }
156
205
  function decideRequestFromInput(input) {
157
206
  const allowedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input) ?? [];
158
207
  return {
@@ -168,6 +217,7 @@ function decideRequestFromInput(input) {
168
217
  type,
169
218
  toolName: sanitizeEventToolName(type)
170
219
  })),
220
+ ...input.payloads ? { options: optionsFromPayloads(allowedEventTypes, input.payloads) } : {},
171
221
  attempts: [],
172
222
  temperature: input.temperature,
173
223
  maxOutputTokens: input.maxOutputTokens,
@@ -175,7 +225,8 @@ function decideRequestFromInput(input) {
175
225
  topK: input.topK,
176
226
  seed: input.seed,
177
227
  stopSequences: input.stopSequences,
178
- metadata: input.metadata
228
+ metadata: input.metadata,
229
+ maxRetries: input.maxRetries
179
230
  };
180
231
  }
181
232
  function decideActorWithExecutor(execute) {
@@ -366,8 +417,8 @@ var AgentDecisionExhaustedError = class extends AgentError {
366
417
  * ```
367
418
  */
368
419
  function renderDecisionAttempts(request) {
369
- const types = request.events.map((event) => event.type).join(", ") || "(none)";
370
- return request.attempts.map((attempt) => userMessage(`Your previous choice failed: ${attempt.reason}. Choose again from: ${types}`));
420
+ const choices = (request.options ? decisionOptionIds(request.options) : request.events.map((event) => event.type)).join(", ") || "(none)";
421
+ return request.attempts.map((attempt) => userMessage(`Your previous choice failed: ${attempt.reason}. Choose again from: ${choices}`));
371
422
  }
372
423
  /**
373
424
  * Validation + retry core for decisions. No provider mechanics — the
@@ -381,6 +432,8 @@ function renderDecisionAttempts(request) {
381
432
  * event's schema), or `'rejected-by-guard'` (passes both checks but
382
433
  * `options.canTake` returns `false` — a type/payload-legal event the
383
434
  * machine's guard rejects right now; omit `canTake` to skip this check).
435
+ * When the request carries `options`, a choice matching none of them (type and
436
+ * payload, deep-equal) also fails as `'rejected-by-guard'`.
384
437
  * Every prior failed attempt for this call is fed back to the executor on
385
438
  * the next attempt via `request.attempts`, so an adapter can render "your
386
439
  * last choice failed because X — try again" into the next model call; core
@@ -400,6 +453,11 @@ async function resolveDecision(request, executors, options = {}) {
400
453
  const maxRetries = options.maxRetries ?? 2;
401
454
  const attempts = [];
402
455
  const eventsByType = new Map(request.events.map((event) => [event.type, event]));
456
+ if (request.options !== void 0 && request.options.length === 0) {
457
+ const error = new AgentDecisionExhaustedError([]);
458
+ error.message = `Decision '${request.name ?? (request.id || "agent.decide")}' has no offered option the current snapshot accepts; no model call was made.`;
459
+ throw error;
460
+ }
403
461
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
404
462
  options.signal?.throwIfAborted();
405
463
  const result = await executor({
@@ -438,6 +496,14 @@ async function resolveDecision(request, executors, options = {}) {
438
496
  continue;
439
497
  }
440
498
  }
499
+ if (request.options && !findDecisionOption(request.options, event) && !findDecisionOption(request.options, validatedEvent)) {
500
+ attempts.push({
501
+ event: validatedEvent,
502
+ failure: "rejected-by-guard",
503
+ reason: `${JSON.stringify(validatedEvent)} matched no offered option: ${decisionOptionIds(request.options).join(", ") || "(none)"}.`
504
+ });
505
+ continue;
506
+ }
441
507
  if (options.canTake?.(validatedEvent) === false) {
442
508
  attempts.push({
443
509
  event: validatedEvent,
@@ -451,4 +517,4 @@ async function resolveDecision(request, executors, options = {}) {
451
517
  throw new AgentDecisionExhaustedError(attempts);
452
518
  }
453
519
  //#endregion
454
- export { isDecisionLogic as a, AgentInvalidEventPayloadError as c, isRecord as d, createDecisionRequest as i, getAcceptedEvents as l, createDecideActor as n, renderDecisionAttempts as o, createDecisionLogic as r, resolveDecision as s, AgentDecisionExhaustedError as t, parseAgentEvent as u };
520
+ export { createDecisionRequest as a, renderDecisionAttempts as c, getAcceptedEvents as d, isEventPattern as f, createDecisionLogic as i, resolveDecision as l, isRecord as m, acceptedDecisionOptions as n, decisionOptionIds as o, parseAgentEvent as p, createDecideActor as r, isDecisionLogic as s, AgentDecisionExhaustedError as t, AgentInvalidEventPayloadError as u };
@@ -1,5 +1,4 @@
1
- const require_text_logic = require("./text-logic-f5F5sCdc.cjs");
2
- require("./validate.cjs");
1
+ const require_event_log = require("./event-log-Dtb16p1p.cjs");
3
2
  let xstate = require("xstate");
4
3
  //#region src/internal/is-record.ts
5
4
  /** True for a plain object-like value (arrays excluded). @internal */
@@ -18,7 +17,7 @@ function isRecord(value) {
18
17
  * has no transition for, so an event the current state does not handle parses
19
18
  * fine and settles the run with `result.ignored` set.
20
19
  */
21
- var AgentInvalidEventPayloadError = class extends require_text_logic.AgentError {
20
+ var AgentInvalidEventPayloadError = class extends require_event_log.AgentError {
22
21
  eventType;
23
22
  constructor(eventType, detail) {
24
23
  super("invalid-event-payload", `Invalid event payload for '${eventType}': ${detail}`);
@@ -34,7 +33,8 @@ function hashString(value) {
34
33
  return (hash >>> 0).toString(36);
35
34
  }
36
35
  function sanitizeEventToolName(eventType) {
37
- const base = `${EVENT_TOOL_PREFIX}${eventType.replace(/[^a-zA-Z0-9_-]/g, "_") || "event"}`;
36
+ const sanitizedType = eventType.replace(/[^a-zA-Z0-9_-]/g, "_") || "event";
37
+ const base = `${EVENT_TOOL_PREFIX}${sanitizedType}`;
38
38
  if (base.length <= 64) return base;
39
39
  const hash = hashString(eventType);
40
40
  const prefixLength = 64 - hash.length - 1;
@@ -106,13 +106,14 @@ function parseAgentEvent(source, payload, options = {}) {
106
106
  if (!isRecord(payload) || typeof payload.type !== "string") throw new AgentInvalidEventPayloadError("(unknown)", "expected an object with a string `type`.");
107
107
  const event = payload;
108
108
  if (isReservedAgentEvent(event.type)) throw new AgentInvalidEventPayloadError(event.type, "this event type is reserved for the library and cannot be sent from outside.");
109
- const registered = require_text_logic.getAgentExecutionOptions(typeof source.provide === "function" ? source : source.machine)?.schemas;
109
+ const machine = typeof source.provide === "function" ? source : source.machine;
110
+ const registered = require_event_log.getAgentExecutionOptions(machine)?.schemas;
110
111
  const inputSchema = (options.events ?? options.schemas?.events ?? registered?.events)?.[event.type];
111
112
  if (!inputSchema) return event;
112
113
  const { type, ...fields } = event;
113
114
  try {
114
115
  return {
115
- ...require_text_logic.validateSchemaSync(inputSchema, fields),
116
+ ...require_event_log.validateSchemaSync(inputSchema, fields),
116
117
  type
117
118
  };
118
119
  } catch (error) {
@@ -154,6 +155,53 @@ function getAcceptedEvents(snapshot, options = {}) {
154
155
  }
155
156
  //#endregion
156
157
  //#region src/decision.ts
158
+ function optionsFromPayloads(eventTypes, payloads) {
159
+ return eventTypes.filter((type) => !isEventPattern(type)).flatMap((type) => (payloads[type] ?? [{}]).map((payload) => ({ event: {
160
+ ...payload,
161
+ type
162
+ } })));
163
+ }
164
+ /**
165
+ * The options a snapshot-aware host offers for a closed-choice request: rebuilt
166
+ * over the request's snapshot-resolved candidate `events` when the decide input
167
+ * carried `payloads` (so wildcard or omitted `allowedEvents` still get theirs),
168
+ * then narrowed to the events `can` accepts. `undefined` when the request is
169
+ * not a closed choice. Pass the result as the request's `options`;
170
+ * {@link resolveDecision} then rejects a choice that matches none of them.
171
+ * @internal
172
+ */
173
+ function acceptedDecisionOptions(request, can) {
174
+ if (!request.options) return void 0;
175
+ const payloads = isRecord(request.input) ? request.input.payloads : void 0;
176
+ return (isRecord(payloads) ? optionsFromPayloads(request.events.map((event) => event.type), payloads) : request.options).filter((option) => {
177
+ try {
178
+ return can(option.event);
179
+ } catch {
180
+ return false;
181
+ }
182
+ });
183
+ }
184
+ /** The offered option equal to `event` (type and payload, key order ignored). @internal */
185
+ function findDecisionOption(options, event) {
186
+ const key = require_event_log.canonicalEventJson(event);
187
+ return options.find((option) => require_event_log.canonicalEventJson(option.event) === key);
188
+ }
189
+ /**
190
+ * Stable, readable ids for options, one per option in order: `type`, then each
191
+ * payload value (`refund:40`). Duplicates get a `#n` suffix. Adapters use them
192
+ * as the labels of a single closed choice and map the answer back by index.
193
+ * @internal
194
+ */
195
+ function decisionOptionIds(options) {
196
+ const taken = /* @__PURE__ */ new Set();
197
+ return options.map(({ event }) => {
198
+ const base = Object.entries(event).filter(([key]) => key !== "type").map(([, value]) => typeof value === "object" ? JSON.stringify(value) : String(value)).reduce((id, value) => `${id}:${value}`, event.type);
199
+ let id = base;
200
+ for (let n = 2; taken.has(id); n++) id = `${base}#${n}`;
201
+ taken.add(id);
202
+ return id;
203
+ });
204
+ }
157
205
  function decideRequestFromInput(input) {
158
206
  const allowedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input) ?? [];
159
207
  return {
@@ -169,6 +217,7 @@ function decideRequestFromInput(input) {
169
217
  type,
170
218
  toolName: sanitizeEventToolName(type)
171
219
  })),
220
+ ...input.payloads ? { options: optionsFromPayloads(allowedEventTypes, input.payloads) } : {},
172
221
  attempts: [],
173
222
  temperature: input.temperature,
174
223
  maxOutputTokens: input.maxOutputTokens,
@@ -176,15 +225,16 @@ function decideRequestFromInput(input) {
176
225
  topK: input.topK,
177
226
  seed: input.seed,
178
227
  stopSequences: input.stopSequences,
179
- metadata: input.metadata
228
+ metadata: input.metadata,
229
+ maxRetries: input.maxRetries
180
230
  };
181
231
  }
182
232
  function decideActorWithExecutor(execute) {
183
233
  const logic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal }) => {
184
- if (!execute) throw new Error(`'${require_text_logic.DECIDE_ACTOR}' has no host execution. Provide an implementation with machine.provide({ actors: { '${require_text_logic.DECIDE_ACTOR}': ... } }) or resolve the returned agent request with resolveDecision(...).`);
234
+ if (!execute) throw new Error(`'${require_event_log.DECIDE_ACTOR}' has no host execution. Provide an implementation with machine.provide({ actors: { '${require_event_log.DECIDE_ACTOR}': ... } }) or resolve the returned agent request with resolveDecision(...).`);
185
235
  const resolvedEventTypes = resolveAllowedEventTypes(input.allowedEvents, input);
186
- if (resolvedEventTypes === void 0) throw new Error(`'${require_text_logic.DECIDE_ACTOR}' input has omitted \`allowedEvents\`, which means "all currently-legal events" — but that requires a snapshot-aware host (createAgentRuntime) to resolve. Under a bare createActor(...), declare \`allowedEvents\` explicitly to use this actor here.`);
187
- if (resolvedEventTypes.some(isEventPattern)) throw new Error(`'${require_text_logic.DECIDE_ACTOR}' input uses wildcard \`allowedEvents\` patterns, which expand against the live snapshot — that requires a snapshot-aware host (createAgentRuntime). Under a bare createActor(...), list event types explicitly.`);
236
+ if (resolvedEventTypes === void 0) throw new Error(`'${require_event_log.DECIDE_ACTOR}' input has omitted \`allowedEvents\`, which means "all currently-legal events" — but that requires a snapshot-aware host (createAgentRuntime) to resolve. Under a bare createActor(...), declare \`allowedEvents\` explicitly to use this actor here.`);
237
+ if (resolvedEventTypes.some(isEventPattern)) throw new Error(`'${require_event_log.DECIDE_ACTOR}' input uses wildcard \`allowedEvents\` patterns, which expand against the live snapshot — that requires a snapshot-aware host (createAgentRuntime). Under a bare createActor(...), list event types explicitly.`);
188
238
  return resolveDecision(decideRequestFromInput(input), { decide: execute }, {
189
239
  maxRetries: input.maxRetries ?? 2,
190
240
  signal
@@ -236,30 +286,30 @@ function resolveAllowedEventTypes(allowedEvents, input) {
236
286
  function createDecisionLogic(config, execute) {
237
287
  const maxRetries = config.maxRetries ?? 2;
238
288
  const request = (input) => {
239
- const parsedInput = config.schemas ? require_text_logic.validateSchemaSync(config.schemas.input, input) : input;
289
+ const parsedInput = config.schemas ? require_event_log.validateSchemaSync(config.schemas.input, input) : input;
240
290
  const args = { input: parsedInput };
241
291
  const allowedEventTypes = resolveAllowedEventTypes(config.allowedEvents, parsedInput);
242
292
  return {
243
293
  kind: "decision",
244
294
  id: "",
245
- name: require_text_logic.resolveTextLogicValue(config.name, args),
295
+ name: require_event_log.resolveTextLogicValue(config.name, args),
246
296
  input: parsedInput,
247
- model: require_text_logic.resolveTextLogicValue(config.model, args),
248
- system: require_text_logic.resolveTextLogicValue(config.system, args),
249
- prompt: require_text_logic.resolveTextLogicValue(config.prompt, args),
250
- messages: require_text_logic.resolveTextLogicValue(config.messages, args),
297
+ model: require_event_log.resolveTextLogicValue(config.model, args),
298
+ system: require_event_log.resolveTextLogicValue(config.system, args),
299
+ prompt: require_event_log.resolveTextLogicValue(config.prompt, args),
300
+ messages: require_event_log.resolveTextLogicValue(config.messages, args),
251
301
  events: (allowedEventTypes ?? []).map((type) => ({
252
302
  type,
253
303
  toolName: sanitizeEventToolName(type)
254
304
  })),
255
305
  attempts: [],
256
- temperature: require_text_logic.resolveTextLogicValue(config.temperature, args),
257
- maxOutputTokens: require_text_logic.resolveTextLogicValue(config.maxOutputTokens, args),
258
- topP: require_text_logic.resolveTextLogicValue(config.topP, args),
259
- topK: require_text_logic.resolveTextLogicValue(config.topK, args),
260
- seed: require_text_logic.resolveTextLogicValue(config.seed, args),
261
- stopSequences: require_text_logic.resolveTextLogicValue(config.stopSequences, args),
262
- metadata: require_text_logic.resolveTextLogicValue(config.metadata, args)
306
+ temperature: require_event_log.resolveTextLogicValue(config.temperature, args),
307
+ maxOutputTokens: require_event_log.resolveTextLogicValue(config.maxOutputTokens, args),
308
+ topP: require_event_log.resolveTextLogicValue(config.topP, args),
309
+ topK: require_event_log.resolveTextLogicValue(config.topK, args),
310
+ seed: require_event_log.resolveTextLogicValue(config.seed, args),
311
+ stopSequences: require_event_log.resolveTextLogicValue(config.stopSequences, args),
312
+ metadata: require_event_log.resolveTextLogicValue(config.metadata, args)
263
313
  };
264
314
  };
265
315
  const logic = (0, xstate.createAsyncLogic)({ run: async ({ input, signal }) => {
@@ -281,7 +331,7 @@ function createDecisionLogic(config, execute) {
281
331
  return createDecisionLogic(config, nextExecute);
282
332
  }
283
333
  });
284
- if (execute) require_text_logic.executorBoundLogics.add(decisionLogic);
334
+ if (execute) require_event_log.executorBoundLogics.add(decisionLogic);
285
335
  return decisionLogic;
286
336
  }
287
337
  /** Type guard: true for any actor logic built by createDecisionLogic/createDecideActor (checks the `kind` marker). @internal */
@@ -344,7 +394,7 @@ function dedupeToolNames(candidates) {
344
394
  * diagnostics; a machine typically routes this via the decision invoke's
345
395
  * `onError`.
346
396
  */
347
- var AgentDecisionExhaustedError = class extends require_text_logic.AgentError {
397
+ var AgentDecisionExhaustedError = class extends require_event_log.AgentError {
348
398
  attempts;
349
399
  constructor(attempts) {
350
400
  super("decision-exhausted", `Decision exhausted after ${attempts.length} attempt${attempts.length === 1 ? "" : "s"}: ` + attempts.map((attempt) => attempt.reason).join("; "));
@@ -367,8 +417,8 @@ var AgentDecisionExhaustedError = class extends require_text_logic.AgentError {
367
417
  * ```
368
418
  */
369
419
  function renderDecisionAttempts(request) {
370
- const types = request.events.map((event) => event.type).join(", ") || "(none)";
371
- return request.attempts.map((attempt) => require_text_logic.userMessage(`Your previous choice failed: ${attempt.reason}. Choose again from: ${types}`));
420
+ const choices = (request.options ? decisionOptionIds(request.options) : request.events.map((event) => event.type)).join(", ") || "(none)";
421
+ return request.attempts.map((attempt) => require_event_log.userMessage(`Your previous choice failed: ${attempt.reason}. Choose again from: ${choices}`));
372
422
  }
373
423
  /**
374
424
  * Validation + retry core for decisions. No provider mechanics — the
@@ -382,6 +432,8 @@ function renderDecisionAttempts(request) {
382
432
  * event's schema), or `'rejected-by-guard'` (passes both checks but
383
433
  * `options.canTake` returns `false` — a type/payload-legal event the
384
434
  * machine's guard rejects right now; omit `canTake` to skip this check).
435
+ * When the request carries `options`, a choice matching none of them (type and
436
+ * payload, deep-equal) also fails as `'rejected-by-guard'`.
385
437
  * Every prior failed attempt for this call is fed back to the executor on
386
438
  * the next attempt via `request.attempts`, so an adapter can render "your
387
439
  * last choice failed because X — try again" into the next model call; core
@@ -397,10 +449,15 @@ function renderDecisionAttempts(request) {
397
449
  */
398
450
  async function resolveDecision(request, executors, options = {}) {
399
451
  const executor = executors?.decide;
400
- if (typeof executor !== "function") throw new require_text_logic.AgentError("missing-decide-executor", "resolveDecision(request, executors, ...) takes the EXECUTOR SET (the same object executeAgentRequest takes), and this one has no 'decide' function. Build one with createAiSdkExecutors({ models }), or pass { decide }.");
452
+ if (typeof executor !== "function") throw new require_event_log.AgentError("missing-decide-executor", "resolveDecision(request, executors, ...) takes the EXECUTOR SET (the same object executeAgentRequest takes), and this one has no 'decide' function. Build one with createAiSdkExecutors({ models }), or pass { decide }.");
401
453
  const maxRetries = options.maxRetries ?? 2;
402
454
  const attempts = [];
403
455
  const eventsByType = new Map(request.events.map((event) => [event.type, event]));
456
+ if (request.options !== void 0 && request.options.length === 0) {
457
+ const error = new AgentDecisionExhaustedError([]);
458
+ error.message = `Decision '${request.name ?? (request.id || "agent.decide")}' has no offered option the current snapshot accepts; no model call was made.`;
459
+ throw error;
460
+ }
404
461
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
405
462
  options.signal?.throwIfAborted();
406
463
  const result = await executor({
@@ -427,7 +484,7 @@ async function resolveDecision(request, executors, options = {}) {
427
484
  const { type, ...payload } = event;
428
485
  try {
429
486
  validatedEvent = {
430
- ...require_text_logic.validateSchemaSync(descriptor.inputSchema, payload),
487
+ ...require_event_log.validateSchemaSync(descriptor.inputSchema, payload),
431
488
  type
432
489
  };
433
490
  } catch (error) {
@@ -439,6 +496,14 @@ async function resolveDecision(request, executors, options = {}) {
439
496
  continue;
440
497
  }
441
498
  }
499
+ if (request.options && !findDecisionOption(request.options, event) && !findDecisionOption(request.options, validatedEvent)) {
500
+ attempts.push({
501
+ event: validatedEvent,
502
+ failure: "rejected-by-guard",
503
+ reason: `${JSON.stringify(validatedEvent)} matched no offered option: ${decisionOptionIds(request.options).join(", ") || "(none)"}.`
504
+ });
505
+ continue;
506
+ }
442
507
  if (options.canTake?.(validatedEvent) === false) {
443
508
  attempts.push({
444
509
  event: validatedEvent,
@@ -464,6 +529,12 @@ Object.defineProperty(exports, "AgentInvalidEventPayloadError", {
464
529
  return AgentInvalidEventPayloadError;
465
530
  }
466
531
  });
532
+ Object.defineProperty(exports, "acceptedDecisionOptions", {
533
+ enumerable: true,
534
+ get: function() {
535
+ return acceptedDecisionOptions;
536
+ }
537
+ });
467
538
  Object.defineProperty(exports, "createDecideActor", {
468
539
  enumerable: true,
469
540
  get: function() {
@@ -482,6 +553,12 @@ Object.defineProperty(exports, "createDecisionRequest", {
482
553
  return createDecisionRequest;
483
554
  }
484
555
  });
556
+ Object.defineProperty(exports, "decisionOptionIds", {
557
+ enumerable: true,
558
+ get: function() {
559
+ return decisionOptionIds;
560
+ }
561
+ });
485
562
  Object.defineProperty(exports, "getAcceptedEvents", {
486
563
  enumerable: true,
487
564
  get: function() {
@@ -494,6 +571,12 @@ Object.defineProperty(exports, "isDecisionLogic", {
494
571
  return isDecisionLogic;
495
572
  }
496
573
  });
574
+ Object.defineProperty(exports, "isEventPattern", {
575
+ enumerable: true,
576
+ get: function() {
577
+ return isEventPattern;
578
+ }
579
+ });
497
580
  Object.defineProperty(exports, "isRecord", {
498
581
  enumerable: true,
499
582
  get: function() {