@acpus/core 0.6.0 → 0.7.0-alpha.0

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 (210) hide show
  1. package/README.md +128 -4
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/graph/builder.d.ts +103 -0
  4. package/dist/graph/builder.d.ts.map +1 -0
  5. package/dist/graph/builder.js +209 -0
  6. package/dist/graph/builder.js.map +1 -0
  7. package/dist/graph/lowering.d.ts +8 -0
  8. package/dist/graph/lowering.d.ts.map +1 -0
  9. package/dist/graph/lowering.js +45 -0
  10. package/dist/graph/lowering.js.map +1 -0
  11. package/dist/graph/refs.d.ts +11 -0
  12. package/dist/graph/refs.d.ts.map +1 -0
  13. package/dist/graph/refs.js +12 -0
  14. package/dist/graph/refs.js.map +1 -0
  15. package/dist/graph/scope.d.ts +21 -0
  16. package/dist/graph/scope.d.ts.map +1 -0
  17. package/dist/graph/scope.js +22 -0
  18. package/dist/graph/scope.js.map +1 -0
  19. package/dist/index.d.ts +11 -25
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +4 -15
  22. package/dist/index.js.map +1 -1
  23. package/dist/internal/symbols.d.ts +9 -0
  24. package/dist/internal/symbols.d.ts.map +1 -0
  25. package/dist/internal/symbols.js +9 -0
  26. package/dist/internal/symbols.js.map +1 -0
  27. package/dist/internal/type-utils.d.ts +7 -0
  28. package/dist/internal/type-utils.d.ts.map +1 -0
  29. package/dist/internal/type-utils.js +8 -0
  30. package/dist/internal/type-utils.js.map +1 -0
  31. package/dist/ir/types.d.ts +218 -0
  32. package/dist/ir/types.d.ts.map +1 -0
  33. package/dist/ir/types.js.map +1 -0
  34. package/dist/ir/validator.d.ts +3 -0
  35. package/dist/ir/validator.d.ts.map +1 -0
  36. package/dist/ir/validator.js +671 -0
  37. package/dist/ir/validator.js.map +1 -0
  38. package/dist/ir.d.ts +3 -0
  39. package/dist/ir.d.ts.map +1 -0
  40. package/dist/ir.js +2 -0
  41. package/dist/ir.js.map +1 -0
  42. package/dist/nodes/composite/fanout.d.ts +24 -0
  43. package/dist/nodes/composite/fanout.d.ts.map +1 -0
  44. package/dist/nodes/composite/fanout.js +24 -0
  45. package/dist/nodes/composite/fanout.js.map +1 -0
  46. package/dist/nodes/composite/if.d.ts +12 -0
  47. package/dist/nodes/composite/if.d.ts.map +1 -0
  48. package/dist/nodes/composite/if.js +13 -0
  49. package/dist/nodes/composite/if.js.map +1 -0
  50. package/dist/nodes/composite/loop.d.ts +17 -0
  51. package/dist/nodes/composite/loop.d.ts.map +1 -0
  52. package/dist/nodes/composite/loop.js +22 -0
  53. package/dist/nodes/composite/loop.js.map +1 -0
  54. package/dist/nodes/composite/parallel.d.ts +29 -0
  55. package/dist/nodes/composite/parallel.d.ts.map +1 -0
  56. package/dist/nodes/composite/parallel.js +18 -0
  57. package/dist/nodes/composite/parallel.js.map +1 -0
  58. package/dist/nodes/composite/shared.d.ts +38 -0
  59. package/dist/nodes/composite/shared.d.ts.map +1 -0
  60. package/dist/nodes/composite/shared.js +2 -0
  61. package/dist/nodes/composite/shared.js.map +1 -0
  62. package/dist/nodes/composite/switch.d.ts +22 -0
  63. package/dist/nodes/composite/switch.d.ts.map +1 -0
  64. package/dist/nodes/composite/switch.js +12 -0
  65. package/dist/nodes/composite/switch.js.map +1 -0
  66. package/dist/nodes/control/assert.d.ts +10 -0
  67. package/dist/nodes/control/assert.d.ts.map +1 -0
  68. package/dist/nodes/control/assert.js +14 -0
  69. package/dist/nodes/control/assert.js.map +1 -0
  70. package/dist/nodes/leaf/agent.d.ts +63 -0
  71. package/dist/nodes/leaf/agent.d.ts.map +1 -0
  72. package/dist/nodes/leaf/agent.js +56 -0
  73. package/dist/nodes/leaf/agent.js.map +1 -0
  74. package/dist/nodes/leaf/shared.d.ts +11 -0
  75. package/dist/nodes/leaf/shared.d.ts.map +1 -0
  76. package/dist/nodes/leaf/shared.js +2 -0
  77. package/dist/nodes/leaf/shared.js.map +1 -0
  78. package/dist/nodes/leaf/signal.d.ts +27 -0
  79. package/dist/nodes/leaf/signal.d.ts.map +1 -0
  80. package/dist/nodes/leaf/signal.js +18 -0
  81. package/dist/nodes/leaf/signal.js.map +1 -0
  82. package/dist/nodes/leaf/task.d.ts +71 -0
  83. package/dist/nodes/leaf/task.d.ts.map +1 -0
  84. package/dist/nodes/leaf/task.js +88 -0
  85. package/dist/nodes/leaf/task.js.map +1 -0
  86. package/dist/runtime/dollar.d.ts +42 -0
  87. package/dist/runtime/dollar.d.ts.map +1 -0
  88. package/dist/runtime/dollar.js +82 -0
  89. package/dist/runtime/dollar.js.map +1 -0
  90. package/dist/runtime/secret.d.ts +10 -0
  91. package/dist/runtime/secret.d.ts.map +1 -0
  92. package/dist/runtime/secret.js +12 -0
  93. package/dist/runtime/secret.js.map +1 -0
  94. package/dist/runtime/task-context.d.ts +28 -0
  95. package/dist/runtime/task-context.d.ts.map +1 -0
  96. package/dist/runtime/task-context.js +2 -0
  97. package/dist/runtime/task-context.js.map +1 -0
  98. package/dist/runtime.d.ts +5 -0
  99. package/dist/runtime.d.ts.map +1 -0
  100. package/dist/runtime.js +3 -0
  101. package/dist/runtime.js.map +1 -0
  102. package/dist/schema/index.d.ts +6 -2
  103. package/dist/schema/index.d.ts.map +1 -1
  104. package/dist/schema/index.js +3 -2
  105. package/dist/schema/index.js.map +1 -1
  106. package/dist/schema/lower.d.ts +25 -0
  107. package/dist/schema/lower.d.ts.map +1 -0
  108. package/dist/schema/lower.js +198 -0
  109. package/dist/schema/lower.js.map +1 -0
  110. package/dist/schema/validate.d.ts +18 -0
  111. package/dist/schema/validate.d.ts.map +1 -0
  112. package/dist/schema/validate.js +22 -0
  113. package/dist/schema/validate.js.map +1 -0
  114. package/dist/schema/zod.d.ts +17 -0
  115. package/dist/schema/zod.d.ts.map +1 -0
  116. package/dist/schema/zod.js +15 -0
  117. package/dist/schema/zod.js.map +1 -0
  118. package/dist/schema.d.ts +3 -0
  119. package/dist/schema.d.ts.map +1 -0
  120. package/dist/schema.js +2 -0
  121. package/dist/schema.js.map +1 -0
  122. package/dist/template/template.d.ts +5 -0
  123. package/dist/template/template.d.ts.map +1 -0
  124. package/dist/template/template.js +10 -0
  125. package/dist/template/template.js.map +1 -0
  126. package/dist/workflow.d.ts +3 -0
  127. package/dist/workflow.d.ts.map +1 -0
  128. package/dist/workflow.js +2 -0
  129. package/dist/workflow.js.map +1 -0
  130. package/package.json +35 -20
  131. package/dist/agent-overrides.d.ts +0 -33
  132. package/dist/agent-overrides.d.ts.map +0 -1
  133. package/dist/agent-overrides.js +0 -178
  134. package/dist/agent-overrides.js.map +0 -1
  135. package/dist/cel-ast.d.ts +0 -39
  136. package/dist/cel-ast.d.ts.map +0 -1
  137. package/dist/cel-ast.js +0 -157
  138. package/dist/cel-ast.js.map +0 -1
  139. package/dist/cel-environment.d.ts +0 -6
  140. package/dist/cel-environment.d.ts.map +0 -1
  141. package/dist/cel-environment.js +0 -44
  142. package/dist/cel-environment.js.map +0 -1
  143. package/dist/compiler.d.ts +0 -4
  144. package/dist/compiler.d.ts.map +0 -1
  145. package/dist/compiler.js +0 -702
  146. package/dist/compiler.js.map +0 -1
  147. package/dist/composite-contract.d.ts +0 -46
  148. package/dist/composite-contract.d.ts.map +0 -1
  149. package/dist/composite-contract.js +0 -97
  150. package/dist/composite-contract.js.map +0 -1
  151. package/dist/diagnostics.d.ts +0 -9
  152. package/dist/diagnostics.d.ts.map +0 -1
  153. package/dist/diagnostics.js +0 -16
  154. package/dist/diagnostics.js.map +0 -1
  155. package/dist/duration.d.ts +0 -13
  156. package/dist/duration.d.ts.map +0 -1
  157. package/dist/duration.js +0 -32
  158. package/dist/duration.js.map +0 -1
  159. package/dist/expression-scope.d.ts +0 -23
  160. package/dist/expression-scope.d.ts.map +0 -1
  161. package/dist/expression-scope.js +0 -545
  162. package/dist/expression-scope.js.map +0 -1
  163. package/dist/expressions-shared.d.ts +0 -19
  164. package/dist/expressions-shared.d.ts.map +0 -1
  165. package/dist/expressions-shared.js +0 -27
  166. package/dist/expressions-shared.js.map +0 -1
  167. package/dist/expressions.d.ts +0 -9
  168. package/dist/expressions.d.ts.map +0 -1
  169. package/dist/expressions.js +0 -95
  170. package/dist/expressions.js.map +0 -1
  171. package/dist/hash.d.ts +0 -19
  172. package/dist/hash.d.ts.map +0 -1
  173. package/dist/hash.js +0 -120
  174. package/dist/hash.js.map +0 -1
  175. package/dist/hook-validation.d.ts +0 -9
  176. package/dist/hook-validation.d.ts.map +0 -1
  177. package/dist/hook-validation.js +0 -73
  178. package/dist/hook-validation.js.map +0 -1
  179. package/dist/hooks.d.ts +0 -150
  180. package/dist/hooks.d.ts.map +0 -1
  181. package/dist/hooks.js +0 -18
  182. package/dist/hooks.js.map +0 -1
  183. package/dist/schedule.d.ts +0 -3
  184. package/dist/schedule.d.ts.map +0 -1
  185. package/dist/schedule.js +0 -21
  186. package/dist/schedule.js.map +0 -1
  187. package/dist/schema/dsl.d.ts +0 -34
  188. package/dist/schema/dsl.d.ts.map +0 -1
  189. package/dist/schema/dsl.js +0 -231
  190. package/dist/schema/dsl.js.map +0 -1
  191. package/dist/schema/helpers.d.ts +0 -24
  192. package/dist/schema/helpers.d.ts.map +0 -1
  193. package/dist/schema/helpers.js +0 -61
  194. package/dist/schema/helpers.js.map +0 -1
  195. package/dist/schema-validator.d.ts +0 -29
  196. package/dist/schema-validator.d.ts.map +0 -1
  197. package/dist/schema-validator.js +0 -666
  198. package/dist/schema-validator.js.map +0 -1
  199. package/dist/source-resolver.d.ts +0 -22
  200. package/dist/source-resolver.d.ts.map +0 -1
  201. package/dist/source-resolver.js +0 -43
  202. package/dist/source-resolver.js.map +0 -1
  203. package/dist/types.d.ts +0 -126
  204. package/dist/types.d.ts.map +0 -1
  205. package/dist/types.js.map +0 -1
  206. package/dist/workflow-schema.d.ts +0 -10
  207. package/dist/workflow-schema.d.ts.map +0 -1
  208. package/dist/workflow-schema.js +0 -523
  209. package/dist/workflow-schema.js.map +0 -1
  210. /package/dist/{types.js → ir/types.js} +0 -0
