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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/ai-sdk.cjs +149 -26
  2. package/dist/ai-sdk.d.cts +57 -34
  3. package/dist/ai-sdk.d.mts +57 -34
  4. package/dist/ai-sdk.mjs +147 -23
  5. package/dist/{decision-RQ0iv0oZ.mjs → decision-Bl_rKQnU.mjs} +73 -7
  6. package/dist/{decision-CmM8-HHz.cjs → decision-oevZnkBe.cjs} +112 -29
  7. package/dist/{text-logic-f5F5sCdc.cjs → event-log-Dtb16p1p.cjs} +813 -6
  8. package/dist/{text-logic-CIqHAmBi.mjs → event-log-GPZO9p7_.mjs} +679 -3
  9. package/dist/{event-log-store-BIPOz-di.d.mts → event-log-store-CN9ygggF.d.cts} +90 -5
  10. package/dist/{event-log-store-2xzFmgr2.d.cts → event-log-store-DS836wch.d.mts} +90 -5
  11. package/dist/event-log-store-bCdskPNS.cjs +95 -0
  12. package/dist/event-log-store-yalq8C77.mjs +84 -0
  13. package/dist/index.cjs +571 -58
  14. package/dist/index.d.cts +104 -510
  15. package/dist/index.d.mts +104 -510
  16. package/dist/index.mjs +542 -32
  17. package/dist/log.cjs +77 -20
  18. package/dist/log.d.cts +5 -5
  19. package/dist/log.d.mts +5 -5
  20. package/dist/log.mjs +58 -3
  21. package/dist/machines.cjs +1 -1
  22. package/dist/machines.d.cts +15 -15
  23. package/dist/machines.d.mts +15 -15
  24. package/dist/machines.mjs +1 -1
  25. package/dist/model-source-C7FFP2Sj.mjs +39 -0
  26. package/dist/model-source-DjJPjGSP.cjs +44 -0
  27. package/dist/openai.cjs +10 -10
  28. package/dist/openai.d.cts +27 -25
  29. package/dist/openai.d.mts +27 -25
  30. package/dist/openai.mjs +2 -2
  31. package/dist/otel.cjs +5 -4
  32. package/dist/otel.d.cts +5 -7
  33. package/dist/otel.d.mts +5 -7
  34. package/dist/otel.mjs +5 -3
  35. package/dist/{run-agent-COXXc0RW.d.mts → run-agent-C9ubX8Tx.d.cts} +87 -30
  36. package/dist/{run-agent-xNTxpt82.mjs → run-agent-CuQT1xSS.mjs} +170 -42
  37. package/dist/{run-agent-ByO-Bxyd.d.cts → run-agent-CxSyZmIE.d.mts} +87 -30
  38. package/dist/{run-agent-Dmf8GV2d.cjs → run-agent-cToZ3K42.cjs} +219 -86
  39. package/dist/{setup-agent-DV4cTou-.cjs → setup-agent-DTF3NQg3.cjs} +31 -20
  40. package/dist/setup-agent-DlbgwsIJ.d.cts +577 -0
  41. package/dist/{setup-agent-BuanDXeh.mjs → setup-agent-DniLvDu7.mjs} +15 -9
  42. package/dist/setup-agent-tsc3vm5I.d.mts +577 -0
  43. package/dist/tanstack-ai.cjs +627 -0
  44. package/dist/tanstack-ai.d.cts +204 -0
  45. package/dist/tanstack-ai.d.mts +204 -0
  46. package/dist/tanstack-ai.mjs +618 -0
  47. package/dist/testing.cjs +98 -985
  48. package/dist/testing.d.cts +51 -347
  49. package/dist/testing.d.mts +51 -347
  50. package/dist/testing.mjs +86 -973
  51. package/dist/{text-logic-IjfYy9DP.d.cts → text-logic-9fv1i9aK.d.cts} +50 -10
  52. package/dist/{text-logic-D-czVPYp.d.mts → text-logic-BDbGjYMh.d.mts} +50 -10
  53. package/dist/{types-BgNbNKnJ.d.mts → types-Dp5_Ru4R.d.cts} +2 -2
  54. package/dist/{types-hR5f5YXa.d.cts → types-Dp5_Ru4R.d.mts} +2 -2
  55. package/dist/usage-D8UphgEA.d.mts +17 -0
  56. package/dist/usage-R1NTmLxY.d.cts +17 -0
  57. package/dist/{utils-_q7F-AZu.d.mts → utils-BnXdWih3.d.mts} +1 -2
  58. package/dist/{utils-o4YRdHJX.d.cts → utils-ebVpCYoF.d.cts} +1 -2
  59. package/dist/validate.cjs +239 -8
  60. package/dist/validate.d.cts +110 -3
  61. package/dist/validate.d.mts +110 -3
  62. package/dist/validate.mjs +238 -7
  63. package/dist/verify-B5qGC5WN.mjs +1181 -0
  64. package/dist/verify-CBuYIpsa.d.mts +414 -0
  65. package/dist/verify-Cqghu4gz.cjs +1222 -0
  66. package/dist/verify-DYH_2VzU.d.cts +414 -0
  67. package/package.json +44 -31
  68. package/readme.md +38 -26
  69. package/skills/generate-machine/SKILL.md +140 -66
  70. package/skills/improve-machine/SKILL.md +190 -0
  71. package/skills/improve-machine/reference.md +96 -0
  72. package/dist/event-log-store-2pAFiWuc.mjs +0 -576
  73. package/dist/event-log-store-C-COhIXP.cjs +0 -702
  74. package/dist/usage-BdEjFuIi.mjs +0 -5
  75. package/dist/usage-efOQPdf_.cjs +0 -10
