@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/index.mjs CHANGED
@@ -1,30 +1,30 @@
1
- import { A as getMessageText, B as AgentTruncatedError, D as getJsonSchema, F as systemMessage, I as toolMessage, L as userMessage, M as getStatePath, N as isStandardSchema, O as getJsonSchemaSync, a as bindRequestExecutor, c as getAgentOutputMode, d as isTextLogic, h as providerOutputSchema, j as getStateMeta, k as getMachineStructuralHash, l as getCallFinishReason, m as parseProviderOutput, p as parseOutput, s as createTextLogic, u as getCallUsage, v as executorBoundLogics, w as assistantMessage, y as getAgentExecutionOptions, z as AgentError } from "./text-logic-CIqHAmBi.mjs";
2
- import { a as isDecisionLogic, c as AgentInvalidEventPayloadError, i as createDecisionRequest, l as getAcceptedEvents, o as renderDecisionAttempts, r as createDecisionLogic, s as resolveDecision, t as AgentDecisionExhaustedError, u as parseAgentEvent } from "./decision-RQ0iv0oZ.mjs";
3
- import { t as AGENT_USAGE_EVENT_TYPE } from "./usage-BdEjFuIi.mjs";
4
- import { n as getAgentSchemas, r as setupAgent, t as createAgentSchemas } from "./setup-agent-BuanDXeh.mjs";
5
- import { o as AgentMachineVersionMismatchError, t as AgentEventLogConflictError } from "./event-log-store-2pAFiWuc.mjs";
6
- import { a as bindDecisionForProvide, c as getConfiguredInvokeSrcs, d as isStateMachineLogic, f as runToQuiescence, i as bindChildMachineForProvide, l as inspectTransitions, m as traceTransitions, n as AgentMaxModelCallsExceededError, o as bindTextForProvide, p as serializeTraceEvent, r as AgentSnapshotDivergedError, s as createAgentRuntime, t as AGENT_TRACE_SCHEMA_VERSION, u as isAgentIdle } from "./run-agent-xNTxpt82.mjs";
1
+ import { $ as getStateMeta, A as createTextLogic, C as AGENT_USAGE_EVENT_TYPE, H as getAgentExecutionOptions, I as parseOutput, L as parseProviderOutput, M as getCallFinishReason, N as getCallUsage, O as bindRequestExecutor, P as isTextLogic, Q as getMessageText, R as providerOutputSchema, V as executorBoundLogics, X as getJsonSchemaSync, Y as getJsonSchema, Z as getMachineStructuralHash, at as userMessage, ct as AgentTruncatedError, et as getStatePath, i as AgentMachineVersionMismatchError, it as toolMessage, j as getAgentOutputMode, q as assistantMessage, rt as systemMessage, st as AgentError, tt as isStandardSchema, u as canonicalEventJson } from "./event-log-DJCMHek_.mjs";
2
+ import { a as createDecisionRequest, c as renderDecisionAttempts, d as getAcceptedEvents, f as isEventPattern, i as createDecisionLogic, l as resolveDecision, m as isRecord, p as parseAgentEvent, s as isDecisionLogic, t as AgentDecisionExhaustedError, u as AgentInvalidEventPayloadError } from "./decision-BHkvuImD.mjs";
3
+ import { n as getAgentSchemas, r as setupAgent, t as createAgentSchemas } from "./setup-agent-7rV_cujV.mjs";
4
+ import { t as AgentEventLogConflictError } from "./event-log-store-DsP6GCRh.mjs";
5
+ import { a as bindDecisionForProvide, c as getConfiguredInvokeSrcs, d as isStateMachineLogic, f as runAgent, h as traceTransitions, i as bindChildMachineForProvide, l as inspectTransitions, m as serializeTraceEvent, n as AgentMaxModelCallsExceededError, o as bindTextForProvide, p as runToQuiescence, r as AgentSnapshotDivergedError, s as createAgentRuntime, t as AGENT_TRACE_SCHEMA_VERSION, u as isAgentIdle } from "./run-agent-BpHqkaZw.mjs";
6
+ import { getNextTransitions, machineToJSON, transition } from "xstate";
7
7
  //#region src/messages.ts
