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

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 (69) hide show
  1. package/CHANGELOG.md +1733 -0
  2. package/dist/ai-sdk.cjs +9 -9
  3. package/dist/ai-sdk.d.cts +5 -5
  4. package/dist/ai-sdk.d.mts +5 -5
  5. package/dist/ai-sdk.mjs +4 -4
  6. package/dist/{decision-Bl_rKQnU.mjs → decision-DqO018Tp.mjs} +23 -10
  7. package/dist/{decision-oevZnkBe.cjs → decision-Iys2hzg3.cjs} +31 -18
  8. package/dist/{event-log-Dtb16p1p.cjs → event-log-Bdww2l1D.cjs} +104 -25
  9. package/dist/{event-log-GPZO9p7_.mjs → event-log-DE430ZCa.mjs} +87 -26
  10. package/dist/{event-log-store-bCdskPNS.cjs → event-log-store-BWMLi6oh.cjs} +1 -1
  11. package/dist/{event-log-store-yalq8C77.mjs → event-log-store-CCWdTPEP.mjs} +1 -1
  12. package/dist/{event-log-store-CN9ygggF.d.cts → event-log-store-DTf_OzB-.d.cts} +1 -1
  13. package/dist/{event-log-store-DS836wch.d.mts → event-log-store-RziIobdW.d.mts} +1 -1
  14. package/dist/index.cjs +136 -23
  15. package/dist/index.d.cts +58 -11
  16. package/dist/index.d.mts +58 -11
  17. package/dist/index.mjs +133 -21
  18. package/dist/log.cjs +2 -2
  19. package/dist/log.d.cts +1 -1
  20. package/dist/log.d.mts +1 -1
  21. package/dist/log.mjs +2 -2
  22. package/dist/machines.cjs +1 -1
  23. package/dist/machines.mjs +1 -1
  24. package/dist/{model-source-C7FFP2Sj.mjs → model-source-CXsSZPq0.mjs} +1 -2
  25. package/dist/{model-source-DjJPjGSP.cjs → model-source-DQ7KgJaT.cjs} +4 -5
  26. package/dist/openai.cjs +3 -3
  27. package/dist/openai.d.cts +2 -2
  28. package/dist/openai.d.mts +2 -2
  29. package/dist/openai.mjs +3 -3
  30. package/dist/otel.d.cts +1 -1
  31. package/dist/otel.d.mts +1 -1
  32. package/dist/{run-agent-C9ubX8Tx.d.cts → run-agent-B5opVUxx.d.cts} +25 -6
  33. package/dist/{run-agent-CxSyZmIE.d.mts → run-agent-D4MwS5GY.d.mts} +25 -6
  34. package/dist/{run-agent-CuQT1xSS.mjs → run-agent-DWZ1uyBJ.mjs} +100 -37
  35. package/dist/{run-agent-cToZ3K42.cjs → run-agent-tmk58M0x.cjs} +100 -37
  36. package/dist/{setup-agent-DniLvDu7.mjs → setup-agent-BaffhCcM.mjs} +38 -3
  37. package/dist/{setup-agent-tsc3vm5I.d.mts → setup-agent-CPyg2ic7.d.mts} +11 -3
  38. package/dist/{setup-agent-DTF3NQg3.cjs → setup-agent-D0L8Anl_.cjs} +38 -3
  39. package/dist/{setup-agent-DlbgwsIJ.d.cts → setup-agent-yFFVXf5s.d.cts} +11 -3
  40. package/dist/tanstack-ai.cjs +3 -3
  41. package/dist/tanstack-ai.d.cts +2 -2
  42. package/dist/tanstack-ai.d.mts +2 -2
  43. package/dist/tanstack-ai.mjs +3 -3
  44. package/dist/testing.cjs +176 -26
  45. package/dist/testing.d.cts +128 -10
  46. package/dist/testing.d.mts +128 -10
  47. package/dist/testing.mjs +168 -20
  48. package/dist/{text-logic-BDbGjYMh.d.mts → text-logic-BwdrDpO9.d.mts} +6 -2
  49. package/dist/{text-logic-9fv1i9aK.d.cts → text-logic-j9hShB7d.d.cts} +6 -2
  50. package/dist/{usage-D8UphgEA.d.mts → usage-BYqv7VBw.d.cts} +1 -1
  51. package/dist/{usage-R1NTmLxY.d.cts → usage-Bl4nK8D-.d.mts} +1 -1
  52. package/dist/validate.cjs +3 -3
  53. package/dist/validate.d.cts +2 -2
  54. package/dist/validate.d.mts +2 -2
  55. package/dist/validate.mjs +3 -3
  56. package/dist/{verify-B5qGC5WN.mjs → verify-BH5e0XLK.mjs} +55 -5
  57. package/dist/{verify-DYH_2VzU.d.cts → verify-CPKDPMwY.d.cts} +14 -6
  58. package/dist/{verify-Cqghu4gz.cjs → verify-CS_xxvVU.cjs} +57 -7
  59. package/dist/{verify-CBuYIpsa.d.mts → verify-tkymWz6X.d.mts} +14 -6
  60. package/package.json +34 -4
  61. package/readme.md +72 -42
  62. package/skills/add-evals/SKILL.md +144 -0
  63. package/skills/add-traces/SKILL.md +147 -0
  64. package/skills/generate-machine/SKILL.md +6 -6
  65. package/skills/graph-engineering/SKILL.md +33 -0
  66. package/skills/improve-machine/SKILL.md +73 -21
  67. package/skills/improve-machine/reference.md +12 -7
  68. package/skills/loop-to-machine/SKILL.md +210 -0
  69. package/skills/loop-to-machine/policy.md +154 -0