@@ -1,4 +1,7 @@
1
- //#region src/validate/index.d.ts
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";
3
+ import { AnyActorLogic, AnyStateMachine } from "xstate";
4
+ //#region src/validate/validate-config.d.ts
2
5
  /**
3
6
  * One schema-validation finding from {@link validateAgentConfig}. `path` is a
4
7
  * JSON Pointer into the config (`/states/idle/invoke`, `/` for the root) and
@@ -26,6 +29,110 @@ interface AgentConfigValidationResult {
26
29
  * not check that named guards/actions/actors are implemented, which
27
30
  * `fromConfig` does). The compiled validator is cached across calls.
28
31
  */
29
- declare function validateAgentConfig(config: unknown): AgentConfigValidationResult;
32
+ export declare function validateAgentConfig(config: unknown): AgentConfigValidationResult;
30
33
  //#endregion
31
- export { AgentConfigDiagnostic, AgentConfigValidationResult, validateAgentConfig };
34
+ //#region src/validate/verify-config.d.ts
35
+ /** The gates {@link verifyAgentConfig} runs, in order. */
36
+ type AgentConfigGateName = "validate" | "vocabulary" | "lower" | "lint" | "explore";
37
+ /** One gate's outcome. `detail` is a short, model-readable message (one problem per line). */
38
+ interface AgentConfigGate {
39
+ name: AgentConfigGateName;
40
+ ok: boolean;
41
+ detail?: string;
42
+ }
43
+ /**
44
+ * The names a config may reference. An omitted key is unrestricted; a listed
45
+ * key restricts that kind of reference to exactly these names.
46
+ *
47
+ * Invoke srcs: a config can only invoke an `agent.*` builtin or a key it
48
+ * declares under `requests` or `actors`; any other src fails the `lower` gate
49
+ * (`Missing actor source`), so a host-provided `actors` binding never adds a
50
+ * src. `requests` limits the declared `requests` keys and `actors` the declared
51
+ * `actors` keys. With `actors` listed, every non-builtin src must also be in
52
+ * `actors` or in `requests` (the declared request keys when `requests` is
53
+ * omitted).
54
+ *
55
+ * Raw xstate expression slots (`{ "@expr": ... }`) fail the gate anywhere in
56
+ * the config: xstate evaluates one in place of its literal siblings, which
57
+ * would hide a model ref or `allowedEvents` from these checks.
58
+ */
59
+ interface AgentConfigVocabulary {
60
+ /** Declared `actors` keys, and (when listed) the non-request invoke srcs. */
61
+ actors?: string[];
62
+ /** Declared `requests` keys. */
63
+ requests?: string[];
64
+ /** Named guard references (`guard: "isReady"`, not `{{ }}` templates). */
65
+ guards?: string[];
66
+ /** Named action types (`{ type: "notify" }`). */
67
+ actions?: string[];
68
+ /** Event types an `agent.decide` invoke may list in `allowedEvents`. */
69
+ events?: string[];
70
+ /** Model refs. Each must be a literal string (not a `{{ }}` template) in this set. */
71
+ models?: string[];
72
+ }
73
+ /**
74
+ * The `explore` options of {@link verifyAgentConfig}. Every other
75
+ * {@link ExplorePathsOptions} key (`outputs`, `errors`, `payloads`, caps)
76
+ * passes through to `explorePaths`; script text requests and invoked actors
77
+ * with `outputs`.
78
+ */
79
+ interface VerifyAgentConfigExploreOptions extends Omit<ExplorePathsOptions, "input"> {
80
+ /** One `explorePaths` run per input. An empty list fails the gate. */
81
+ inputs: unknown[];
82
+ /** Terminal statuses every explored path must end in. Default `['done']`. */
83
+ terminals?: Array<"done" | "idle">;
84
+ }
85
+ /** Options for {@link verifyAgentConfig}. */
86
+ interface VerifyAgentConfigOptions {
87
+ /** The JSON Schema compiler `setupAgent.fromConfig` uses. */
88
+ compileSchema: SchemaCompiler;
89
+ /** Named guard implementations, passed to `setupAgent.fromConfig`. */
90
+ guards?: FromConfigOptions["guards"];
91
+ /** Named action implementations, passed to `setupAgent.fromConfig`. */
92
+ actions?: FromConfigOptions["actions"];
93
+ /** Actor implementations, bound with `machine.provide({ actors })` after lowering. */
94
+ actors?: Record<string, AnyActorLogic>;
95
+ /** The names the config may reference. Omit to skip the vocabulary checks. */
96
+ allow?: AgentConfigVocabulary;
97
+ /** Lint policy. Error findings always fail; `warnings: true` fails on warnings too. */
98
+ lint?: {
99
+ warnings?: boolean;
100
+ };
101
+ /** Path exploration. Omit to skip the `explore` gate. */
102
+ explore?: VerifyAgentConfigExploreOptions;
103
+ }
104
+ /** Result of {@link verifyAgentConfig}. */
105
+ interface VerifyAgentConfigResult {
106
+ /** True when every gate that ran passed. */
107
+ ok: boolean;
108
+ /** The gates that ran, in order. A failing gate is the last entry. */
109
+ gates: AgentConfigGate[];
110
+ /** The lowered machine, set when the `lower` gate passed. */
111
+ machine?: AnyStateMachine;
112
+ /** The typed config, set when the `validate` gate passed. */
113
+ config?: AgentWorkflowConfig;
114
+ }
115
+ /**
116
+ * Runs every pre-run gate on a model-authored workflow config and stops at
117
+ * the first failure. Never throws on a bad config: each failure becomes a gate
118
+ * record whose `detail` is a repair prompt.
119
+ *
120
+ * - `validate`: the config's shape, against the shipped JSON Schema.
121
+ * - `vocabulary`: names the config references, against `allow`.
122
+ * - `lower`: `setupAgent.fromConfig` (unresolved guards/actions, bad targets).
123
+ * - `lint`: `lintAgentMachine` (errors fail; warnings fail with `lint.warnings`).
124
+ * - `explore`: `explorePaths` from each `explore.inputs` entry; every terminal
125
+ * must be one of `explore.terminals`, no cap (`maxPaths`, `maxNodes`) may cut
126
+ * it short, every event's payloads must be tried (`unexpandedEvents`), and
127
+ * every reached text request or invoke needs an `explore.outputs` entry.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const result = await verifyAgentConfig(candidate, { compileSchema, allow: { events: ["APPROVE", "DENY"] } });
132
+ * const failed = result.gates.find((gate) => !gate.ok);
133
+ * if (failed) feedback.push(`gate '${failed.name}' failed: ${failed.detail}`);
134
+ * ```
135
+ */
136
+ export declare function verifyAgentConfig(config: unknown, options: VerifyAgentConfigOptions): Promise<VerifyAgentConfigResult>;
137
+ //#endregion
138
+ export type { AgentConfigDiagnostic, AgentConfigValidationResult, VerifyAgentConfigOptions, VerifyAgentConfigResult };
@@ -1,4 +1,7 @@
1
- //#region src/validate/index.d.ts
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";
3
+ import { AnyActorLogic, AnyStateMachine } from "xstate";
4
+ //#region src/validate/validate-config.d.ts
2
5
  /**
3
6
  * One schema-validation finding from {@link validateAgentConfig}. `path` is a
4
7
  * JSON Pointer into the config (`/states/idle/invoke`, `/` for the root) and
@@ -26,6 +29,110 @@ interface AgentConfigValidationResult {
26
29
  * not check that named guards/actions/actors are implemented, which
27
30
  * `fromConfig` does). The compiled validator is cached across calls.
28
31
  */