8
- const KNOWN_PART_TYPES = new Set([
8
+ const KNOWN_PART_TYPES = /* @__PURE__ */ new Set([
9
9
  "text",
10
10
  "image",
11
11
  "file",
12
12
  "tool-call",
13
13
  "tool-result"
14
14
  ]);
15
- const USER_PART_TYPES = new Set([
15
+ const USER_PART_TYPES = /* @__PURE__ */ new Set([
16
16
  "text",
17
17
  "image",
18
18
  "file"
19
19
  ]);
20
- const ASSISTANT_PART_TYPES = new Set([
20
+ const ASSISTANT_PART_TYPES = /* @__PURE__ */ new Set([
21
21
  "text",
22
22
  "file",
23
23
  "tool-call",
24
24
  "tool-result"
25
25
  ]);
26
- const TOOL_PART_TYPES = new Set(["tool-result"]);
27
- const TOOL_RESULT_CONTENT_PART_TYPES = new Set(["text", "image"]);
26
+ const TOOL_PART_TYPES = /* @__PURE__ */ new Set(["tool-result"]);
27
+ const TOOL_RESULT_CONTENT_PART_TYPES = /* @__PURE__ */ new Set(["text", "image"]);
28
28
  function isKnownPart(part) {
29
29
  const type = part && typeof part === "object" ? part.type : void 0;
30
30
  return typeof type === "string" && KNOWN_PART_TYPES.has(type);
@@ -38,7 +38,7 @@ function isMediaData(value) {
38
38
  function requireMediaData(part, type, field) {
39
39
  return isMediaData(part[field]) ? void 0 : `${type} part requires "${field}" to be a string, Uint8Array, ArrayBuffer, or URL`;
40
40
  }
41
- const TOOL_RESULT_OUTPUT_TYPES = new Set([
41
+ const TOOL_RESULT_OUTPUT_TYPES = /* @__PURE__ */ new Set([
42
42
  "text",
43
43
  "json",
44
44
  "error-text",
@@ -152,11 +152,22 @@ function validateEventMeta(value, path) {
152
152
  if (event !== void 0 && (!event || typeof event !== "object" || Array.isArray(event))) issues.push(issue("Expected an object of fixed event fields", [...path, "event"]));
153
153
  return issues;
154
154
  }
155
+ function validateChoice(value, path) {
156
+ if (!value || typeof value !== "object" || Array.isArray(value)) return [issue("Expected a { label, event } choice", path)];
157
+ const choice = value;
158
+ const issues = [];
159
+ if (typeof choice["label"] !== "string") issues.push(issue("Expected a string", [...path, "label"]));
160
+ if (choice["style"] !== void 0 && typeof choice["style"] !== "string") issues.push(issue("Expected a string", [...path, "style"]));
161
+ const event = choice["event"];
162
+ if (!event || typeof event !== "object" || Array.isArray(event) || typeof event.type !== "string") issues.push(issue("Expected an event object with a string `type`", [...path, "event"]));
163
+ return issues;
164
+ }
155
165
  /**
156
166
  * A {@link StandardSchemaV1} for the state metadata {@link getInteraction}
157
167
  * reads: an object whose optional `interaction` declares a `label` (a string
158
168
  * with `{context.path}` interpolation, or a function of the context), an
159
- * `events` map of choices, and a `textEvent` for free-text answers. This is
169
+ * `events` map of choices, payload `choices` (an array, or a context path to
170
+ * one), and a `textEvent` (a type, or `{ type, field }`) for free-text answers. This is
160
171
  * the default `meta` type `setupAgent` gives a machine; pass it to
161
172
  * `createAgentSchemas({ meta: interactionMetaSchema })` only when building a
162
173
  * schema pack by hand.
@@ -174,13 +185,27 @@ const interactionMetaSchema = { "~standard": {
174
185
  const label = descriptor["label"];
175
186
  if (label !== void 0 && typeof label !== "string" && typeof label !== "function") issues.push(issue("Expected a string or a (context) => string", ["interaction", "label"]));
176
187
  const events = descriptor["events"];
177
- if (events !== void 0) if (!events || typeof events !== "object" || Array.isArray(events)) issues.push(issue("Expected an object keyed by event type", ["interaction", "events"]));
178
- else for (const [type, config] of Object.entries(events)) issues.push(...validateEventMeta(config, [
179
- "interaction",
180
- "events",
181
- type
182
- ]));
183
- if (descriptor["textEvent"] !== void 0 && typeof descriptor["textEvent"] !== "string") issues.push(issue("Expected a string", ["interaction", "textEvent"]));
188
+ if (events !== void 0) {
189
+ if (!events || typeof events !== "object" || Array.isArray(events)) issues.push(issue("Expected an object keyed by event type", ["interaction", "events"]));
190
+ else for (const [type, config] of Object.entries(events)) issues.push(...validateEventMeta(config, [
191
+ "interaction",
192
+ "events",
193
+ type
194
+ ]));
195
+ }
196
+ const choices = descriptor["choices"];
197
+ if (choices !== void 0 && typeof choices !== "string") {
198
+ if (!Array.isArray(choices)) issues.push(issue("Expected an array of choices or a context path string", ["interaction", "choices"]));
199
+ else choices.forEach((choice, index) => {
200
+ issues.push(...validateChoice(choice, [
201
+ "interaction",
202
+ "choices",
203
+ index
204
+ ]));
205
+ });
206
+ }
207
+ const textEvent = descriptor["textEvent"];
208
+ if (textEvent !== void 0 && typeof textEvent !== "string" && !(textEvent && typeof textEvent === "object" && typeof textEvent.type === "string" && typeof textEvent.field === "string")) issues.push(issue("Expected a string or { type, field }", ["interaction", "textEvent"]));
184
209
  return issues.length > 0 ? { issues } : { value };
185
210
  }
186
211
  } };
@@ -197,13 +222,20 @@ function interpolate(label, context, preserveWhitespace) {
197
222
  });
198
223
  return preserveWhitespace ? interpolated : interpolated.replace(/\s+/g, " ").trim();
199
224
  }
225
+ function eventSchemasOf(snapshot) {
226
+ const machine = snapshot.machine;
227
+ return getAgentExecutionOptions(machine)?.schemas?.events ?? (machine?.schemas)?.events;
228
+ }
229
+ function isChoice(value) {
230
+ return validateChoice(value, []).length === 0;
231
+ }
200
232
  /** Reads the active state's interaction metadata and filters it through the
201
233
  * events XState currently accepts. Returns `undefined` when none is declared. */
202
234
  function getInteraction(snapshot, options = {}) {
203
235
  const interaction = getStateMeta(snapshot).interaction;
204
236
  if (!interaction) return void 0;
205
237
  const preserveWhitespace = options.preserveWhitespace ?? false;
206
- const schemas = getAgentExecutionOptions(snapshot.machine)?.schemas;
238
+ const eventSchemas = eventSchemasOf(snapshot);
207
239
  const accepted = new Set(getAcceptedEvents(snapshot).map((event) => event.type));
208
240
  const label = typeof interaction.label === "function" ? interaction.label({ context: snapshot.context }) : interaction.label ?? "";
209
241
  const events = Object.entries(interaction.events ?? {}).filter((entry) => entry[1] !== void 0 && accepted.has(entry[0])).map(([type, config]) => {
@@ -213,7 +245,7 @@ function getInteraction(snapshot, options = {}) {
213
245
  const complete = parseAgentEvent(snapshot, {
214
246
  ...descriptor.event,
215
247
  type
216
- }, { schemas });
248
+ }, { events: eventSchemas });
217
249
  return snapshot.can(complete);
218
250
  } catch {
219
251
  return true;
@@ -226,23 +258,61 @@ function getInteraction(snapshot, options = {}) {
226
258
  ...descriptor.event ? { event: descriptor.event } : {}
227
259
  };
228
260
  });
261
+ const declaresChoices = interaction.choices !== void 0;
262
+ const declared = typeof interaction.choices === "string" ? readPath(snapshot.context, interaction.choices) : interaction.choices;
263
+ const choices = (Array.isArray(declared) ? declared : []).filter(isChoice).filter((choice) => {
264
+ try {
265
+ const event = parseAgentEvent(snapshot, choice.event, { events: eventSchemas });
266
+ return snapshot.can(event);
267
+ } catch {
268
+ return false;
269
+ }
270
+ }).map((choice) => ({
271
+ label: interpolate(choice.label, snapshot.context, preserveWhitespace),
272
+ event: choice.event,
273
+ ...choice.style ? { style: choice.style } : {}
274
+ }));
275
+ const text = typeof interaction.textEvent === "string" ? {
276
+ type: interaction.textEvent,
277
+ field: "text"
278
+ } : interaction.textEvent;
229
279
  return {
230
280
  label: interpolate(label, snapshot.context, preserveWhitespace),
231
281
  events,
232
- ...interaction.textEvent && accepted.has(interaction.textEvent) ? { textEvent: interaction.textEvent } : {}
282
+ ...declaresChoices ? { choices } : {},
283
+ ...text && accepted.has(text.type) ? {
284
+ textEvent: text.type,
285
+ ...text.field !== "text" ? { textField: text.field } : {}
286
+ } : {}
233
287
  };
234
288
  }
235
- /** Converts a rendered interaction choice back into a schema-validated machine
236
- * event. Fixed fields declared in metadata are merged before user fields. */
289
+ /**
290
+ * Converts a rendered interaction answer back into a schema-validated machine
291
+ * event:
292
+ *
293
+ * - `{ type, ...fields }` — an `events` choice; fixed fields declared in
294
+ * metadata win over user fields.
295
+ * - `{ text }` — free text, placed in the `textEvent` field.
296
+ * - `{ choice }` — a payload choice, by its index in `getInteraction(snapshot).choices`
297
+ * or by its label. A label that several choices share throws; pass the index.
298
+ */
237
299
  function eventFromInteraction(snapshot, choice) {
238
300
  const interaction = getInteraction(snapshot);
239
301
  if (!interaction) throw new AgentInvalidEventPayloadError("(interaction)", "this state renders no interaction, so there is no choice to convert.");
240
302
  let event;
241
- if ("text" in choice) {
303
+ if (!("type" in choice) && "choice" in choice) {
304
+ const choices = interaction.choices ?? [];
305
+ const key = choice.choice;
306
+ const labeled = typeof key === "number" ? [] : choices.flatMap((candidate, index) => candidate.label === key ? [index] : []);
307
+ if (labeled.length > 1) throw new AgentInvalidEventPayloadError("(choice)", `${labeled.length} choices are labeled ${JSON.stringify(key)} (indexes ${labeled.join(", ")}); pass { choice: <index> } to pick one.`);
308
+ const picked = typeof key === "number" ? choices[key] : choices[labeled[0] ?? -1];
309
+ if (!picked) throw new AgentInvalidEventPayloadError("(choice)", `this interaction offers no choice ${JSON.stringify(key)}. Choices: ${choices.map(({ label }) => JSON.stringify(label)).join(", ") || "(none)"}.`);
310
+ event = { ...picked.event };
311
+ } else if ("text" in choice) {
242
312
  if (!interaction.textEvent) throw new AgentInvalidEventPayloadError("(text)", `this interaction takes no free text. Choices: ${interaction.events.map(({ type }) => type).join(", ") || "(none)"}.`);
243
313
  event = {
244
314
  type: interaction.textEvent,
245
- text: choice.text
315
+ [interaction.textField ?? "text"]: choice.text
246
316
  };
247
317
  } else {
248
318
  const descriptor = interaction.events.find(({ type }) => type === choice.type);
@@ -253,7 +323,452 @@ function eventFromInteraction(snapshot, choice) {
253
323
  type: choice.type
254
324
  };
255
325
  }
256
- return parseAgentEvent(snapshot, event);
326
+ return parseAgentEvent(snapshot, event, { events: eventSchemasOf(snapshot) });
327
+ }
328
+ //#endregion
329
+ //#region src/describe-machine.ts
330
+ /**
331
+ * A machine, described in plain text for a model to read: the prompt for a
332
+ * model that proposes an improved machine, or extra context for a decider.
333
+ *
334
+ * The text is read from the machine's own definition (`machineToJSON`):
335
+ * for a `setupAgent.fromConfig(...)` machine that is the lowered JSON config,
336
+ * so every target, guard and context patch is data; for a TS-authored machine,
337
+ * transitions written as functions are read from their source.
338
+ *
339
+ * @module
340
+ */
341
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
342
+ const toList = (value) => value === void 0 ? [] : Array.isArray(value) ? value : [value];
343
+ const oneLine = (text) => text.replace(/\s+/g, " ").trim();
344
+ /** The source of a function as serialized (`{ "@code" }`) or as a live function. */
345
+ function codeOf(value) {
346
+ if (typeof value === "function") return String(value);
347
+ if (isObject(value) && typeof value["@code"] === "string") return value["@code"];
348
+ }
349
+ /** The decoded value of a fromConfig `{ "@expr": "<json>" }` slot, or the value itself. */
350
+ function decodeExpr(value) {
351
+ if (isObject(value) && typeof value["@expr"] === "string") try {
352
+ return JSON.parse(value["@expr"]);
353
+ } catch {
354
+ return value["@expr"];
355
+ }
356
+ return value;
357
+ }
358
+ /** A target as written, resolved from the state the transition leaves: `#id`, `.child`, or a sibling (`sibling.child`). */
359
+ function resolveTarget(source, target) {
360
+ if (target.startsWith("#")) try {
361
+ return source.machine.getStateNodeById(target.slice(1));
362
+ } catch {
363
+ return;
364
+ }
365
+ const [from, path] = target.startsWith(".") ? [source, target.slice(1)] : [source.parent ?? source, target];
366
+ let node = from;
367
+ for (const key of path.split(".")) node = node?.states[key];
368
+ return node;
369
+ }
370
+ /** A target as said from where the transition starts: a sibling by key, else its path below the machine. */
371
+ const nameOf = (source, target) => target.parent === source.parent ? target.key : target.path.join(".");
372
+ /**
373
+ * The keys of the object literal whose `{` is at `open` in `text`, at its top
374
+ * level: `a`, `b` for `{ a: 1, b, ...c }`, and `…c` for a spread, whose keys
375
+ * cannot be read. Strings, templates and nested brackets are skipped over.
376
+ */
377
+ function objectKeys(text, open) {
378
+ const keys = [];
379
+ let depth = 0;
380
+ let expectKey = false;
381
+ for (let i = open; i < text.length; i++) {
382
+ const c = text[i];
383
+ if (c === "\"" || c === "'" || c === "`") {
384
+ const end = text.indexOf(c, i + 1);
385
+ if (end < 0) break;
386
+ i = end;
387
+ continue;
388
+ }
389
+ if (c === "{" || c === "(" || c === "[") {
390
+ depth++;
391
+ if (depth === 1) expectKey = true;
392
+ continue;
393
+ }
394
+ if (c === "}" || c === ")" || c === "]") {
395
+ if (--depth === 0) break;
396
+ continue;
397
+ }
398
+ if (depth !== 1) continue;
399
+ if (c === ",") {
400
+ expectKey = true;
401
+ continue;
402
+ }
403
+ if (expectKey && !/\s/.test(c)) {
404
+ expectKey = false;
405
+ if (text.startsWith("...", i)) {
406
+ const spread = /^\.\.\.\s*([\w$.]+)(\()?/.exec(text.slice(i));
407
+ keys.push(`…${spread?.[1] ?? ""}${spread?.[2] ? "()" : ""}`);
408
+ continue;
409
+ }
410
+ const key = /^[A-Za-z_$][\w$]*/.exec(text.slice(i))?.[0];
411
+ if (key) {
412
+ keys.push(key);
413
+ i += key.length - 1;
414
+ }
415
+ }
416
+ }
417
+ return keys;
418
+ }
419
+ /** Every key of each object literal that follows a match of `at` in `code`; `…name` when what follows is not a literal. */
420
+ function keysInSource(code, at, keys) {
421
+ for (const match of code.matchAll(at)) {
422
+ const open = match.index + match[0].length - 1;
423
+ if (code[open] === "{") for (const key of objectKeys(code, open)) keys.add(key);
424
+ else {
425
+ const from = /^[\w$.]+(\()?/.exec(code.slice(open));
426
+ keys.add(from ? `…${from[0]}${from[1] ? ")" : ""}` : "…");
427
+ }
428
+ }
429
+ }
430
+ /** What a transition sets in context: a declarative patch's keys, or each `context: { … }` in a function's source. */
431
+ function contextKeys(transition, code) {
432
+ const keys = /* @__PURE__ */ new Set();
433
+ if (code) keysInSource(code, /\bcontext\s*:\s*[^\s]/g, keys);
434
+ if (transition) {
435
+ const context = decodeExpr(transition.context);
436
+ const contextCode = codeOf(context);
437
+ if (contextCode) keysInSource(contextCode, /=>\s*\(\s*\{|\breturn\s*\{/g, keys);
438
+ else if (isObject(context)) for (const key of Object.keys(context)) keys.add(key);
439
+ for (const action of toList(transition.actions)) if (isObject(action) && action.type === "@xstate.assign") {
440
+ const assigned = decodeExpr(action.context);
441
+ if (isObject(assigned)) for (const key of Object.keys(assigned)) keys.add(key);
442
+ }
443
+ }
444
+ return [...keys];
445
+ }
446
+ /** A guard as said in the text: a template's dotted path, a named guard, or `guarded` when it is code. */
447
+ function guardText(guard) {
448
+ if (guard === void 0 || guard === null) return void 0;
449
+ const decoded = decodeExpr(guard);
450
+ if (typeof decoded === "string") return oneLine(/^\{\{\s*([\s\S]*?)\s*\}\}$/.exec(decoded)?.[1] ?? decoded);
451
+ if (isObject(decoded) && typeof decoded.type === "string") return decoded.type.startsWith("__fromConfigGuard") ? "guarded" : decoded.type;
452
+ return "guarded";
453
+ }
454
+ /**
455
+ * True when a transition function's source has a path that returns no
456
+ * transition, so it may not be taken. Matches minified source too: `void 0`
457
+ * for `undefined`, and a ternary `?` with no spaces (but not `?.` or `??`).
458
+ * String and template literals are blanked first, so their text never counts.
459
+ */
460
+ const mayDecline = (code) => /\bundefined\b|\bnull\b|\bvoid 0\b|\breturn\s*;|\bif\s*\(|(?<!\?)\?(?![.?])|&&/.test(code.replace(/(["'`])(?:\\.|(?!\1)[^\\])*\1/g, "\"\""));
461
+ /**
462
+ * Where a transition written as a function goes: every string literal after a
463
+ * `target:` that names a real state node, `(conditional)` when it may decline
464
+ * or pick among several, `(stays)` when its source has no `target`, `?` when
465
+ * it names none that resolves.
466
+ */
467
+ function targetsInSource(source, code, choice) {
468
+ const found = /* @__PURE__ */ new Set();
469
+ for (const [, expression] of code.matchAll(/\btarget\s*:\s*(\[[^\]]*\]|[^,}\n]+)/g)) for (const [, , literal] of expression.matchAll(/(['"`])([^'"`$]+)\1/g)) {
470
+ const node = resolveTarget(source, literal);
471
+ if (node) found.add(node);
472
+ }
473
+ const names = [...found].map((node) => nameOf(source, node));
474
+ if (names.length) {
475
+ const conditional = names.length > 1 || !choice && mayDecline(code);
476
+ return `${names.join(", ")}${conditional ? " (conditional)" : ""}`;
477
+ }
478
+ return /\btarget\s*:/.test(code) ? "?" : "(stays)";
479
+ }
480
+ /** One transition, as a line: `on GO if context.ready → review; sets draft — description`. */
481
+ function transitionLine(source, head, transition, options = {}) {
482
+ const code = codeOf(transition);
483
+ let to;
484
+ let guard;
485
+ let description;
486
+ let sets;
487
+ if (typeof transition === "string") {
488
+ const node = resolveTarget(source, transition);
489
+ to = node ? nameOf(source, node) : `${transition} ?`;
490
+ sets = [];
491
+ } else if (code !== void 0) {
492
+ to = targetsInSource(source, code, options.choice ?? false);
493
+ sets = contextKeys(void 0, code);
494
+ } else if (isObject(transition) && !("@expr" in transition)) {
495
+ const targets = toList(transition.target).filter((t) => typeof t === "string");
496
+ to = targets.length ? targets.map((target) => {
497
+ const node = resolveTarget(source, target);
498
+ return node ? nameOf(source, node) : `${target} ?`;
499
+ }).join(", ") : "(stays)";
500
+ guard = guardText(transition[options.guardKey ?? "guard"]);
501
+ description = typeof transition.description === "string" ? transition.description : void 0;
502
+ sets = contextKeys(transition, void 0);
503
+ } else {
504
+ to = "?";
505
+ sets = [];
506
+ }
507
+ return `${head}${guard ? ` if ${guard}` : ""} → ${to}${sets.length ? `; sets ${sets.join(", ")}` : ""}${description ? ` — ${oneLine(description)}` : ""}`;
508
+ }
509
+ function formatMs(ms) {
510
+ if (ms < 1e3) return `${ms}ms`;
511
+ return `${ms / 1e3}s`;
512
+ }
513
+ /** An `after` key as said: `1s`, or a named delay with its duration when it is a number (`slow (5s)`). */
514
+ function delayText(key, delays) {
515
+ if (/^\d+(\.\d+)?$/.test(key)) return formatMs(Number(key));
516
+ const delay = delays[key];
517
+ return typeof delay === "number" ? `${key} (${formatMs(delay)})` : key;
518
+ }
519
+ /** Every transition leaving one state node, as lines at `pad`. */
520
+ function transitionLines(node, json, delays, pad) {
521
+ const lines = [];
522
+ const push = (head, value) => {
523
+ for (const transition of toList(value)) lines.push(`${pad}${transitionLine(node, head, transition)}`);
524
+ };
525
+ for (const [event, value] of Object.entries(isObject(json.on) ? json.on : {})) push(event === "xstate.done.state" ? "onDone" : `on ${event}`, value);
526
+ push("onDone", json.onDone);
527
+ push("always", json.always);
528
+ for (const [key, value] of Object.entries(isObject(json.after) ? json.after : {})) push(`after ${delayText(key, delays)}`, value);
529
+ for (const invoke of toList(json.invoke)) {
530
+ if (!isObject(invoke)) continue;
531
+ const src = typeof invoke.src === "string" ? invoke.src : "(inline)";
532
+ const id = typeof invoke.id === "string" && invoke.id !== src ? ` (${invoke.id})` : "";
533
+ lines.push(`${pad}invoke ${src}${id}`);
534
+ push(" done", invoke.onDone);
535
+ push(" error", invoke.onError);
536
+ }
537
+ if (json.choice !== void 0) toList(json.choice).forEach((branch, index) => {
538
+ const head = isObject(branch) && branch.when !== void 0 || index === 0 ? "choice" : "choice else";
539
+ lines.push(`${pad}${transitionLine(node, head, branch, {
540
+ guardKey: "when",
541
+ choice: true
542
+ })}`);
543
+ });
544
+ return lines;
545
+ }
546
+ /** The state node's line: key, type, tags, interaction label, description. */
547
+ function stateLine(node, json) {
548
+ let marker = "";
549
+ if (node.type === "final" || node.type === "choice" || node.type === "parallel") marker = ` (${node.type})`;
550
+ else if (node.type === "history") marker = json.history === "deep" ? " (history, deep)" : " (history)";
551
+ else if (node.type === "compound" && typeof json.initial === "string") marker = ` (initial: ${json.initial})`;
552
+ const tags = node.tags.length ? ` [tags: ${node.tags.join(", ")}]` : "";
553
+ const interaction = node.meta?.interaction;
554
+ const label = typeof interaction?.label === "string" ? ` [interaction: "${oneLine(interaction.label)}"]` : "";
555
+ const description = node.description ? ` — ${oneLine(node.description)}` : "";
556
+ return `${node.key}${marker}${tags}${label}${description}`;
557
+ }
558
+ /** The machine's events with their schema descriptions and fields, sorted by type. */
559
+ function eventLines(machine) {
560
+ const schemas = getAgentExecutionOptions(machine)?.schemas?.events ?? machine.schemas?.events ?? {};
561
+ const lines = [];
562
+ for (const type of Object.keys(schemas).sort()) {
563
+ if (type.startsWith("@agent.") || type.startsWith("xstate.")) continue;
564
+ let schema;
565
+ try {
566
+ schema = getJsonSchemaSync(schemas[type]);
567
+ } catch {
568
+ schema = void 0;
569
+ }
570
+ const direct = schemas[type].description;
571
+ const description = typeof schema?.description === "string" ? schema.description : typeof direct === "string" ? direct : void 0;
572
+ lines.push(` ${type}${description ? ` — ${oneLine(description)}` : ""}`);
573
+ const properties = isObject(schema?.properties) ? schema.properties : {};
574
+ const required = new Set(Array.isArray(schema?.required) ? schema.required : []);
575
+ for (const [field, property] of Object.entries(properties)) {
576
+ if (!isObject(property)) continue;
577
+ const fieldType = Array.isArray(property.enum) ? property.enum.map((value) => JSON.stringify(value)).join(" | ") : Array.isArray(property.type) ? property.type.join(" | ") : typeof property.type === "string" ? property.type : void 0;
578
+ const fieldDescription = typeof property.description === "string" ? ` — ${oneLine(property.description)}` : "";
579
+ lines.push(` ${field}${required.has(field) ? "" : "?"}${fieldType ? `: ${fieldType}` : ""}${fieldDescription}`);
580
+ }
581
+ }
582
+ return lines;
583
+ }
584
+ /**
585
+ * Describes a machine in plain text for a model to read. The machine's
586
+ * `description` is its goal, so it leads: `Machine <id>`, then `Goal: …`.
587
+ * Then every state node, indented by depth, with its type, tags, interaction
588
+ * label and description (`*` marks the active ones when `snapshot` is given),
589
+ * and under it every transition leaving it, one per line: `on EVENT`,
590
+ * `always`, `after <delay>`, invoke `done`/`error`, and `choice` branches,
591
+ * each with its guard (`if …`), where it goes (`→ review`), and what it sets
592
+ * in context (`sets draft, attempts`). An `Events` section lists the
593
+ * machine's event schemas with their descriptions and fields.
594
+ *
595
+ * A transition written as a function shows the targets its source names
596
+ * (`→ review (conditional)` when it may decline or pick among several),
597
+ * `(stays)` when its source has no `target`, or `→ ?` when no target can be
598
+ * read. A `setupAgent.fromConfig(...)` machine is read from its config, so
599
+ * its targets, guards and context patches are always known.
600
+ *
601
+ * @example
602
+ * ```ts
603
+ * const prompt = `Propose a better machine for this goal.\n\n${describeMachine(machine)}`;
604
+ * ```
605
+ */
606
+ function describeMachine(machine, options = {}) {
607
+ const json = machineToJSON(machine);
608
+ const delays = isObject(json.delays) ? json.delays : {};
609
+ const active = new Set(options.snapshot?.nodes.map((node) => node.id) ?? []);
610
+ const root = machine.root;
611
+ const lines = [`Machine ${machine.id}`];
612
+ if (root.description) lines.push(`Goal: ${root.description.trim()}`);
613
+ const rootTransitions = transitionLines(root, json, delays, " ");
614
+ if (rootTransitions.length) lines.push("Any state:", ...rootTransitions);
615
+ const initial = typeof json.initial === "string" ? ` (initial: ${json.initial})` : "";
616
+ lines.push(`States${initial}${options.snapshot ? " (* = current)" : ""}:`);
617
+ const walk = (node, nodeJson, depth) => {
618
+ const pad = " ".repeat(depth);
619
+ const mark = active.has(node.id) ? `${pad.slice(2)}* ` : pad;
620
+ lines.push(`${mark}${stateLine(node, nodeJson)}`);
621
+ lines.push(...transitionLines(node, nodeJson, delays, `${pad} `));
622
+ const childJson = isObject(nodeJson.states) ? nodeJson.states : {};
623
+ for (const [key, child] of Object.entries(node.states)) walk(child, isObject(childJson[key]) ? childJson[key] : {}, depth + 1);
624
+ };
625
+ const rootStates = isObject(json.states) ? json.states : {};
626
+ for (const [key, child] of Object.entries(root.states)) walk(child, isObject(rootStates[key]) ? rootStates[key] : {}, 1);
627
+ const events = eventLines(machine);
628
+ if (events.length) lines.push("Events:", ...events);
629
+ return lines.join("\n");
630
+ }
631
+ //#endregion
632
+ //#region src/decide-next.ts
633
+ /**
634
+ * Asks a model to choose the next event for ANY XState snapshot, including a
635
+ * machine built with plain `createMachine`/`setup` that knows nothing about AI.
636
+ * The candidates are the `allowedEvents` an active state handles, one option per
637
+ * payload, kept only where `snapshot.can(event)` is true; the rest come back as
638
+ * `withheld`. Each option is
639
+ * described from the transition's `description`, else the event schema's,
640
+ * plus (with `lookahead`) what it changes. The chosen event must equal one
641
+ * offered option; anything else is retried.
642
+ *
643
+ * It never sends. The host sends the event, re-checking `can` first, because
644
+ * the actor may have moved while the model was thinking. With no options it
645
+ * returns `{ event: null }` without calling the executor. Exhausting retries
646
+ * throws `AgentDecisionExhaustedError`.
647
+ *
648
+ * @example
649
+ * ```ts no-check
650
+ * let busy = false;
651
+ * actor.subscribe(async (snapshot) => {
652
+ * if (busy || !snapshot.matches('reviewing')) return;
653
+ * busy = true;
654
+ * try {
655
+ * const { event } = await decideNext(snapshot, {
656
+ * decide,
657
+ * model: 'quick',
658
+ * allowedEvents: 'desk.*',
659
+ * payloads: (s) => ({ 'desk.refund': [{ amount: s.context.requested }] }),
660
+ * lookahead: true,
661
+ * });
662
+ * if (event && actor.getSnapshot().can(event)) actor.send(event);
663
+ * } finally {
664
+ * busy = false;
665
+ * }
666
+ * });
667
+ * ```
668
+ */
669
+ async function decideNext(snapshot, options) {
670
+ const patterns = typeof options.allowedEvents === "string" ? [options.allowedEvents] : options.allowedEvents;
671
+ const schemas = snapshot.machine.schemas?.events;
672
+ const events = getAcceptedEvents(snapshot, {
673
+ events: schemas,
674
+ eventTypes: patterns
675
+ }).filter((event) => !isEventPattern(event.type));
676
+ const payloads = options.payloads?.(snapshot) ?? {};
677
+ const accepted = [];
678
+ const withheld = [];
679
+ for (const { type } of events) for (const payload of payloads[type] ?? [{}]) {
680
+ const event = {
681
+ ...payload,
682
+ type
683
+ };
684
+ (safeCan(snapshot, event) ? accepted : withheld).push(event);
685
+ }
686
+ const offered = accepted.map((event) => ({
687
+ event,
688
+ description: describeOption(snapshot, event, schemas?.[event.type], options.lookahead)
689
+ }));
690
+ const request = createDecisionRequest({
691
+ name: options.name ?? "decideNext",
692
+ model: options.model,
693
+ system: options.system,
694
+ prompt: options.prompt,
695
+ input: {
696
+ value: snapshot.value,
697
+ context: snapshot.context
698
+ },
699
+ events,
700
+ options: offered,
701
+ withheld
702
+ });
703
+ if (offered.length === 0) return {
704
+ event: null,
705
+ attempts: [],
706
+ request,
707
+ withheld
708
+ };
709
+ let attempts = [];
710
+ return {
711
+ event: await resolveDecision(request, { decide: (attemptRequest, info) => {
712
+ attempts = attemptRequest.attempts;
713
+ return options.decide(attemptRequest, info);
714
+ } }, {
715
+ maxRetries: options.maxRetries,
716
+ signal: options.signal,
717
+ canTake: (chosen) => safeCan(snapshot, chosen)
718
+ }),
719
+ attempts,
720
+ request,
721
+ withheld
722
+ };
723
+ }
724
+ function safeCan(snapshot, event) {
725
+ try {
726
+ return snapshot.can(event);
727
+ } catch {
728
+ return false;
729
+ }
730
+ }
731
+ function describeOption(snapshot, event, schema, lookahead) {
732
+ const what = getNextTransitions(snapshot).find((candidate) => candidate.eventType === event.type && candidate.description)?.description ?? schemaDescription(schema) ?? event.type;
733
+ const fields = Object.entries(event).filter(([key]) => key !== "type").map(([key, value]) => `${key}: ${short(value)}`);
734
+ const base = fields.length ? `${what} (${fields.join("; ")})` : what;
735
+ const effect = lookahead ? describeEffect(snapshot, event, lookahead === true ? void 0 : lookahead) : void 0;
736
+ return effect ? `${base}. Then: ${effect}` : base;
737
+ }
738
+ function schemaDescription(schema) {
739
+ if (!schema) return void 0;
740
+ const direct = schema.description;
741
+ if (typeof direct === "string") return direct;
742
+ try {
743
+ const json = schema["~standard"].jsonSchema?.input?.({ target: "draft-2020-12" });
744
+ return isRecord(json) && typeof json.description === "string" ? json.description : void 0;
745
+ } catch {
746
+ return;
747
+ }
748
+ }
749
+ function describeEffect(snapshot, event, keys) {
750
+ const maxLength = 120;
751
+ let next;
752
+ try {
753
+ [next] = transition(snapshot.machine, snapshot, event);
754
+ } catch {
755
+ return;
756
+ }
757
+ const changes = [];
758
+ if (canonicalEventJson(snapshot.value) !== canonicalEventJson(next.value)) changes.push(`state: ${short(snapshot.value, maxLength)} → ${short(next.value, maxLength)}`);
759
+ const before = isRecord(snapshot.context) ? snapshot.context : {};
760
+ const after = isRecord(next.context) ? next.context : {};
761
+ for (const key of keys ?? /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) if (canonicalEventJson(before[key]) !== canonicalEventJson(after[key])) changes.push(`${key}: ${short(before[key], maxLength)} → ${short(after[key], maxLength)}`);
762
+ return changes.length ? changes.join("; ") : "nothing changes";
763
+ }
764
+ function short(value, max = 60) {
765
+ let text;
766
+ try {
767
+ text = value === void 0 ? "(none)" : JSON.stringify(value) ?? String(value);
768
+ } catch {
769
+ text = String(value);
770
+ }
771
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
257
772
  }
258
773
  //#endregion
259
774
  //#region src/provide-executors.ts
@@ -309,8 +824,8 @@ function provideExecutors(machine, executors, options = {}) {
309
824
  const invokedSrcs = getConfiguredInvokeSrcs(provided);
310
825
  for (const [key, logic] of Object.entries(effectiveSources)) {
311
826
  if (isStateMachineLogic(logic)) {
312
- if (invokedSrcs.has(key)) assertChildBindable(logic, executors, key, new Set([provided]));
313
- const rebound = bindChildMachineForProvide(logic, executors, bindOptions, new Set([provided]));
827
+ if (invokedSrcs.has(key)) assertChildBindable(logic, executors, key, /* @__PURE__ */ new Set([provided]));
828
+ const rebound = bindChildMachineForProvide(logic, executors, bindOptions, /* @__PURE__ */ new Set([provided]));
314
829
  if (rebound !== logic) wrappedSources[key] = rebound;
315
830
  continue;
316
831
  }
@@ -351,7 +866,7 @@ function executorRequirementOf(logic) {
351
866
  */
352
867
  function assertChildBindable(childMachine, executors, path, visited) {
353
868
  if (visited.has(childMachine)) return;
354
- const nextVisited = new Set([...visited, childMachine]);
869
+ const nextVisited = /* @__PURE__ */ new Set([...visited, childMachine]);
355
870
  const sources = childMachine.sources.actors;
356
871
  for (const src of getConfiguredInvokeSrcs(childMachine)) {
357
872
  const logic = sources[src];
@@ -369,4 +884,4 @@ function missingExecutorError(src, kind, executor) {
369
884
  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.`);
370
885
  }
371
886
  //#endregion
372
- export { AGENT_TRACE_SCHEMA_VERSION, AGENT_USAGE_EVENT_TYPE, AgentDecisionExhaustedError, AgentError, AgentEventLogConflictError, AgentInvalidEventPayloadError, AgentMachineVersionMismatchError, AgentMaxModelCallsExceededError, AgentSnapshotDivergedError, AgentTruncatedError, assistantMessage, bindRequestExecutor, createAgentRuntime, createAgentSchemas, createDecisionLogic, createDecisionRequest, createTextLogic, eventFromInteraction, getAcceptedEvents, getAgentOutputMode, getAgentSchemas, getCallFinishReason, getCallUsage, getInteraction, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, getMessageText, getStateMeta, getStatePath, inspectTransitions, interactionMetaSchema, isAgentIdle, isAgentMessages, isStandardSchema, messagesSchema, parseAgentEvent, parseOutput, parseProviderOutput, provideExecutors, providerOutputSchema, renderDecisionAttempts, resolveDecision, runToQuiescence, serializeTraceEvent, setupAgent, systemMessage, toolMessage, traceTransitions, userMessage };
887
+ export { AGENT_TRACE_SCHEMA_VERSION, AGENT_USAGE_EVENT_TYPE, AgentDecisionExhaustedError, AgentError, AgentEventLogConflictError, AgentInvalidEventPayloadError, AgentMachineVersionMismatchError, AgentMaxModelCallsExceededError, AgentSnapshotDivergedError, AgentTruncatedError, assistantMessage, bindRequestExecutor, createAgentRuntime, createAgentSchemas, createDecisionLogic, createDecisionRequest, createTextLogic, decideNext, describeMachine, eventFromInteraction, getAcceptedEvents, getAgentOutputMode, getAgentSchemas, getCallFinishReason, getCallUsage, getInteraction, getJsonSchema, getJsonSchemaSync, getMachineStructuralHash, getMessageText, getStateMeta, getStatePath, inspectTransitions, interactionMetaSchema, isAgentIdle, isAgentMessages, isStandardSchema, messagesSchema, parseAgentEvent, parseOutput, parseProviderOutput, provideExecutors, providerOutputSchema, renderDecisionAttempts, resolveDecision, runAgent, runToQuiescence, serializeTraceEvent, setupAgent, systemMessage, toolMessage, traceTransitions, userMessage };