package/dist/testing.mjs CHANGED
@@ -1,7 +1,6 @@
1
- import { $ as getStateMeta, F as normalizeGeneratorResult, N as getCallUsage, st as AgentError } from "./event-log-GPZO9p7_.mjs";
2
- import { m as isRecord } from "./decision-Bl_rKQnU.mjs";
3
- import { p as runToQuiescence, s as createAgentRuntime } from "./run-agent-CuQT1xSS.mjs";
4
- import { a as explorePaths, i as canReach, n as AgentUnknownStateError, o as lintAgentMachine, r as assertAgentMachine, s as simulateAgent, t as AgentLintError } from "./verify-B5qGC5WN.mjs";
1
+ import { $ as getStateMeta, F as normalizeGeneratorResult, N as getCallUsage, lt as isRecord, ut as AgentError } from "./event-log-DE430ZCa.mjs";
2
+ import { p as runToQuiescence, s as createAgentRuntime } from "./run-agent-DWZ1uyBJ.mjs";
3
+ import { a as explorePaths, i as canReach, n as AgentUnknownStateError, o as lintAgentMachine, r as assertAgentMachine, s as simulateAgent, t as AgentLintError } from "./verify-BH5e0XLK.mjs";
5
4
  //#region src/trajectory.ts
6
5
  /**
7
6
  * Classifies one item. Event-shaped things (log entries, events) become events;
@@ -320,7 +319,7 @@ async function runSeam(machine, options) {
320
319
  ...event ? { event } : {}
321
320
  });
322
321
  if (result.status !== "idle" || turn === maxTurns) break;
323
- const next = options.respond?.({
322
+ const next = await options.respond?.({
324
323
  snapshot: result.snapshot,
325
324
  state: result.snapshot.value,
326
325
  meta: getStateMeta(result.snapshot),
@@ -360,13 +359,36 @@ async function runSeam(machine, options) {
360
359
  };
361
360
  }
362
361
  //#endregion
362
+ //#region src/internal/trace-states.ts
363
+ /** The `value` of a trace event's `from` or `snapshot`. @internal */
364
+ function valueOf(holder) {
365
+ return isRecord(holder) ? holder.value : void 0;
366
+ }
367
+ /** A decision request on its first attempt (no failed attempts yet). @internal */
368
+ function isFirstDecision(request) {
369
+ return isRecord(request) && request.kind === "decision" && (!Array.isArray(request.attempts) || request.attempts.length === 0);
370
+ }
371
+ /**
372
+ * Every active leaf of a state value as a dot path:
373
+ * `'a'` → `['a']`; `{ a: 'b' }` → `['a.b']`; `{ p: { x: 'one', y: {} } }` → `['p.x.one', 'p.y']`.
374
+ * @internal
375
+ */
376
+ function statePaths(value, prefix = "") {
377
+ if (typeof value === "string") return [prefix + value];
378
+ if (!isRecord(value)) return [];
379
+ const entries = Object.entries(value);
380
+ if (entries.length === 0) return prefix ? [prefix.slice(0, -1)] : [];
381
+ return entries.flatMap(([key, child]) => statePaths(child, `${prefix}${key}.`));
382
+ }
383
+ //#endregion
363
384
  //#region src/fold-traces.ts
364
385
  /**
365
386
  * Folds many runs' traces into per-state stats: how many runs visited each
366
- * state, how many of those failed, and how many options the guards withheld
367
- * at decisions made there. Pure; reads only:
387
+ * state, how many of those failed, how many options the guards withheld
388
+ * at decisions made there, and how many events it ignored. Pure; reads only:
368
389
  *
369
- * - `machine.transition`: the run visited `from.value` and `snapshot.value`.
390
+ * - `machine.transition`: the run visited `from.value` and `snapshot.value`;
391
+ * with `ignored`, each of those states ignored one event.
370
392
  * - `request.start` of a decision, first attempt only: its `request.withheld`
371
393
  * counts toward the state the run was in, the `from.value` of the
372
394
  * transition its choice causes.
@@ -392,7 +414,8 @@ function foldTraces(traces, options) {
392
414
  state,
393
415
  visits: 0,
394
416
  failures: 0,
395
- withheld: 0
417
+ withheld: 0,
418
+ ignored: 0
396
419
  };
397
420
  stats.set(state, entry);
398
421
  }
@@ -409,6 +432,7 @@ function foldTraces(traces, options) {
409
432
  for (const state of statePaths(valueOf(event.from))) visit(state);
410
433
  current = statePaths(valueOf(event.snapshot));
411
434
  for (const state of current) visit(state);
435
+ if (event.ignored === true) for (const state of current) statsFor(state).ignored++;
412
436
  } else if (event.type === "request.start" && isFirstDecision(event.request)) {
413
437
  const withheld = event.request.withheld;
414
438
  if (!Array.isArray(withheld)) continue;
@@ -423,18 +447,142 @@ function foldTraces(traces, options) {
423
447
  }
424
448
  return [...stats.values()];
425
449
  }
426
- function valueOf(holder) {
427
- return isRecord(holder) ? holder.value : void 0;
450
+ //#endregion
451
+ //#region src/decision-requests.ts
452
+ /**
453
+ * The decisions a run asked for, read from its trace: each decision's first
454
+ * `request.start`, with the states the run was in. A trace is one run's events,
455
+ * live or serialized with `serializeTraceEvent`. Retries of the same decision
456
+ * are left out, so each decision appears once.
457
+ *
458
+ * Use the requests as eval rows (run a decider on each and compare its event
459
+ * with a label) or as probe inputs for a policy. A serialized request keeps its
460
+ * `options`, `metadata`, `system`, and `prompt`; event schemas do not survive
461
+ * serialization, so replay closed choices (`options`) rather than open ones.
462
+ *
463
+ * @example
464
+ * ```ts no-check
465
+ * const rows = traces.flatMap((trace) => getDecisionRequests(trace, { name: "chooseResolution" }));
466
+ * for (const { request } of rows) console.log((await decide(request)).event);
467
+ * ```
468
+ */
469
+ function getDecisionRequests(trace, options = {}) {
470
+ const requests = [];
471
+ let current = [];
472
+ for (const event of trace) if (event.type === "machine.transition") current = statePaths(valueOf(event.snapshot));
473
+ else if (event.type === "request.start" && isFirstDecision(event.request)) {
474
+ if (options.name !== void 0 && event.request.name !== options.name) continue;
475
+ requests.push({
476
+ states: [...current],
477
+ request: event.request
478
+ });
479
+ }
480
+ return requests;
428
481
  }