29
- declare function validateAgentConfig(config: unknown): AgentConfigValidationResult;
32
+ export declare function validateAgentConfig(config: unknown): AgentConfigValidationResult;
30
33
  //#endregion
31
- export { AgentConfigDiagnostic, AgentConfigValidationResult, validateAgentConfig };
34
+ //#region src/validate/verify-config.d.ts
35
+ /** The gates {@link verifyAgentConfig} runs, in order. */
36
+ type AgentConfigGateName = "validate" | "vocabulary" | "lower" | "lint" | "explore";
37
+ /** One gate's outcome. `detail` is a short, model-readable message (one problem per line). */
38
+ interface AgentConfigGate {
39
+ name: AgentConfigGateName;
40
+ ok: boolean;
41
+ detail?: string;
42
+ }
43
+ /**
44
+ * The names a config may reference. An omitted key is unrestricted; a listed
45
+ * key restricts that kind of reference to exactly these names.
46
+ *
47
+ * Invoke srcs: a config can only invoke an `agent.*` builtin or a key it
48
+ * declares under `requests` or `actors`; any other src fails the `lower` gate
49
+ * (`Missing actor source`), so a host-provided `actors` binding never adds a
50
+ * src. `requests` limits the declared `requests` keys and `actors` the declared
51
+ * `actors` keys. With `actors` listed, every non-builtin src must also be in
52
+ * `actors` or in `requests` (the declared request keys when `requests` is
53
+ * omitted).
54
+ *
55
+ * Raw xstate expression slots (`{ "@expr": ... }`) fail the gate anywhere in
56
+ * the config: xstate evaluates one in place of its literal siblings, which
57
+ * would hide a model ref or `allowedEvents` from these checks.
58
+ */
59
+ interface AgentConfigVocabulary {
60
+ /** Declared `actors` keys, and (when listed) the non-request invoke srcs. */
61
+ actors?: string[];
62
+ /** Declared `requests` keys. */
63
+ requests?: string[];
64
+ /** Named guard references (`guard: "isReady"`, not `{{ }}` templates). */
65
+ guards?: string[];
66
+ /** Named action types (`{ type: "notify" }`). */
67
+ actions?: string[];
68
+ /** Event types an `agent.decide` invoke may list in `allowedEvents`. */
69
+ events?: string[];
70
+ /** Model refs. Each must be a literal string (not a `{{ }}` template) in this set. */
71
+ models?: string[];
72
+ }
73
+ /**
74
+ * The `explore` options of {@link verifyAgentConfig}. Every other
75
+ * {@link ExplorePathsOptions} key (`outputs`, `errors`, `payloads`, caps)
76
+ * passes through to `explorePaths`; script text requests and invoked actors
77
+ * with `outputs`.
78
+ */
79
+ interface VerifyAgentConfigExploreOptions extends Omit<ExplorePathsOptions, "input"> {
80
+ /** One `explorePaths` run per input. An empty list fails the gate. */
81
+ inputs: unknown[];
82
+ /** Terminal statuses every explored path must end in. Default `['done']`. */
83
+ terminals?: Array<"done" | "idle">;
84
+ }
85
+ /** Options for {@link verifyAgentConfig}. */
86
+ interface VerifyAgentConfigOptions {
87
+ /** The JSON Schema compiler `setupAgent.fromConfig` uses. */
88
+ compileSchema: SchemaCompiler;
89
+ /** Named guard implementations, passed to `setupAgent.fromConfig`. */
90
+ guards?: FromConfigOptions["guards"];
91
+ /** Named action implementations, passed to `setupAgent.fromConfig`. */
92
+ actions?: FromConfigOptions["actions"];
93
+ /** Actor implementations, bound with `machine.provide({ actors })` after lowering. */
94
+ actors?: Record<string, AnyActorLogic>;
95
+ /** The names the config may reference. Omit to skip the vocabulary checks. */
96
+ allow?: AgentConfigVocabulary;
97
+ /** Lint policy. Error findings always fail; `warnings: true` fails on warnings too. */
98
+ lint?: {
99
+ warnings?: boolean;
100
+ };
101
+ /** Path exploration. Omit to skip the `explore` gate. */
102
+ explore?: VerifyAgentConfigExploreOptions;
103
+ }
104
+ /** Result of {@link verifyAgentConfig}. */
105
+ interface VerifyAgentConfigResult {
106
+ /** True when every gate that ran passed. */
107
+ ok: boolean;
108
+ /** The gates that ran, in order. A failing gate is the last entry. */
109
+ gates: AgentConfigGate[];
110
+ /** The lowered machine, set when the `lower` gate passed. */
111
+ machine?: AnyStateMachine;
112
+ /** The typed config, set when the `validate` gate passed. */
113
+ config?: AgentWorkflowConfig;
114
+ }
115
+ /**
116
+ * Runs every pre-run gate on a model-authored workflow config and stops at
117
+ * the first failure. Never throws on a bad config: each failure becomes a gate
118
+ * record whose `detail` is a repair prompt.
119
+ *
120
+ * - `validate`: the config's shape, against the shipped JSON Schema.
121
+ * - `vocabulary`: names the config references, against `allow`.
122
+ * - `lower`: `setupAgent.fromConfig` (unresolved guards/actions, bad targets).
123
+ * - `lint`: `lintAgentMachine` (errors fail; warnings fail with `lint.warnings`).
124
+ * - `explore`: `explorePaths` from each `explore.inputs` entry; every terminal
125
+ * must be one of `explore.terminals`, no cap (`maxPaths`, `maxNodes`) may cut
126
+ * it short, every event's payloads must be tried (`unexpandedEvents`), and
127
+ * every reached text request or invoke needs an `explore.outputs` entry.
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * const result = await verifyAgentConfig(candidate, { compileSchema, allow: { events: ["APPROVE", "DENY"] } });
132
+ * const failed = result.gates.find((gate) => !gate.ok);
133
+ * if (failed) feedback.push(`gate '${failed.name}' failed: ${failed.detail}`);
134
+ * ```
135
+ */
136
+ export declare function verifyAgentConfig(config: unknown, options: VerifyAgentConfigOptions): Promise<VerifyAgentConfigResult>;
137
+ //#endregion
138
+ export type { AgentConfigDiagnostic, AgentConfigValidationResult, VerifyAgentConfigOptions, VerifyAgentConfigResult };
package/dist/validate.mjs CHANGED
@@ -1,3 +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
4
  import Ajv2020 from "ajv/dist/2020.js";
