@statelyai/agent 2.0.0-alpha.28 → 2.0.0-alpha.30

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 (80) hide show
  1. package/dist/ai-sdk.cjs +150 -27
  2. package/dist/ai-sdk.d.cts +59 -36
  3. package/dist/ai-sdk.d.mts +59 -36
  4. package/dist/ai-sdk.mjs +148 -24
  5. package/dist/{decision-RQ0iv0oZ.mjs → decision-BHkvuImD.mjs} +74 -8
  6. package/dist/{decision-CmM8-HHz.cjs → decision-CIb7ddIv.cjs} +113 -30
  7. package/dist/{text-logic-f5F5sCdc.cjs → event-log-BZRfBRd7.cjs} +814 -7
  8. package/dist/{text-logic-CIqHAmBi.mjs → event-log-DJCMHek_.mjs} +680 -4
  9. package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CIIJohjj.d.mts} +90 -5
  10. package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-CLAC9q2Y.d.cts} +90 -5
  11. package/dist/event-log-store-DZR4IIWe.cjs +95 -0
  12. package/dist/event-log-store-DsP6GCRh.mjs +84 -0
  13. package/dist/index.cjs +576 -58
  14. package/dist/index.d.cts +104 -510
  15. package/dist/index.d.mts +104 -510
  16. package/dist/index.mjs +547 -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-BCKg4qJ1.cjs +44 -0
  26. package/dist/model-source-BaHwJ1wK.mjs +39 -0
  27. package/dist/openai.cjs +11 -11
  28. package/dist/openai.d.cts +28 -26
  29. package/dist/openai.d.mts +28 -26
  30. package/dist/openai.mjs +3 -3
  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-xNTxpt82.mjs → run-agent-BpHqkaZw.mjs} +181 -44
  36. package/dist/{run-agent-COXXc0RW.d.mts → run-agent-CAVt5R-r.d.mts} +95 -30
  37. package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CTlMc3Jb.d.cts} +95 -30
  38. package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-ctLCvNgL.cjs} +230 -88
  39. package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-7rV_cujV.mjs} +15 -9
  40. package/dist/setup-agent-CGELn1qZ.d.mts +577 -0
  41. package/dist/setup-agent-CLPCkXsu.d.cts +577 -0
  42. package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DoUk7EdC.cjs} +31 -20
  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 +204 -985
  48. package/dist/testing.d.cts +132 -347
  49. package/dist/testing.d.mts +132 -347
  50. package/dist/testing.mjs +191 -973
  51. package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-BRYOPWvY.d.mts} +52 -12
  52. package/dist/{text-logic-D-czVPYp.d.mts → text-logic-DmciuVUQ.d.cts} +52 -12
  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-BkM3z02Y.d.mts +17 -0
  56. package/dist/usage-Cm_ovz9V.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-COFWyprJ.mjs +1181 -0
  64. package/dist/verify-CVlsTZ-V.cjs +1222 -0
  65. package/dist/verify-Cn86rT8l.d.mts +414 -0
  66. package/dist/verify-Dzwd4Mvk.d.cts +414 -0
  67. package/package.json +44 -31
  68. package/readme.md +40 -28
  69. package/skills/add-evals/SKILL.md +129 -0
  70. package/skills/add-traces/SKILL.md +135 -0
  71. package/skills/generate-machine/SKILL.md +143 -69
  72. package/skills/graph-engineering/SKILL.md +33 -0
  73. package/skills/improve-machine/SKILL.md +241 -0
  74. package/skills/improve-machine/reference.md +101 -0
  75. package/skills/loop-to-machine/SKILL.md +183 -0
  76. package/skills/loop-to-machine/policy.md +158 -0
  77. package/dist/event-log-store-2pAFiWuc.mjs +0 -576
  78. package/dist/event-log-store-C-COhIXP.cjs +0 -702
  79. package/dist/usage-BdEjFuIi.mjs +0 -5
  80. 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-DJCMHek_.mjs";
2
+ import { c as renderDecisionAttempts, m as isRecord, o as decisionOptionIds } from "./decision-BHkvuImD.mjs";
3
+ import { t as resolveModelSource } from "./model-source-BaHwJ1wK.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,9 +244,12 @@ 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
- * workflows and registry-less hosts use, e.g. `"openai/gpt-5.4-mini"`) into
252
+ * workflows and registry-less hosts use, e.g. `"openai/gpt-6-luna"`) into
202
253
  * its parts. A ref with no `/` has no provider — `modelId` is the whole ref.
203
254
  * The standard building block for a host's `resolveModel`:
204
255
  *
@@ -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-DJCMHek_.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) {
@@ -222,7 +273,7 @@ function resolveAllowedEventTypes(allowedEvents, input) {
222
273
  *
223
274
  * export const chooseMove = createDecisionLogic({
224
275
  * schemas: { input: z.object({ playerHp: z.number(), enemyHp: z.number() }) },
225
- * model: 'openai/gpt-5.4-mini',
276
+ * model: 'openai/gpt-6-luna',
226
277
  * system: 'You are playing a turn-based game. Choose exactly one legal move.',
227
278
  * prompt: ({ input }) => `Player HP: ${input.playerHp}\nEnemy HP: ${input.enemyHp}`,
228
279
  * allowedEvents: ({ input }) =>
@@ -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 };