429
- function isFirstDecision(request) {
430
- return isRecord(request) && request.kind === "decision" && (!Array.isArray(request.attempts) || request.attempts.length === 0);
482
+ //#endregion
483
+ //#region src/compare-runs.ts
484
+ /**
485
+ * Runs every scenario through a baseline and a candidate implementation and
486
+ * labels each difference, so a port can show where it behaves like the code it
487
+ * replaces and where it differs on purpose.
488
+ *
489
+ * Parity is outcome-level: the terminal outcome and the effects in the world,
490
+ * not the sequence of tool calls, since a machine usually reorders steps. A
491
+ * candidate that broke no rule where the baseline broke one is intended, even
492
+ * when the outcome matches. A difference also counts as intended when only the
493
+ * candidate reached the scenario's `expected` outcome. Every other difference,
494
+ * and every candidate rule break, is a regression for a person to review.
495
+ *
496
+ * Runs are sequential, baseline first. A run that throws is recorded with
497
+ * outcome `'threw'` and the error message as its `detail`. Parity holds for
498
+ * these scenarios and these runs only; with a live model, run it more than once.
499
+ *
500
+ * @example
501
+ * ```ts no-check
502
+ * const report = await compareRuns({
503
+ * scenarios,
504
+ * baseline: async (s) => toParityRun(await runRawLoop(s)),
505
+ * candidate: async (s) => toParityRun(await runDesk(s)),
506
+ * });
507
+ * expect(report.ok).toBe(true);
508
+ * ```
509
+ */
510
+ async function compareRuns(options) {
511
+ const scenarios = [];
512
+ const counts = {
513
+ same: 0,
514
+ intended: 0,
515
+ regression: 0
516
+ };
517
+ for (const scenario of options.scenarios) {
518
+ const baseline = await settle(() => options.baseline(scenario));
519
+ const candidate = await settle(() => options.candidate(scenario));
520
+ const { label, reason } = labelRun(scenario.expected, baseline, candidate);
521
+ counts[label]++;
522
+ scenarios.push({
523
+ id: scenario.id,
524
+ scenario,
525
+ label,
526
+ reason,
527
+ baseline,
528
+ candidate
529
+ });
530
+ }
531
+ return {
532
+ scenarios,
533
+ counts,
534
+ ok: counts.regression === 0
535
+ };
431
536
  }
432
- function statePaths(value, prefix = "") {
433
- if (typeof value === "string") return [prefix + value];
434
- if (!isRecord(value)) return [];
435
- const entries = Object.entries(value);
436
- if (entries.length === 0) return prefix ? [prefix.slice(0, -1)] : [];
437
- return entries.flatMap(([key, child]) => statePaths(child, `${prefix}${key}.`));
537
+ async function settle(run) {
538
+ try {
539
+ return await run();
540
+ } catch (error) {
541
+ return {
542
+ outcome: "threw",
543
+ detail: error instanceof Error ? error.message : String(error)
544
+ };
545
+ }
546
+ }
547
+ function labelRun(expected, baseline, candidate) {
548
+ if (candidate.violations?.length) return {
549
+ label: "regression",
550
+ reason: `candidate broke a rule: ${list(candidate.violations)}`
551
+ };
552
+ if (candidate.outcome === "threw") return {
553
+ label: "regression",
554
+ reason: `candidate threw: ${String(candidate.detail)}`
555
+ };
556
+ if (baseline.violations?.length) return {
557
+ label: "intended",
558
+ reason: `baseline broke a rule: ${list(baseline.violations)}`
559
+ };
560
+ const sameOutcome = baseline.outcome === candidate.outcome;
561
+ if (sameOutcome && structurallyEqual(baseline.effects, candidate.effects)) return {
562
+ label: "same",
563
+ reason: ""
564
+ };
565
+ if (expected !== void 0 && candidate.outcome === expected && baseline.outcome !== expected) return {
566
+ label: "intended",
567
+ reason: `only the candidate reached the expected '${expected}'`
568
+ };
569
+ return {
570
+ label: "regression",
571
+ reason: sameOutcome ? "effects differ" : `outcome differs: ${baseline.outcome} → ${candidate.outcome}`
572
+ };
573
+ }
574
+ function list(items) {
575
+ return items.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join(", ");
576
+ }
577
+ function structurallyEqual(a, b) {
578
+ if (Object.is(a, b)) return true;
579
+ if (a instanceof Date || b instanceof Date) return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
580
+ if (a instanceof Map || b instanceof Map) return a instanceof Map && b instanceof Map && structurallyEqual([...a], [...b]);
581
+ if (a instanceof Set || b instanceof Set) return a instanceof Set && b instanceof Set && structurallyEqual([...a], [...b]);
582
+ if (Array.isArray(a) || Array.isArray(b)) return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => structurallyEqual(item, b[index]));
583
+ if (!isRecord(a) || !isRecord(b)) return false;
584
+ const keys = Object.keys(a);
585
+ return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && structurallyEqual(a[key], b[key]));
438
586
  }