@@ -1,666 +0,0 @@
1
- /**
2
- * Schema-based structural validation for Acpus workflow specs.
3
- *
4
- * Compiles WORKFLOW_SCHEMA with Ajv and maps validation errors to
5
- * diagnostic codes used by the compiler. This handles:
6
- * - Unknown fields (additionalProperties: false)
7
- * - Type / enum / required constraints
8
- * - if/then cross-field dependencies
9
- * - oneOf step-kind dispatch
10
- *
11
- * Duration format validation and semantic checks (cross-references,
12
- * DSL compilation) remain in the hand-written compiler code.
13
- */
14
- import { Ajv } from "ajv";
15
- import { WORKFLOW_SCHEMA } from "./workflow-schema.js";
16
- // ── Module-level Ajv singleton ──
17
- const ajv = new Ajv({ allErrors: true, strict: false });
18
- const validate = ajv.compile(WORKFLOW_SCHEMA);
19
- // ── Error mapping ──
20
- /**
21
- * Map Ajv validation errors to compiler diagnostics.
22
- *
23
- * Strategy:
24
- * - `additionalProperties` → SPEC_SHAPE / AGENT_SHAPE / STEP_SHAPE based on path
25
- * - `required` → specific code based on missingProperty
26
- * - `enum` / `const` → specific code based on allowedValues context
27
- * - Step-level oneOf failures: suppress structural oneOf/const errors that are
28
- * artifacts of branch-matching; only report STEP_KIND if no specific
29
- * property-level error can be extracted.
30
- * - Sub-oneOf errors (timeout, cmd, fanout.over) are classified individually.
31
- * - Everything else → SPEC_SHAPE as fallback
32
- */
33
- export function validateWithSchema(spec, diagnostics) {
34
- if (!validate(spec)) {
35
- const errors = validate.errors ?? [];
36
- mapErrors(errors, diagnostics, spec);
37
- }
38
- }
39
- function mapErrors(errors, diagnostics, spec) {
40
- // Identify step-level oneOf failures — these produce many noisy sub-errors
41
- // because Ajv tries each branch and reports all mismatches.
42
- const stepOneOfPaths = new Set();
43
- for (const err of errors) {
44
- if (err.keyword === "oneOf" && isStepPath(err.instancePath)) {
45
- stepOneOfPaths.add(err.instancePath);
46
- }
47
- }
48
- // For each step-level oneOf failure, re-validate the step against only its
49
- // intended branch (inferred from the data's discriminator). This avoids two
50
- // classes of noise that arise once composite child lists recurse into the
51
- // step union: (1) cross-branch artifacts (e.g. a `loop` step reported as
52
- // "missing prompt" because the agent branch also failed), and (2) ancestor
53
- // cascade (a parent step failing its oneOf only because a descendant step is
54
- // invalid). Descendant step errors are reported by their own oneOf iteration.
55
- const reportedStepSubErrors = new Set(); // instancePath of reported sub-errors
56
- for (const stepPath of stepOneOfPaths) {
57
- const stepData = navigate(spec, stepPath);
58
- const branch = inferStepBranch(stepData);
59
- if (branch) {
60
- const branchErrors = validateAgainstBranch(branch, stepData);
61
- // Keep only errors that belong to THIS step. Errors inside a nested child
62
- // step (under a `do`/`parallel` list) are owned by that child's own oneOf
63
- // iteration, so drop them here to avoid ancestor cascade.
64
- const relevant = branchErrors.filter((err) => !isWithinNestedStep(err.instancePath));
65
- let reportedAny = false;
66
- for (const err of relevant) {
67
- const absolutePath = prefixPath(stepPath, err.instancePath);
68
- const path = toPath(absolutePath);
69
- const code = classifyError(err, path);
70
- if (code === "STEP_KIND")
71
- continue;
72
- if (err.keyword === "if")
73
- continue;
74
- if (code === "STEP_SHAPE" && isDirectParallelStepNoiseAtAbsolutePath(err, absolutePath, spec))
75
- continue;
76
- const dedupeKey = `${absolutePath}|${code}|${err.keyword}|${JSON.stringify(err.params)}`;
77
- if (reportedStepSubErrors.has(dedupeKey))
78
- continue;
79
- reportedStepSubErrors.add(dedupeKey);
80
- diagnostics.error(code, formatMessage(err, code, path), path);
81
- reportedAny = true;
82
- }
83
- // The step itself is well-formed (its only failures were invalid
84
- // descendants, which report their own diagnostics) → emit nothing.
85
- if (reportedAny || !relevant.some((err) => err.keyword !== "if"))
86
- continue;
87
- }
88
- // No intended branch could be inferred (or none of its errors classified) —
89
- // the step truly has no matching kind.
90
- diagnostics.error("STEP_KIND", "Step must define one of run: agent, run: program, run: signal, parallel, fanout, if, switch, loop, guard, subworkflow, or include.", toPath(stepPath));
91
- }
92
- // Now process all remaining errors (non-step-oneOf sub-errors, and errors
93
- // not under a step-level oneOf)
94
- for (const err of errors) {
95
- // Skip errors that are part of a step-level oneOf group (already handled)
96
- if (stepOneOfPaths.size > 0 && isSubErrorOf(err.instancePath, stepOneOfPaths)) {
97
- continue;
98
- }
99
- // Skip step-level oneOf errors themselves (already handled above)
100
- if (err.keyword === "oneOf" && isStepPath(err.instancePath)) {
101
- continue;
102
- }
103
- // Skip "if" keyword errors — these are artifacts of if/then evaluation
104
- // and the "then" error is what's actually useful (or we handle required
105
- // errors from the then clause directly).
106
- if (err.keyword === "if")
107
- continue;
108
- const path = toPath(err.instancePath);
109
- const code = classifyError(err, path);
110
- if (code === "STEP_SHAPE" && isDirectParallelStepNoise(err, spec))
111
- continue;
112
- const message = formatMessage(err, code, path);
113
- diagnostics.error(code, message, path);
114
- }
115
- }
116
- // ── Branch inference for step oneOf failures ──
117
- /** Map a step's data discriminator to the `$defs` branch it intended to match. */
118
- function inferStepBranch(stepData) {
119
- if (typeof stepData !== "object" || stepData === null)
120
- return undefined;
121
- const data = stepData;
122
- if (data.run === "agent")
123
- return "agentStep";
124
- if (data.run === "program")
125
- return "programStep";
126
- if (data.run === "signal")
127
- return "signalStep";
128
- if ("pipeline" in data)
129
- return "pipelineStep";
130
- if ("parallel" in data)
131
- return "parallelStep";
132
- if ("fanout" in data)
133
- return "fanoutStep";
134
- if ("if" in data)
135
- return "ifStep";
136
- if ("switch" in data)
137
- return "switchStep";
138
- if ("loop" in data)
139
- return "loopStep";
140
- if ("guard" in data)
141
- return "guardStep";
142
- if ("subworkflow" in data)
143
- return "subworkflowStep";
144
- return undefined;
145
- }
146
- /** Cache of per-branch validators so repeated steps don't recompile. */
147
- const branchValidators = new Map();
148
- /** Validate `stepData` against a single step branch and return its errors. */
149
- function validateAgainstBranch(branch, stepData) {
150
- let v = branchValidators.get(branch);
151
- if (!v) {
152
- v = ajv.compile({ $ref: `#/$defs/${branch}`, $defs: WORKFLOW_SCHEMA.$defs });
153
- branchValidators.set(branch, v);
154
- }
155
- v(stepData);
156
- return v.errors ?? [];
157
- }
158
- /** Resolve the JSON value at an Ajv instancePath ("/workflow/steps/0"). */
159
- function navigate(spec, instancePath) {
160
- const parts = instancePath.split("/").filter(Boolean);
161
- let cur = spec;
162
- for (const part of parts) {
163
- if (cur == null || typeof cur !== "object")
164
- return undefined;
165
- cur = cur[decodePointer(part)];
166
- }
167
- return cur;
168
- }
169
- function isDirectParallelStepNoise(err, spec) {
170
- if (err.keyword !== "additionalProperties")
171
- return false;
172
- if (!/\/parallel\/\d+$/.test(err.instancePath))
173
- return false;
174
- const value = navigate(spec, err.instancePath);
175
- return isParallelBranchMissingDo(value);
176
- }
177
- function isDirectParallelStepNoiseAtAbsolutePath(err, absolutePath, spec) {
178
- if (err.keyword !== "additionalProperties")
179
- return false;
180
- if (!/\/parallel\/\d+$/.test(absolutePath))
181
- return false;
182
- return isParallelBranchMissingDo(navigate(spec, absolutePath));
183
- }
184
- function isParallelBranchMissingDo(value) {
185
- return typeof value === "object" && value !== null && !Object.prototype.hasOwnProperty.call(value, "do");
186
- }
187
- /** Decode a JSON Pointer token (~1 → /, ~0 → ~). */
188
- function decodePointer(token) {
189
- return token.replace(/~1/g, "/").replace(/~0/g, "~");
190
- }
191
- /** Join a step's absolute instancePath with a branch-local instancePath. */
192
- function prefixPath(stepPath, localPath) {
193
- return localPath ? stepPath + localPath : stepPath;
194
- }
195
- /**
196
- * True when a branch-local instancePath points inside a nested child step (an
197
- * entry in a `do`, `then`, `else`, or `pipeline` list). Such errors belong to that child step's
198
- * own oneOf iteration, not the enclosing step.
199
- */
200
- function isWithinNestedStep(localPath) {
201
- return /\/(do|then|else|pipeline)\/\d+(\/|$)/.test(localPath);
202
- }
203
- // ── Error classification ──
204
- function classifyError(err, path) {
205
- switch (err.keyword) {
206
- case "additionalProperties":
207
- return classifyAdditionalProperties(err, path);
208
- case "required": {
209
- const missing = err.params.missingProperty;
210
- return classifyRequired(missing, path);
211
- }
212
- case "enum":
213
- return classifyEnum(err, path);
214
- case "const":
215
- return classifyConst(err, path);
216
- case "type":
217
- return classifyType(err, path);
218
- case "minimum":
219
- case "exclusiveMinimum":
220
- case "minItems":
221
- return classifyMinimum(err, path);
222
- case "pattern":
223
- return path.endsWith(".id") ? "STEP_ID_INVALID" : "SPEC_SHAPE";
224
- default:
225
- return "SPEC_SHAPE";
226
- }
227
- }
228
- function classifyAdditionalProperties(err, path) {
229
- const ctx = pathContext(path);
230
- switch (ctx) {
231
- case "agent":
232
- return "AGENT_SHAPE";
233
- case "step":
234
- return "STEP_SHAPE";
235
- case "fanout":
236
- return "STEP_SHAPE";
237
- case "if":
238
- return "STEP_SHAPE";
239
- case "switch-case":
240
- return "STEP_SHAPE";
241
- case "switch-spec":
242
- return "STEP_SHAPE";
243
- case "guard":
244
- return "STEP_SHAPE";
245
- case "capture":
246
- return "CAPTURE_SHAPE";
247
- case "retry":
248
- return "RETRY_SHAPE";
249
- case "success-criteria":
250
- return "FANOUT_SUCCESS_CRITERIA";
251
- case "loop":
252
- return "STEP_SHAPE";
253
- case "workflow":
254
- return "SPEC_SHAPE";
255
- default:
256
- return "SPEC_SHAPE";
257
- }
258
- }
259
- function classifyRequired(missing, path) {
260
- const ctx = pathContext(path);
261
- switch (missing) {
262
- case "id":
263
- return "STEP_ID";
264
- case "use":
265
- return "AGENT_SHAPE";
266
- case "prompt":
267
- return "AGENT_PROMPT";
268
- case "over":
269
- return "FANOUT_OVER";
270
- case "do":
271
- return path.includes(".parallel[") ? "PARALLEL_DO" : "FANOUT_DO";
272
- case "condition":
273
- return ctx === "if" ? "STEP_SHAPE" : "SPEC_SHAPE";
274
- case "then":
275
- return ctx === "if" ? "STEP_SHAPE" : ctx === "guard" ? "GUARD_ACTION" : "SPEC_SHAPE";
276
- case "pipeline":
277
- return "STEP_KIND";
278
- case "quorum":
279
- return "FANOUT_QUORUM";
280
- case "cmd":
281
- return "PROGRAM_CMD";
282
- case "from":
283
- return "CAPTURE_FROM";
284
- case "parse":
285
- return "CAPTURE_PARSE";
286
- case "path":
287
- return "CAPTURE_PATH";
288
- case "timeout":
289
- return "STEP_TIMEOUT";
290
- case "on_timeout":
291
- return "SIGNAL_ON_TIMEOUT";
292
- case "default":
293
- return ctx === "switch-spec" ? "SPEC_SHAPE" : "SIGNAL_DEFAULT";
294
- case "max_iterations":
295
- return "LOOP_MAX_ITERATIONS";
296
- case "when":
297
- return ctx === "guard" ? "GUARD_WHEN" : "SPEC_SHAPE";
298
- case "else":
299
- return ctx === "guard" ? "GUARD_ACTION" : "SPEC_SHAPE";
300
- case "max":
301
- return "RETRY_SHAPE";
302
- case "steps":
303
- return "SPEC_SHAPE";
304
- case "version":
305
- case "name":
306
- case "workflow":
307
- return "SPEC_SHAPE";
308
- default:
309
- return "SPEC_SHAPE";
310
- }
311
- }
312
- function classifyEnum(err, path) {
313
- const allowedValues = err.params.allowedValues;
314
- // version enum
315
- if (allowedValues.length === 1 && allowedValues[0] === 1) {
316
- return "SPEC_VERSION";
317
- }
318
- // agent type enum
319
- if (setsEqual(allowedValues, ["builtin", "command"])) {
320
- return "AGENT_SHAPE";
321
- }
322
- // on_error enum
323
- if (setsEqual(allowedValues, ["fail", "retry", "skip"])) {
324
- return "STEP_ON_ERROR";
325
- }
326
- // on_timeout enum
327
- if (setsEqual(allowedValues, ["fail", "default"])) {
328
- return "SIGNAL_ON_TIMEOUT";
329
- }
330
- // join enum (parallel: all, race)
331
- if (setsEqual(allowedValues, ["all", "race"])) {
332
- return "JOIN_VALUE";
333
- }
334
- // join enum (fanout: all, race, quorum)
335
- if (setsEqual(allowedValues, ["all", "race", "quorum"])) {
336
- return "JOIN_VALUE";
337
- }
338
- // capture.from enum
339
- if (setsEqual(allowedValues, ["stdout", "file"])) {
340
- return "CAPTURE_FROM";
341
- }
342
- // capture.parse enum
343
- if (setsEqual(allowedValues, ["json", "text"])) {
344
- return "CAPTURE_PARSE";
345
- }
346
- // guard action enum
347
- if (setsEqual(allowedValues, ["continue", "fail", "complete"])) {
348
- return "GUARD_ACTION";
349
- }
350
- return "SPEC_SHAPE";
351
- }
352
- function classifyConst(err, _path) {
353
- // run: "agent" or run: "program" const — these are artifacts of oneOf
354
- // branch matching and don't represent real user errors by themselves.
355
- return "STEP_KIND";
356
- }
357
- function classifyType(err, path) {
358
- const ctx = pathContext(path);
359
- // Step id type error
360
- if (path.endsWith(".id")) {
361
- return "STEP_ID";
362
- }
363
- // on_error type error (e.g. null instead of string)
364
- if (/\.on_error$/.test(path)) {
365
- return "STEP_ON_ERROR";
366
- }
367
- // timeout type error in step context
368
- if (/\.timeout$/.test(path) && ctx === "step") {
369
- return "STEP_TIMEOUT";
370
- }
371
- // capture type error (not an object)
372
- if (/\.capture$/.test(path)) {
373
- return "CAPTURE_SHAPE";
374
- }
375
- // fanout.over type error (not string, not array)
376
- if (/\.fanout\.over$/.test(path)) {
377
- return "FANOUT_OVER_TYPE";
378
- }
379
- if (/\.if\.condition$/.test(path)) {
380
- return "STEP_SHAPE";
381
- }
382
- if (/\.if\.(then|else)$/.test(path)) {
383
- return "STEP_SHAPE";
384
- }
385
- if (/\.pipeline$/.test(path)) {
386
- return "STEP_SHAPE";
387
- }
388
- if (/\.guard\.when$/.test(path)) {
389
- return "GUARD_WHEN_TYPE";
390
- }
391
- if (/\.guard\.message$/.test(path)) {
392
- return "GUARD_MESSAGE";
393
- }
394
- // Agent type errors
395
- if (ctx === "agent") {
396
- return "AGENT_SHAPE";
397
- }
398
- // Output type error
399
- if (path.endsWith(".output")) {
400
- return "OUTPUT_SHAPE";
401
- }
402
- return "SPEC_SHAPE";
403
- }
404
- function classifyMinimum(err, path) {
405
- if (/\.if\.(then|else)$/.test(path)) {
406
- return "STEP_SHAPE";
407
- }
408
- // retry.max minimum violation
409
- if (/\.retry\.max$/.test(path)) {
410
- return "RETRY_SHAPE";
411
- }
412
- // fanout success_criteria min_success minimum violation
413
- if (/\.success_criteria\.min_success$/.test(path)) {
414
- return "FANOUT_SUCCESS_CRITERIA";
415
- }
416
- // timeout exclusiveMinimum violation (negative number)
417
- if (/\.timeout$/.test(path)) {
418
- return "STEP_TIMEOUT";
419
- }
420
- return "SPEC_SHAPE";
421
- }
422
- function formatMessage(err, code, path) {
423
- switch (err.keyword) {
424
- case "additionalProperties": {
425
- const additionalProp = err.params.additionalProperty;
426
- const ctx = pathContext(path);
427
- if (ctx === "agent") {
428
- return `Unknown agent property '${additionalProp}'.`;
429
- }
430
- if (ctx === "step") {
431
- return `Unknown step property '${additionalProp}'.`;
432
- }
433
- if (ctx === "capture") {
434
- return `Unknown capture property '${additionalProp}'.`;
435
- }
436
- if (ctx === "retry") {
437
- return `Unknown retry property '${additionalProp}'.`;
438
- }
439
- return `Unknown property '${additionalProp}'.`;
440
- }
441
- case "required": {
442
- const missing = err.params.missingProperty;
443
- return formatRequiredMessage(missing, code, path);
444
- }
445
- case "enum": {
446
- const allowed = err.params.allowedValues;
447
- return `Must be one of ${allowed.join(", ")}.`;
448
- }
449
- case "const": {
450
- const value = err.params.allowedValue;
451
- return `Must be ${JSON.stringify(value)}.`;
452
- }
453
- case "type": {
454
- const expected = err.params ? err.params.type : "unknown";
455
- return formatTypeMessage(code, path, expected);
456
- }
457
- case "minimum":
458
- case "exclusiveMinimum": {
459
- return formatMinimumMessage(code, path);
460
- }
461
- case "minItems": {
462
- return formatMinimumMessage(code, path);
463
- }
464
- case "pattern": {
465
- if (code === "STEP_ID_INVALID")
466
- return "Author ids must match ^[A-Za-z_][A-Za-z0-9_-]*$ and must not start with '$'.";
467
- return err.message ?? "Schema validation error.";
468
- }
469
- default:
470
- return err.message ?? "Schema validation error.";
471
- }
472
- }
473
- function formatRequiredMessage(missing, code, _path) {
474
- switch (code) {
475
- case "SPEC_VERSION":
476
- return "Only DSL version 1 is supported.";
477
- case "STEP_ID":
478
- return "Every workflow step must define a non-empty string id.";
479
- case "AGENT_SHAPE":
480
- return "Agent definition (type builtin/command) must define a non-empty use.";
481
- case "AGENT_PROMPT":
482
- return "run: agent steps must define a prompt string.";
483
- case "PROGRAM_CMD":
484
- return "run: program steps must define cmd as a string or array.";
485
- case "FANOUT_OVER":
486
- return "fanout.over is required.";
487
- case "FANOUT_DO":
488
- return "fanout.do must be an array of steps.";
489
- case "PARALLEL_DO":
490
- return "parallel entries are branch descriptors { id, do }, not direct steps; wrap branch steps under do.";
491
- case "FANOUT_QUORUM":
492
- return "fanout.quorum must be a positive integer when join is quorum.";
493
- case "STEP_SHAPE":
494
- if (missing === "condition")
495
- return "if.condition is required.";
496
- if (missing === "then")
497
- return "if.then must be a non-empty array of steps.";
498
- return `Missing required property '${missing}'.`;
499
- case "CAPTURE_FROM":
500
- return "run: program capture.from must be stdout or file.";
501
- case "CAPTURE_PARSE":
502
- return "run: program capture.parse must be json or text.";
503
- case "CAPTURE_PATH":
504
- return "run: program capture.path must be a string when capture.from is file.";
505
- case "SIGNAL_PROMPT":
506
- return "run: signal steps must define a prompt string.";
507
- case "SIGNAL_TIMEOUT":
508
- return "signal.timeout must be a duration string.";
509
- case "SIGNAL_ON_TIMEOUT":
510
- return "signal.on_timeout must be fail or default, and is required when timeout is set.";
511
- case "SIGNAL_DEFAULT":
512
- return "signal.default is required when on_timeout is default.";
513
- case "LOOP_MAX_ITERATIONS":
514
- return "loop.max_iterations must be a number.";
515
- case "GUARD_WHEN":
516
- return "guard.when is required.";
517
- case "GUARD_ACTION":
518
- return "guard.then and guard.else must be one of continue, fail, or complete.";
519
- case "RETRY_SHAPE":
520
- return "retry.max must be a non-negative integer.";
521
- default:
522
- return `Missing required property '${missing}'.`;
523
- }
524
- }
525
- function formatTypeMessage(code, path, expectedType) {
526
- switch (code) {
527
- case "STEP_ON_ERROR":
528
- return `step.on_error must be a string, one of fail, retry, skip.`;
529
- case "STEP_TIMEOUT":
530
- return `step.timeout must be a duration string or number (ms).`;
531
- case "CAPTURE_SHAPE":
532
- return `run: program capture must be an object when present.`;
533
- case "OUTPUT_SHAPE":
534
- if (path.includes("agent")) {
535
- return `run: agent output must be an object when present.`;
536
- }
537
- return `run: program output must be an object when present.`;
538
- case "FANOUT_OVER_TYPE":
539
- return `fanout.over must be an array or CEL expression string.`;
540
- case "STEP_SHAPE":
541
- if (/\.if\.condition$/.test(path))
542
- return `if.condition must be a boolean or CEL expression string.`;
543
- if (/\.if\.then$/.test(path))
544
- return `if.then must be a non-empty array of steps.`;
545
- if (/\.if\.else$/.test(path))
546
- return `if.else must be a non-empty array of steps when present.`;
547
- return `Must be of type ${expectedType}.`;
548
- case "GUARD_WHEN_TYPE":
549
- return `guard.when must be a boolean or CEL expression string.`;
550
- case "GUARD_MESSAGE":
551
- return `guard.message must be a string template.`;
552
- default:
553
- return `Must be of type ${expectedType}.`;
554
- }
555
- }
556
- function formatMinimumMessage(code, _path) {
557
- switch (code) {
558
- case "STEP_SHAPE":
559
- if (/\.if\.then$/.test(_path))
560
- return "if.then must be a non-empty array of steps.";
561
- if (/\.if\.else$/.test(_path))
562
- return "if.else must be a non-empty array of steps when present.";
563
- return "Value must be a positive integer.";
564
- case "RETRY_SHAPE":
565
- return "retry.max must be a non-negative integer.";
566
- case "FANOUT_SUCCESS_CRITERIA":
567
- return "fanout.success_criteria.min_success must be a positive integer.";
568
- case "STEP_TIMEOUT":
569
- return "step.timeout number must be positive (milliseconds).";
570
- default:
571
- return "Value must be a positive integer.";
572
- }
573
- }
574
- /** Convert Ajv instancePath ("/workflow/steps/0/run") to $-path ("$.workflow.steps[0].run") */
575
- function toPath(instancePath) {
576
- if (!instancePath)
577
- return "$";
578
- // Split on "/" and convert array indices
579
- const parts = instancePath.split("/").filter(Boolean);
580
- let result = "$";
581
- for (const part of parts) {
582
- if (/^\d+$/.test(part)) {
583
- result += `[${part}]`;
584
- }
585
- else {
586
- result += `.${part}`;
587
- }
588
- }
589
- return result;
590
- }
591
- /** Determine the semantic context of a path for error classification. */
592
- function pathContext(path) {
593
- // Match patterns like $.agents.xxx or $.workflow.steps[n].fanout etc.
594
- // Note: there is no "signal" context — a `run: signal` step shares the flat
595
- // `$.workflow.steps[n]` path with agent/program steps, so it resolves to
596
- // "step". Signal-specific diagnostics are owned by the compiler
597
- // (validateSignalStep), not the schema-validator.
598
- if (/\.agents\.[^.]+$/.test(path) || /\.agents\.[^.]+\./.test(path)) {
599
- return "agent";
600
- }
601
- // A step entry itself — top-level `steps[n]` or a nested composite child
602
- // (`do[n]` / `then[n]` / `else[n]` / `parallel[n]`). Checked before the composite-keyword cases so a
603
- // nested step entry resolves to "step" rather than its enclosing composite.
604
- if (/\.(steps|do|then|else|parallel)\[\d+\]$/.test(path)) {
605
- return "step";
606
- }
607
- if (/\.fanout\b/.test(path)) {
608
- if (/\.fanout\.success_criteria/.test(path))
609
- return "success-criteria";
610
- return "fanout";
611
- }
612
- if (/\.if\b/.test(path)) {
613
- return "if";
614
- }
615
- if (/\.capture\b/.test(path)) {
616
- return "capture";
617
- }
618
- if (/\.retry\b/.test(path)) {
619
- return "retry";
620
- }
621
- if (/\.switch\b/.test(path) && /\.cases\[\d+\]$/.test(path)) {
622
- return "switch-case";
623
- }
624
- if (/\.switch\b/.test(path)) {
625
- return "switch-spec";
626
- }
627
- if (/\.guard\b/.test(path)) {
628
- return "guard";
629
- }
630
- if (/\.loop\b/.test(path)) {
631
- return "loop";
632
- }
633
- if (/\.workflow\b/.test(path)) {
634
- return "workflow";
635
- }
636
- if (/\.steps\[\d+\]/.test(path)) {
637
- return "step";
638
- }
639
- return "top";
640
- }
641
- /** Check if an instancePath looks like a step (an entry in a steps/do/parallel list). */
642
- function isStepPath(instancePath) {
643
- // Steps live directly in `workflow.steps` or nested body lists (`do`,
644
- // `then`, `else`, or `parallel`). Each is an array whose entries are full
645
- // Nodes, so a step entry path ends with one of those array names followed by
646
- // a numeric index.
647
- return /\/(steps|do|then|else|parallel)\/\d+$/.test(instancePath);
648
- }
649
- /** Check if errPath is a sub-path of any of the given parent paths. */
650
- function isSubErrorOf(errPath, parentPaths) {
651
- for (const parent of parentPaths) {
652
- if (errPath === parent || errPath.startsWith(parent + "/")) {
653
- return true;
654
- }
655
- }
656
- return false;
657
- }
658
- /** Set equality check for enum classification. */
659
- function setsEqual(a, b) {
660
- if (a.length !== b.length)
661
- return false;
662
- const sortedA = [...a].sort();
663
- const sortedB = [...b].sort();
664
- return sortedA.every((v, i) => v === sortedB[i]);
665
- }
666
- //# sourceMappingURL=schema-validator.js.map