2
5
  //#region schemas/agent-workflow.json
3
6
  var agent_workflow_default = {
@@ -359,13 +362,10 @@ var agent_workflow_default = {
359
362
  }
360
363
  };
361
364
  //#endregion
362
- //#region src/validate/index.ts
365
+ //#region src/validate/validate-config.ts
363
366
  /**
364
- * Schema validation for agent workflow configs.
365
- *
366
- * Lives behind the `@statelyai/agent/validate` subpath so the core package
367
- * stays dependency-free. This module imports `ajv`, which is declared as an
368
- * optional peer dependency.
367
+ * Schema validation for agent workflow configs. Imports `ajv`, an optional
368
+ * peer dependency of the package.
369
369
  */
370
370
  let compiledWorkflowValidator;
371
371
  function getWorkflowValidator() {
@@ -400,4 +400,235 @@ function validateAgentConfig(config) {
400
400
  };
401
401
  }
402
402
  //#endregion
403
- export { validateAgentConfig };
403
+ //#region src/validate/verify-config.ts
404
+ const BUILTIN_SRCS = [
405
+ DECIDE_ACTOR,
406
+ GENERATE_TEXT_ACTOR,
407
+ STREAM_TEXT_ACTOR
408
+ ];
409
+ const MAX_DETAIL_LINES = 10;
410
+ /**
411
+ * Runs every pre-run gate on a model-authored workflow config and stops at
412
+ * the first failure. Never throws on a bad config: each failure becomes a gate
413
+ * record whose `detail` is a repair prompt.
414
+ *
415
+ * - `validate`: the config's shape, against the shipped JSON Schema.
416
+ * - `vocabulary`: names the config references, against `allow`.
417
+ * - `lower`: `setupAgent.fromConfig` (unresolved guards/actions, bad targets).
418
+ * - `lint`: `lintAgentMachine` (errors fail; warnings fail with `lint.warnings`).
419
+ * - `explore`: `explorePaths` from each `explore.inputs` entry; every terminal
420
+ * must be one of `explore.terminals`, no cap (`maxPaths`, `maxNodes`) may cut
421
+ * it short, every event's payloads must be tried (`unexpandedEvents`), and
422
+ * every reached text request or invoke needs an `explore.outputs` entry.
423
+ *
424
+ * @example
425
+ * ```ts
426
+ * const result = await verifyAgentConfig(candidate, { compileSchema, allow: { events: ["APPROVE", "DENY"] } });
427
+ * const failed = result.gates.find((gate) => !gate.ok);
428
+ * if (failed) feedback.push(`gate '${failed.name}' failed: ${failed.detail}`);
429
+ * ```
430
+ */
431
+ async function verifyAgentConfig(config, options) {
432
+ const gates = [];
433
+ const record = (name, problems, notes = []) => {
434
+ const lines = problems.length ? problems : notes;
435
+ gates.push({
436
+ name,
437
+ ok: problems.length === 0,
438
+ ...lines.length ? { detail: formatDetail(lines) } : {}
439
+ });
440
+ return problems.length === 0;
441
+ };
442
+ const fail = (extra = {}) => ({
443
+ ok: false,
444
+ gates,
445
+ ...extra
446
+ });
447
+ if (!record("validate", validateAgentConfig(config).errors.map((error) => `${error.path} ${error.message}`))) return fail();
448
+ const typed = config;
449
+ if (options.allow && !record("vocabulary", checkVocabulary(typed, options.allow))) return fail({ config: typed });
450
+ let machine;
451
+ try {
452
+ machine = setupAgentFromConfig(typed, {
453
+ compileSchema: options.compileSchema,
454
+ ...options.guards ? { guards: options.guards } : {},
455
+ ...options.actions ? { actions: options.actions } : {}
456
+ }).machine;
457
+ if (options.actors) machine = machine.provide({ actors: options.actors });
458
+ } catch (error) {
459
+ record("lower", [errorMessage(error)]);
460
+ return fail({ config: typed });
461
+ }
462
+ record("lower", []);
463
+ const diagnostics = lintAgentMachine(machine).map((d) => ({
464
+ failing: d.severity === "error" || options.lint?.warnings === true,
465
+ line: `${d.severity} ${d.code} at '${d.path}': ${d.message}`
466
+ }));
467
+ if (!record("lint", diagnostics.filter((d) => d.failing).map((d) => d.line), diagnostics.map((d) => d.line))) return fail({
468
+ config: typed,
469
+ machine
470
+ });
471
+ if (options.explore && !record("explore", await checkExplore(machine, options.explore))) return fail({
472
+ config: typed,
473
+ machine
474
+ });
475
+ return {
476
+ ok: true,
477
+ gates,
478
+ config: typed,
479
+ machine
480
+ };
481
+ }
482
+ /** A config's `T | T[]` field as a list of objects. */
483
+ const nodes = (value) => (value === void 0 ? [] : Array.isArray(value) ? value : [value]).filter((node) => !!node && typeof node === "object");
484
+ const hasTemplate = (value) => typeof value === "string" && value.includes("{{");
485
+ const list = (names) => names.length ? names.join(", ") : "(none)";
486
+ /** Every state in the config, depth-first, with its dotted path. */
487
+ function walkStates(states, prefix = "") {
488
+ if (!states || typeof states !== "object") return [];
489
+ return Object.entries(states).flatMap(([key, state]) => {
490
+ const path = prefix ? `${prefix}.${key}` : key;
491
+ return [{
492
+ path,
493
+ state
494
+ }, ...walkStates(state?.states, path)];
495
+ });
496
+ }
497
+ /** Every transition on a state: `on`, `after`, `always`, `choice`, `onDone`, and invoke done/error. */
498
+ function transitionsOf(state) {
499
+ return [
500
+ ...Object.values(state.on ?? {}).flatMap(nodes),
501
+ ...Object.values(state.after ?? {}).flatMap(nodes),
502
+ ...nodes(state.always),
503
+ ...nodes(state.choice),
504
+ ...nodes(state.onDone),
505
+ ...nodes(state.invoke).flatMap((invoke) => [...nodes(invoke.onDone), ...nodes(invoke.onError)])
506
+ ];
507
+ }
508
+ /**
509
+ * Paths of raw xstate expression slots (`{ "@expr": ... }`, `{ "@code": ... }`).
510
+ * xstate evaluates such an object whole, ignoring its literal siblings, so a
511
+ * slot would hide the model ref or `allowedEvents` the checks below read.
512
+ */
513
+ function rawExpressionPaths(value, path) {
514
+ if (Array.isArray(value)) return value.flatMap((item, index) => rawExpressionPaths(item, `${path}[${index}]`));
515
+ if (!value || typeof value !== "object") return [];
516
+ const node = value;
517
+ if (typeof node["@expr"] === "string" || typeof node["@code"] === "string") return [path];
518
+ return Object.entries(node).flatMap(([key, item]) => rawExpressionPaths(item, path ? `${path}.${key}` : key));
519
+ }
520
+ function checkVocabulary(config, allow) {
521
+ const problems = rawExpressionPaths(config, "").map((path) => `${path || "(root)"} uses a raw '@expr' slot, which the vocabulary cannot check; write {{ }} templates instead`);
522
+ const checkModel = (where, model) => {
523
+ if (!allow.models) return;
524
+ if (typeof model !== "string" || hasTemplate(model)) problems.push(`${where} needs a literal string model ref; allowed: ${list(allow.models)}`);
525
+ else if (!allow.models.includes(model)) problems.push(`${where} uses model '${model}'; allowed: ${list(allow.models)}`);
526
+ };
527
+ for (const [kind, declared, allowed] of [[
528
+ "request",
529
+ config.requests,
530
+ allow.requests
531
+ ], [
532
+ "actor",
533
+ config.actors,
534
+ allow.actors
535
+ ]]) {
536
+ if (!allowed) continue;
537
+ for (const key of Object.keys(declared ?? {})) if (!allowed.includes(key)) problems.push(`${kind} '${key}' is not in the vocabulary; allowed: ${list(allowed)}`);
538
+ }
539
+ for (const [key, request] of Object.entries(config.requests ?? {})) checkModel(`request '${key}'`, request.model);
540
+ const srcs = allow.actors ? [
541
+ ...BUILTIN_SRCS,
542
+ ...allow.actors,
543
+ ...allow.requests ?? Object.keys(config.requests ?? {})
544
+ ] : void 0;
545
+ for (const { path, state } of walkStates(config.states)) {
546
+ for (const invoke of nodes(state.invoke)) {
547
+ const src = String(invoke.src);
548
+ if (srcs && !srcs.includes(src)) {
549
+ problems.push(`state '${path}' invokes unknown src '${src}'; allowed: ${list(srcs)}`);
550
+ continue;
551
+ }
552
+ if (!BUILTIN_SRCS.includes(src)) continue;
553
+ const input = invoke.input;
554
+ if ((allow.models || src === "agent.decide" && allow.events) && !isPlainObject(input)) {
555
+ problems.push(`state '${path}': '${src}' input must be a literal object`);
556
+ continue;
557
+ }
558
+ checkModel(`state '${path}': '${src}'`, input?.model);
559
+ if (src !== "agent.decide" || !allow.events) continue;
560
+ const allowedEvents = input.allowedEvents;
561
+ if (!Array.isArray(allowedEvents) || allowedEvents.length === 0) {
562
+ problems.push(`state '${path}': agent.decide needs a literal, non-empty allowedEvents list; allowed: ${list(allow.events)}`);
563
+ continue;
564
+ }
565
+ for (const type of allowedEvents) if (typeof type !== "string" || !allow.events.includes(type)) problems.push(`state '${path}': agent.decide allowedEvents entry '${String(type)}' is not allowed; allowed: ${list(allow.events)}`);
566
+ }
567
+ const actionsAt = [["entry", state.entry], ["exit", state.exit]];
568
+ for (const transition of transitionsOf(state)) {
569
+ const guard = transition.guard;
570
+ if (allow.guards && typeof guard === "string" && !hasTemplate(guard) && !allow.guards.includes(guard)) problems.push(`state '${path}' uses unknown named guard '${guard}'; allowed: ${list(allow.guards)}`);
571
+ actionsAt.push(["a transition", transition.actions]);
572
+ }
573
+ if (!allow.actions) continue;
574
+ for (const [where, actions] of actionsAt) for (const action of nodes(actions)) if (typeof action.type === "string" && !allow.actions.includes(action.type)) problems.push(`state '${path}' ${where} uses unknown named action '${action.type}'; allowed: ${list(allow.actions)}`);
575
+ }
576
+ return problems;
577
+ }
578
+ const isPlainObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
579
+ async function checkExplore(machine, options) {
580
+ const { inputs, terminals = ["done"], ...rest } = options;
581
+ if (inputs.length === 0) return ["explore.inputs is empty, so no path was explored; give at least one input"];
582
+ const allowed = terminals;
583
+ const maxDepth = options.maxDepth ?? 8;
584
+ const problems = /* @__PURE__ */ new Set();
585
+ for (const [index, input] of inputs.entries()) {
586
+ const at = inputs.length > 1 ? `input ${index}: ` : "";
587
+ let report;
588
+ try {
589
+ report = await explorePaths(machine, {
590
+ ...rest,
591
+ input
592
+ });
593
+ } catch (error) {
594
+ problems.add(`${at}exploration threw: ${errorMessage(error)}`);
595
+ continue;
596
+ }
597
+ for (const terminal of report.terminals) {
598
+ if (allowed.includes(terminal.status)) continue;
599
+ const trail = `path [${terminal.path.map((event) => event.type).join(", ")}]`;
600
+ const state = JSON.stringify(terminal.state);
601
+ switch (terminal.status) {
602
+ case "max-depth":
603
+ problems.add(`${at}${trail} hit maxDepth ${maxDepth} in state ${state} without ending; the workflow may loop`);
604
+ break;
605
+ case "needs-output":
606
+ problems.add(`${at}${trail} reached '${terminal.missingSrc}' in state ${state}, which has no output to explore with; add an explore.outputs entry for it`);
607
+ break;
608
+ case "error":
609
+ problems.add(`${at}${trail} errored in state ${state} with no onError: ${errorMessage(terminal.error)}`);
610
+ break;
611
+ default: problems.add(`${at}${trail} ends '${terminal.status}' in state ${state}; every path must end ${allowed.map((t) => `'${t}'`).join(" or ")}`);
612
+ }
613
+ }
614
+ if (report.hitPathCap) problems.add(`${at}a cap (maxPaths ${options.maxPaths ?? 200} or maxNodes ${options.maxNodes ?? 1e5}) cut exploration short; the workflow branches too much to verify`);
615
+ if (report.unexpandedEvents.length > 0) problems.add(`${at}explore did not try every payload of: ${report.unexpandedEvents.join(", ")}; add explore.payloads entries for them`);
616
+ }
617
+ return [...problems];
618
+ }
619
+ function formatDetail(lines) {
620
+ const shown = lines.slice(0, MAX_DETAIL_LINES);
621
+ const hidden = lines.length - shown.length;
622
+ return [...shown, ...hidden > 0 ? [`(${hidden} more)`] : []].join("\n");
623
+ }
624
+ function errorMessage(error) {
625
+ if (error instanceof Error) return error.message;
626
+ if (typeof error === "string") return error;
627
+ try {
628
+ return JSON.stringify(error);
629
+ } catch {
630
+ return String(error);
631
+ }
632
+ }
633
+ //#endregion
634
+ export { validateAgentConfig, verifyAgentConfig };