439
587
  //#endregion
440
- export { AgentLintError, AgentUnknownStateError, assertAgentMachine, canReach, explorePaths, foldTraces, lintAgentMachine, matchesTrajectory, runSeam, simulateAgent };
588
+ export { AgentLintError, AgentUnknownStateError, assertAgentMachine, canReach, compareRuns, explorePaths, foldTraces, getDecisionRequests, lintAgentMachine, matchesTrajectory, runSeam, simulateAgent };
@@ -130,9 +130,13 @@ declare function parseAgentEvent<TSource extends AnyStateMachine | AnyMachineSna
130
130
  * ({@link RESERVED_AGENT_EVENT_PREFIX}) event types are always excluded, before
131
131
  * any `allowedEvents` matching — a machine that handles `'@agent.usage'` still
132
132
  * never offers it to a model.
133
+ *
134
+ * Pass `actor: "agent"` for the list a model may choose from: it also drops an
135
+ * event the state handles through a transition tagged `meta: { actor: "human" }`.
133
136
  */
134
137
  declare function getAcceptedEvents(snapshot: AnyMachineSnapshot, options?: Pick<AgentRequestOptions, "events" | "schemas"> & {
135
138
  eventTypes?: readonly string[];
139
+ actor?: "agent";
136
140
  }): AgentEventDescriptor[];
137
141
  //#endregion
138
142
  //#region src/decision.d.ts
@@ -228,7 +232,7 @@ interface DecisionLogic<TInputSchema extends StandardSchemaV1 = StandardSchemaV1
228
232
  *
229
233
  * export const chooseMove = createDecisionLogic({
230
234
  * schemas: { input: z.object({ playerHp: z.number(), enemyHp: z.number() }) },
231
- * model: 'openai/gpt-5.4-mini',
235
+ * model: 'openai/gpt-6-luna',
232
236
  * system: 'You are playing a turn-based game. Choose exactly one legal move.',
233
237
  * prompt: ({ input }) => `Player HP: ${input.playerHp}\nEnemy HP: ${input.enemyHp}`,
234
238
  * allowedEvents: ({ input }) =>
@@ -733,7 +737,7 @@ interface TextLogic<TInputSchema extends StandardSchemaV1 = StandardSchemaV1, TO
733
737
  * export const tellJoke = createTextLogic({
734
738
  * mode: 'stream',
735
739
  * schemas: { input: z.object({ topic: z.string() }), output: z.string() },
736
- * model: 'openai/gpt-5.4-mini',
740
+ * model: 'openai/gpt-6-luna',
737
741
  * system: 'You tell short, punchy jokes.',
738
742
  * prompt: ({ input }) => `Tell a joke about ${input.topic}.`,
739
743
  * });
@@ -130,9 +130,13 @@ declare function parseAgentEvent<TSource extends AnyStateMachine | AnyMachineSna
130
130
  * ({@link RESERVED_AGENT_EVENT_PREFIX}) event types are always excluded, before
131
131
  * any `allowedEvents` matching — a machine that handles `'@agent.usage'` still
132
132
  * never offers it to a model.
133
+ *
134
+ * Pass `actor: "agent"` for the list a model may choose from: it also drops an
135
+ * event the state handles through a transition tagged `meta: { actor: "human" }`.
133
136
  */
134
137
  declare function getAcceptedEvents(snapshot: AnyMachineSnapshot, options?: Pick<AgentRequestOptions, "events" | "schemas"> & {
135
138
  eventTypes?: readonly string[];
139
+ actor?: "agent";
136
140
  }): AgentEventDescriptor[];
137
141
  //#endregion
138
142
  //#region src/decision.d.ts
@@ -228,7 +232,7 @@ interface DecisionLogic<TInputSchema extends StandardSchemaV1 = StandardSchemaV1
228
232
  *
229
233
  * export const chooseMove = createDecisionLogic({
230
234
  * schemas: { input: z.object({ playerHp: z.number(), enemyHp: z.number() }) },
231
- * model: 'openai/gpt-5.4-mini',
235
+ * model: 'openai/gpt-6-luna',
232
236
  * system: 'You are playing a turn-based game. Choose exactly one legal move.',
233
237
  * prompt: ({ input }) => `Player HP: ${input.playerHp}\nEnemy HP: ${input.enemyHp}`,
234
238
  * allowedEvents: ({ input }) =>
@@ -733,7 +737,7 @@ interface TextLogic<TInputSchema extends StandardSchemaV1 = StandardSchemaV1, TO
733
737
  * export const tellJoke = createTextLogic({
734
738
  * mode: 'stream',
735
739
  * schemas: { input: z.object({ topic: z.string() }), output: z.string() },
736
- * model: 'openai/gpt-5.4-mini',
740
+ * model: 'openai/gpt-6-luna',
737
741
  * system: 'You tell short, punchy jokes.',
738
742
  * prompt: ({ input }) => `Tell a joke about ${input.topic}.`,
739
743
  * });
@@ -1,4 +1,4 @@
1
- import { t as AgentCallUsage } from "./text-logic-BDbGjYMh.mjs";
1
+ import { t as AgentCallUsage } from "./text-logic-j9hShB7d.cjs";
2
2
  import { EventObject } from "xstate";
3
3
  //#region src/usage.d.ts
4
4
  /** Reserved event delivered after a model call reports token usage. */
@@ -1,4 +1,4 @@
1
- import { t as AgentCallUsage } from "./text-logic-9fv1i9aK.cjs";
1
+ import { t as AgentCallUsage } from "./text-logic-BwdrDpO9.mjs";
2
2
  import { EventObject } from "xstate";
3
3
  //#region src/usage.d.ts
4
4
  /** Reserved event delivered after a model call reports token usage. */
package/dist/validate.cjs CHANGED
@@ -21,9 +21,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  enumerable: true
22
22
  }) : target, mod));
23
23
  //#endregion
24
- const require_event_log = require("./event-log-Dtb16p1p.cjs");
25
- const require_setup_agent = require("./setup-agent-DTF3NQg3.cjs");
26
- const require_verify = require("./verify-Cqghu4gz.cjs");
24
+ const require_event_log = require("./event-log-Bdww2l1D.cjs");
25
+ const require_setup_agent = require("./setup-agent-D0L8Anl_.cjs");
26
+ const require_verify = require("./verify-CS_xxvVU.cjs");
27
27
  let ajv_dist_2020_js = require("ajv/dist/2020.js");
28
28
  ajv_dist_2020_js = __toESM(ajv_dist_2020_js, 1);
29
29
  //#region schemas/agent-workflow.json
@@ -1,5 +1,5 @@
1
- import { d as AgentWorkflowConfig, g as FromConfigOptions, v as SchemaCompiler } from "./setup-agent-DlbgwsIJ.cjs";
2
- import { l as ExplorePathsOptions } from "./verify-DYH_2VzU.cjs";
1
+ import { d as AgentWorkflowConfig, g as FromConfigOptions, v as SchemaCompiler } from "./setup-agent-yFFVXf5s.cjs";
2
+ import { u as ExplorePathsOptions } from "./verify-CPKDPMwY.cjs";
3
3
  import { AnyActorLogic, AnyStateMachine } from "xstate";
4
4
  //#region src/validate/validate-config.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { d as AgentWorkflowConfig, g as FromConfigOptions, v as SchemaCompiler } from "./setup-agent-tsc3vm5I.mjs";
2
- import { l as ExplorePathsOptions } from "./verify-CBuYIpsa.mjs";
1
+ import { d as AgentWorkflowConfig, g as FromConfigOptions, v as SchemaCompiler } from "./setup-agent-CPyg2ic7.mjs";
2
+ import { u as ExplorePathsOptions } from "./verify-tkymWz6X.mjs";
3
3
  import { AnyActorLogic, AnyStateMachine } from "xstate";
4
4
  //#region src/validate/validate-config.d.ts
5
5
  /**
package/dist/validate.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { D as STREAM_TEXT_ACTOR, E as GENERATE_TEXT_ACTOR, T as DECIDE_ACTOR } from "./event-log-GPZO9p7_.mjs";
2
- import { i as setupAgentFromConfig } from "./setup-agent-DniLvDu7.mjs";
3
- import { a as explorePaths, o as lintAgentMachine } from "./verify-B5qGC5WN.mjs";
1
+ import { D as STREAM_TEXT_ACTOR, E as GENERATE_TEXT_ACTOR, T as DECIDE_ACTOR } from "./event-log-DE430ZCa.mjs";
2
+ import { i as setupAgentFromConfig } from "./setup-agent-BaffhCcM.mjs";
3
+ import { a as explorePaths, o as lintAgentMachine } from "./verify-BH5e0XLK.mjs";
4
4
  import Ajv2020 from "ajv/dist/2020.js";
5
5
  //#region schemas/agent-workflow.json
6
6
  var agent_workflow_default = {
@@ -1,6 +1,6 @@
1
- import { P as isTextLogic, U as getRegisteredAgentExecutionOptions, V as executorBoundLogics, X as getJsonSchemaSync, st as AgentError } from "./event-log-GPZO9p7_.mjs";
2
- import { d as getAcceptedEvents, l as resolveDecision, m as isRecord, n as acceptedDecisionOptions, s as isDecisionLogic, t as AgentDecisionExhaustedError } from "./decision-Bl_rKQnU.mjs";
3
- import { getNextTransitions, initialTransition, transition } from "xstate";
1
+ import { P as isTextLogic, U as getRegisteredAgentExecutionOptions, V as executorBoundLogics, X as getJsonSchemaSync, lt as isRecord, ut as AgentError } from "./event-log-DE430ZCa.mjs";
2
+ import { d as getAcceptedEvents, f as getHumanEventTypes, l as resolveDecision, n as acceptedDecisionOptions, p as isEventPattern, s as isDecisionLogic, t as AgentDecisionExhaustedError } from "./decision-DqO018Tp.mjs";
3
+ import { getNextTransitions, initialTransition, machineToJSON, transition } from "xstate";
4
4
  //#region src/steps.ts
5
5
  /**
6
6
  * Internal: the pure `(state, event) => (nextState, requests)` view of an
@@ -79,12 +79,15 @@ function getAgentRequests(actions, { machine, ...options } = {}) {
79
79
  const events = options.snapshot ? getAcceptedEvents(options.snapshot, {
80
80
  events: options.events,
81
81
  schemas: options.schemas,
82
- eventTypes: allowedEventTypes
82
+ eventTypes: allowedEventTypes,
83
+ actor: "agent"
83
84
  }) : [];
85
+ const humanEventTypes = options.snapshot ? getHumanEventTypes(options.snapshot) : /* @__PURE__ */ new Set();
84
86
  return [{
85
87
  ...decisionRequest,
86
88
  id: params.id,
87
- events
89
+ events,
90
+ ...decisionRequest.options ? { options: decisionRequest.options.filter((option) => !humanEventTypes.has(option.event.type)) } : {}
88
91
  }];
89
92
  }
90
93
  const textLogic = isTextLogic(registeredLogic) ? registeredLogic : void 0;
@@ -335,6 +338,45 @@ function checkDecideWithoutEvents(ctx) {
335
338
  }
336
339
  return out;
337
340
  }
341
+ function literalAllowedEvents(json, path, index) {
342
+ let node = json;
343
+ for (const key of path.split(".")) node = node?.states?.[key];
344
+ let input = normalizeInvokes(node?.invoke)[index]?.input;
345
+ if (isRecord(input) && typeof input["@expr"] === "string") try {
346
+ input = JSON.parse(input["@expr"]);
347
+ } catch {
348
+ return;
349
+ }
350
+ const allowedEvents = isRecord(input) ? input.allowedEvents : void 0;
351
+ return typeof allowedEvents === "string" ? [allowedEvents] : Array.isArray(allowedEvents) ? allowedEvents : void 0;
352
+ }
353
+ function handlesAsHuman(config, eventType) {
354
+ const transitions = config.on?.[eventType];
355
+ return (Array.isArray(transitions) ? transitions : [transitions]).some((transition) => !!transition && typeof transition === "object" && transition.meta?.actor === "human");
356
+ }
357
+ function checkDecideAllowsHumanEvent(ctx) {
358
+ const out = [];
359
+ for (const node of ctx.index.values()) for (const [index, invoke] of node.invokes.entries()) {
360
+ if (!isDecisionInvoke(invoke.src, ctx.actors)) continue;
361
+ const allowedEvents = literalAllowedEvents(ctx.json, node.path, index);
362
+ if (!allowedEvents) continue;
363
+ const configs = [
364
+ node.config,
365
+ ...ancestorChain(node, ctx.index).map((ancestor) => ancestor.config),
366
+ ctx.config
367
+ ];
368
+ for (const type of allowedEvents) {
369
+ if (typeof type !== "string" || isEventPattern(type) || !configs.some((config) => handlesAsHuman(config, type))) continue;
370
+ out.push({
371
+ code: "decide-allows-human-event",
372
+ severity: "error",
373
+ path: node.path,
374
+ message: `State '${node.path}' lets a decision choose '${type}', but a transition tagged 'meta: { actor: "human" }' handles it, so the model is never offered it. Remove '${type}' from 'allowedEvents', or drop the tag if a model may send it.`
375
+ });
376
+ }
377
+ }
378
+ return out;
379
+ }
338
380
  function checkDirectObjectSrc(ctx) {
339
381
  const out = [];
340
382
  for (const node of ctx.index.values()) for (const invoke of node.invokes) {
@@ -371,6 +413,7 @@ function checkInvokeWithoutOnError(ctx) {
371
413
  }
372
414
  const LINT_CHECKS = [
373
415
  checkDecideWithoutEvents,
416
+ checkDecideAllowsHumanEvent,
374
417
  checkDirectObjectSrc,
375
418
  checkInvokeWithoutOnError
376
419
  ];
@@ -408,8 +451,15 @@ function lintAgentMachine(machine, options = {}) {
408
451
  const config = machine.config ?? {};
409
452
  const index = buildStateIndex(config);
410
453
  const registered = getRegisteredAgentExecutionOptions(machine);
454
+ let json;
455
+ try {
456
+ json = machineToJSON(machine);
457
+ } catch {
458
+ json = void 0;
459
+ }
411
460
  const ctx = {
412
461
  config,
462
+ json,
413
463
  index,
414
464
  schemas: registered.schemas,
415
465
  actors: registered.actors ?? machine.sources?.actors ?? {}
@@ -1,5 +1,5 @@
1
1
  import { u as ChosenEvent } from "./types-Dp5_Ru4R.cjs";
2
- import { Q as AgentError } from "./text-logic-9fv1i9aK.cjs";
2
+ import { Q as AgentError } from "./text-logic-j9hShB7d.cjs";
3
3
  import { AnyMachineSnapshot, AnyStateMachine } from "xstate";
4
4
  //#region src/verify.d.ts
5
5
  /** Severity of an {@link AgentLintDiagnostic}. `error` findings fail CI/the CLI; `warning`s are advisory. */
@@ -10,7 +10,7 @@ type AgentLintSeverity = "error" | "warning";
10
10
  * or config location, and `message` explains the problem and its remedy.
11
11
  */
12
12
  interface AgentLintDiagnostic {
13
- code: "decide-without-events" | "direct-object-src" | "invoke-without-on-error";
13
+ code: "decide-without-events" | "decide-allows-human-event" | "direct-object-src" | "invoke-without-on-error";
14
14
  severity: AgentLintSeverity;
15
15
  /** State path (`parent.child`) or config pointer (e.g. `(root)`, `context`) the finding is about. */
16
16
  path: string;
@@ -212,6 +212,14 @@ interface SimulateAgentResult {
212
212
  */
213
213
  declare function simulateAgent(machine: AnyStateMachine, options: SimulateAgentOptions): Promise<SimulateAgentResult>;
214
214
  /** Options for {@link explorePaths}. */
215
+ /**
216
+ * The snapshot type the exploration callbacks (`payloads`, `dedupeBy`, a
217
+ * `canReach` predicate) receive. Import it from here rather than from your
218
+ * own `xstate`, which can be another version than the one this package uses.
219
+ */
220
+ type ExploreSnapshot = AnyMachineSnapshot;
221
+ /** What {@link canReach} looks for: a state id or path, or a snapshot predicate. */
222
+ type CanReachTarget = string | ((snapshot: ExploreSnapshot) => boolean);
215
223
  interface ExplorePathsOptions {
216
224
  input?: unknown;
217
225
  /** Max branch points (decisions + external-event forks) along any path. Default 8. */
@@ -274,7 +282,7 @@ interface ExplorePathsOptions {
274
282
  * were not all tried, so it is listed in `unexpandedEvents`. A type with no
275
283
  * schema at all is applied as `{ type }`.
276
284
  */
277
- payloads?: (snapshot: AnyMachineSnapshot) => Partial<Record<string, Record<string, unknown>[]>>;
285
+ payloads?: (snapshot: ExploreSnapshot) => Partial<Record<string, Record<string, unknown>[]>>;
278
286
  /**
279
287
  * A projection of the snapshot that determines its future. Settled
280
288
  * snapshots with an equal key are explored once (again only when reached
@@ -283,7 +291,7 @@ interface ExplorePathsOptions {
283
291
  * a key that drops a field the machine reads makes the search unsound.
284
292
  * Without it, every path is explored in full.
285
293
  */
286
- dedupeBy?: (snapshot: AnyMachineSnapshot) => string;
294
+ dedupeBy?: (snapshot: ExploreSnapshot) => string;
287
295
  }
288
296
  /** A single explored path's terminal outcome. */
289
297
  interface AgentPathTerminal {
@@ -409,6 +417,6 @@ declare class AgentUnknownStateError extends AgentError {
409
417
  * // violation.reachable → false is the safety proof
410
418
  * ```
411
419
  */
412
- declare function canReach(machine: AnyStateMachine, target: string | ((snapshot: AnyMachineSnapshot) => boolean), options?: ExplorePathsOptions): Promise<CanReachResult>;
420
+ declare function canReach(machine: AnyStateMachine, target: CanReachTarget, options?: ExplorePathsOptions): Promise<CanReachResult>;
413
421
  //#endregion
414
- export { explorePaths as _, AgentPathTerminal as a, CanReachResult as c, SimulateAgentOptions as d, SimulateAgentResult as f, canReach as g, assertAgentMachine as h, AgentPathReport as i, ExplorePathsOptions as l, SimulationTrailEntry as m, AgentLintError as n, AgentUnknownStateError as o, SimulationScript as p, AgentLintSeverity as r, AssertAgentMachineOptions as s, AgentLintDiagnostic as t, LintAgentMachineOptions as u, lintAgentMachine as v, simulateAgent as y };
422
+ export { assertAgentMachine as _, AgentPathTerminal as a, lintAgentMachine as b, CanReachResult as c, ExploreSnapshot as d, LintAgentMachineOptions as f, SimulationTrailEntry as g, SimulationScript as h, AgentPathReport as i, CanReachTarget as l, SimulateAgentResult as m, AgentLintError as n, AgentUnknownStateError as o, SimulateAgentOptions as p, AgentLintSeverity as r, AssertAgentMachineOptions as s, AgentLintDiagnostic as t, ExplorePathsOptions as u, canReach as v, simulateAgent as x, explorePaths as y };
@@ -1,5 +1,5 @@
1
- const require_event_log = require("./event-log-Dtb16p1p.cjs");
2
- const require_decision = require("./decision-oevZnkBe.cjs");
1
+ const require_event_log = require("./event-log-Bdww2l1D.cjs");
2
+ const require_decision = require("./decision-Iys2hzg3.cjs");
3
3
  let xstate = require("xstate");
4
4
  //#region src/steps.ts
5
5
  /**
@@ -79,12 +79,15 @@ function getAgentRequests(actions, { machine, ...options } = {}) {
79
79
  const events = options.snapshot ? require_decision.getAcceptedEvents(options.snapshot, {
80
80
  events: options.events,
81
81
  schemas: options.schemas,
82
- eventTypes: allowedEventTypes
82
+ eventTypes: allowedEventTypes,
83
+ actor: "agent"
83
84
  }) : [];
85
+ const humanEventTypes = options.snapshot ? require_decision.getHumanEventTypes(options.snapshot) : /* @__PURE__ */ new Set();
84
86
  return [{
85
87
  ...decisionRequest,
86
88
  id: params.id,
87
- events
89
+ events,
90
+ ...decisionRequest.options ? { options: decisionRequest.options.filter((option) => !humanEventTypes.has(option.event.type)) } : {}
88
91
  }];
89
92
  }
90
93
  const textLogic = require_event_log.isTextLogic(registeredLogic) ? registeredLogic : void 0;
@@ -335,6 +338,45 @@ function checkDecideWithoutEvents(ctx) {
335
338
  }
336
339
  return out;
337
340
  }
341
+ function literalAllowedEvents(json, path, index) {
342
+ let node = json;
343
+ for (const key of path.split(".")) node = node?.states?.[key];
344
+ let input = normalizeInvokes(node?.invoke)[index]?.input;
345
+ if (require_event_log.isRecord(input) && typeof input["@expr"] === "string") try {
346
+ input = JSON.parse(input["@expr"]);
347
+ } catch {
348
+ return;
349
+ }
350
+ const allowedEvents = require_event_log.isRecord(input) ? input.allowedEvents : void 0;
351
+ return typeof allowedEvents === "string" ? [allowedEvents] : Array.isArray(allowedEvents) ? allowedEvents : void 0;
352
+ }
353
+ function handlesAsHuman(config, eventType) {
354
+ const transitions = config.on?.[eventType];
355
+ return (Array.isArray(transitions) ? transitions : [transitions]).some((transition) => !!transition && typeof transition === "object" && transition.meta?.actor === "human");
356
+ }
357
+ function checkDecideAllowsHumanEvent(ctx) {
358
+ const out = [];
359
+ for (const node of ctx.index.values()) for (const [index, invoke] of node.invokes.entries()) {
360
+ if (!isDecisionInvoke(invoke.src, ctx.actors)) continue;
361
+ const allowedEvents = literalAllowedEvents(ctx.json, node.path, index);
362
+ if (!allowedEvents) continue;
363
+ const configs = [
364
+ node.config,
365
+ ...ancestorChain(node, ctx.index).map((ancestor) => ancestor.config),
366
+ ctx.config
367
+ ];
368
+ for (const type of allowedEvents) {
369
+ if (typeof type !== "string" || require_decision.isEventPattern(type) || !configs.some((config) => handlesAsHuman(config, type))) continue;
370
+ out.push({
371
+ code: "decide-allows-human-event",
372
+ severity: "error",
373
+ path: node.path,
374
+ message: `State '${node.path}' lets a decision choose '${type}', but a transition tagged 'meta: { actor: "human" }' handles it, so the model is never offered it. Remove '${type}' from 'allowedEvents', or drop the tag if a model may send it.`
375
+ });
376
+ }
377
+ }
378
+ return out;
379
+ }
338
380
  function checkDirectObjectSrc(ctx) {
339
381
  const out = [];
340
382
  for (const node of ctx.index.values()) for (const invoke of node.invokes) {
@@ -371,6 +413,7 @@ function checkInvokeWithoutOnError(ctx) {
371
413
  }
372
414
  const LINT_CHECKS = [
373
415
  checkDecideWithoutEvents,
416
+ checkDecideAllowsHumanEvent,
374
417
  checkDirectObjectSrc,
375
418
  checkInvokeWithoutOnError
376
419
  ];
@@ -408,8 +451,15 @@ function lintAgentMachine(machine, options = {}) {
408
451
  const config = machine.config ?? {};
409
452
  const index = buildStateIndex(config);
410
453
  const registered = require_event_log.getRegisteredAgentExecutionOptions(machine);
454
+ let json;
455
+ try {
456
+ json = (0, xstate.machineToJSON)(machine);
457
+ } catch {
458
+ json = void 0;
459
+ }
411
460
  const ctx = {
412
461
  config,
462
+ json,
413
463
  index,
414
464
  schemas: registered.schemas,
415
465
  actors: registered.actors ?? machine.sources?.actors ?? {}
@@ -716,7 +766,7 @@ const MAX_ADVANCE_STEPS = 1e3;
716
766
  const FINITE_PAYLOAD_CAP = 64;
717
767
  const DEFAULT_MAX_NODES = 1e5;
718
768
  function finiteDomain(schema) {
719
- if (!require_decision.isRecord(schema)) return;
769
+ if (!require_event_log.isRecord(schema)) return;
720
770
  if ("const" in schema) return [schema["const"]];
721
771
  if (Array.isArray(schema["enum"])) return schema["enum"];
722
772
  const union = schema["anyOf"] ?? schema["oneOf"];
@@ -733,8 +783,8 @@ function finitePayloads(jsonSchema) {
733
783
  payloads: [{}],
734
784
  complete: false
735
785
  };
736
- if (!require_decision.isRecord(jsonSchema) || jsonSchema["type"] !== "object" && !require_decision.isRecord(jsonSchema["properties"])) return bare;
737
- const properties = require_decision.isRecord(jsonSchema["properties"]) ? jsonSchema["properties"] : {};
786
+ if (!require_event_log.isRecord(jsonSchema) || jsonSchema["type"] !== "object" && !require_event_log.isRecord(jsonSchema["properties"])) return bare;
787
+ const properties = require_event_log.isRecord(jsonSchema["properties"]) ? jsonSchema["properties"] : {};
738
788
  const required = new Set(Array.isArray(jsonSchema["required"]) ? jsonSchema["required"] : []);
739
789
  if ([...required].some((key) => key !== "type" && !(key in properties))) return bare;
740
790
  let product = [{}];