intentdna 1.9.0 → 1.9.3

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 (131) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +227 -87
  4. package/dist/cli/commands/run-lifecycle.d.ts +61 -11
  5. package/dist/cli/commands/run-lifecycle.js +184 -50
  6. package/dist/cli/commands/run-observer.d.ts +15 -0
  7. package/dist/cli/commands/run-observer.js +210 -0
  8. package/dist/cli/commands/run.d.ts +16 -20
  9. package/dist/cli/commands/run.js +461 -659
  10. package/dist/cli/commands/sync.js +16 -12
  11. package/dist/cli/index.js +14 -5
  12. package/dist/compiler/compile.d.ts +1 -0
  13. package/dist/compiler/compile.js +1 -1
  14. package/dist/compiler/controller.d.ts +16 -0
  15. package/dist/compiler/controller.js +693 -0
  16. package/dist/compiler/input-resolver.d.ts +2 -0
  17. package/dist/compiler/input-resolver.js +22 -6
  18. package/dist/hooks/cli.d.ts +45 -0
  19. package/dist/hooks/cli.js +263 -31
  20. package/dist/hooks/host-event.d.ts +57 -0
  21. package/dist/hooks/host-event.js +187 -0
  22. package/dist/hooks/index.d.ts +1 -0
  23. package/dist/hooks/index.js +1 -0
  24. package/dist/hooks/protocol.d.ts +7 -0
  25. package/dist/hooks/schema.d.ts +1 -0
  26. package/dist/hooks/schema.js +89 -1
  27. package/dist/hooks/state.d.ts +23 -1
  28. package/dist/hooks/state.js +9 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +1 -0
  31. package/dist/mcp/index.js +2 -0
  32. package/dist/mcp/tools-run.d.ts +4 -0
  33. package/dist/mcp/tools-run.js +419 -0
  34. package/dist/mcp/tools-state.d.ts +1 -1
  35. package/dist/mcp/tools-state.js +13 -241
  36. package/dist/runtime/artifact-store.d.ts +146 -0
  37. package/dist/runtime/artifact-store.js +1436 -0
  38. package/dist/runtime/canonical-attempt-outcome.d.ts +12 -0
  39. package/dist/runtime/canonical-attempt-outcome.js +195 -0
  40. package/dist/runtime/canonical-json.d.ts +10 -0
  41. package/dist/runtime/canonical-json.js +105 -0
  42. package/dist/runtime/canonical-run-application.d.ts +93 -0
  43. package/dist/runtime/canonical-run-application.js +229 -0
  44. package/dist/runtime/canonical-run-service.d.ts +454 -0
  45. package/dist/runtime/canonical-run-service.js +4760 -0
  46. package/dist/runtime/canonical-runtime-composition.d.ts +22 -0
  47. package/dist/runtime/canonical-runtime-composition.js +34 -0
  48. package/dist/runtime/canonical-runtime-projection.d.ts +3 -0
  49. package/dist/runtime/canonical-runtime-projection.js +4 -0
  50. package/dist/runtime/canonical-target-compiler.d.ts +46 -0
  51. package/dist/runtime/canonical-target-compiler.js +514 -0
  52. package/dist/runtime/claude-agent-identity.d.ts +2 -0
  53. package/dist/runtime/claude-agent-identity.js +21 -0
  54. package/dist/runtime/executable-run-plan.d.ts +328 -0
  55. package/dist/runtime/executable-run-plan.js +1485 -0
  56. package/dist/runtime/execution-authority.d.ts +83 -0
  57. package/dist/runtime/execution-authority.js +98 -0
  58. package/dist/runtime/handoff-resolver.js +4 -1
  59. package/dist/runtime/harness-pull-adapter.d.ts +146 -0
  60. package/dist/runtime/harness-pull-adapter.js +315 -0
  61. package/dist/runtime/index.d.ts +39 -14
  62. package/dist/runtime/index.js +29 -7
  63. package/dist/runtime/local-execution-authority.d.ts +194 -0
  64. package/dist/runtime/local-execution-authority.js +2897 -0
  65. package/dist/runtime/local-execution-reconciliation.d.ts +20 -0
  66. package/dist/runtime/local-execution-reconciliation.js +107 -0
  67. package/dist/runtime/local-execution-supervisor-script.d.ts +7 -0
  68. package/dist/runtime/local-execution-supervisor-script.js +793 -0
  69. package/dist/runtime/local-provider-sandbox.d.ts +42 -0
  70. package/dist/runtime/local-provider-sandbox.js +154 -0
  71. package/dist/runtime/local-verifier-execution.d.ts +24 -0
  72. package/dist/runtime/local-verifier-execution.js +290 -0
  73. package/dist/runtime/local-windows-execution-supervisor-script.d.ts +6 -0
  74. package/dist/runtime/local-windows-execution-supervisor-script.js +788 -0
  75. package/dist/runtime/plan-store.d.ts +30 -0
  76. package/dist/runtime/plan-store.js +231 -0
  77. package/dist/runtime/process-tree.d.ts +9 -0
  78. package/dist/runtime/process-tree.js +97 -24
  79. package/dist/runtime/providers/codex.js +2 -2
  80. package/dist/runtime/push-driver.d.ts +184 -0
  81. package/dist/runtime/push-driver.js +1002 -0
  82. package/dist/runtime/result-store.d.ts +34 -3
  83. package/dist/runtime/result-store.js +1073 -2
  84. package/dist/runtime/run-binding.d.ts +68 -0
  85. package/dist/runtime/run-binding.js +278 -0
  86. package/dist/runtime/run-contracts.d.ts +575 -0
  87. package/dist/runtime/run-contracts.js +58 -7
  88. package/dist/runtime/run-controller.d.ts +1 -6
  89. package/dist/runtime/run-controller.js +0 -41
  90. package/dist/runtime/run-store.d.ts +85 -7
  91. package/dist/runtime/run-store.js +2528 -186
  92. package/dist/runtime/skill-adapter.d.ts +18 -7
  93. package/dist/runtime/skill-adapter.js +100 -742
  94. package/dist/runtime/structured-output-validator.d.ts +44 -0
  95. package/dist/runtime/structured-output-validator.js +171 -0
  96. package/dist/runtime/verifier-command-binding.d.ts +22 -0
  97. package/dist/runtime/verifier-command-binding.js +162 -0
  98. package/dist/runtime/verifier.d.ts +46 -6
  99. package/dist/runtime/verifier.js +355 -53
  100. package/dist/runtime/windows-job-keeper.d.ts +47 -0
  101. package/dist/runtime/windows-job-keeper.js +231 -0
  102. package/dist/runtime/worker-executor.d.ts +17 -1
  103. package/dist/runtime/worker-executor.js +26 -5
  104. package/dist/runtime/workflow-plan-adapter.d.ts +19 -0
  105. package/dist/runtime/workflow-plan-adapter.js +502 -6
  106. package/dist/runtime/workflow-runtime-manifest.d.ts +1 -0
  107. package/dist/runtime/workflow-runtime-manifest.js +5 -0
  108. package/dist/runtime/workspace-isolation.d.ts +29 -2
  109. package/dist/runtime/workspace-isolation.js +488 -11
  110. package/dist/runtime/workspace-observation.d.ts +44 -0
  111. package/dist/runtime/workspace-observation.js +216 -0
  112. package/dist/schema/controller-registry.d.ts +0 -7
  113. package/dist/schema/controller-registry.js +0 -8
  114. package/dist/schema/types.d.ts +84 -10
  115. package/dist/schema/types.js +2 -0
  116. package/dist/schema/validate.js +151 -1
  117. package/dist/schema/validators/controllers.d.ts +1 -1
  118. package/dist/schema/validators/controllers.js +126 -41
  119. package/dist/schema/workflow-authoring-contract.js +2 -0
  120. package/dist/schema/yaml-parser.js +1 -1
  121. package/dist/templates/flutter-rewrite-new.dna.yaml +1 -0
  122. package/dist/templates/flutter-rewrite.dna.yaml +87 -60
  123. package/dist/templates/safe-refactoring.dna.yaml +1 -0
  124. package/native/windows-job-keeper/README.md +80 -0
  125. package/native/windows-job-keeper/bin/aarch64/intentdna-windows-job-keeper.exe +0 -0
  126. package/native/windows-job-keeper/bin/x86_64/intentdna-windows-job-keeper.exe +0 -0
  127. package/native/windows-job-keeper/keeper.c +979 -0
  128. package/native/windows-job-keeper/manifest.json +33 -0
  129. package/package.json +6 -2
  130. package/scripts/build-windows-job-keeper.mjs +172 -0
  131. package/scripts/verify-windows-job-keeper.mjs +184 -0
@@ -0,0 +1,1485 @@
1
+ import { createHash } from "node:crypto";
2
+ import { isAbsolute, win32 } from "node:path";
3
+ import { canonicalizeJson, } from "./canonical-json.js";
4
+ export const EXECUTABLE_RUN_PLAN_SCHEMA_VERSION = "intentdna.executable_run_plan.v1";
5
+ export const STORED_EXECUTABLE_RUN_PLAN_SCHEMA_VERSION = "intentdna.stored_executable_run_plan.v1";
6
+ export const EXECUTABLE_RETRYABLE_OUTCOME_VALUES = [
7
+ "non_zero_exit",
8
+ "malformed_result",
9
+ "timeout",
10
+ "cancelled",
11
+ "signal",
12
+ "launch_error",
13
+ ];
14
+ export const EXECUTABLE_ARTIFACT_KIND_VALUES = [
15
+ "file",
16
+ "directory",
17
+ "test_result",
18
+ "git_commit",
19
+ "summary",
20
+ "state",
21
+ ];
22
+ export class ExecutableRunPlanError extends TypeError {
23
+ path;
24
+ constructor(path, message) {
25
+ super(`${path}: ${message}`);
26
+ this.name = "ExecutableRunPlanError";
27
+ this.path = path;
28
+ }
29
+ }
30
+ function record(value, path, fields) {
31
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
32
+ throw new ExecutableRunPlanError(path, "must be an object");
33
+ }
34
+ const prototype = Object.getPrototypeOf(value);
35
+ if (prototype !== Object.prototype && prototype !== null) {
36
+ throw new ExecutableRunPlanError(path, "must be a plain object");
37
+ }
38
+ const unknown = Object.keys(value).filter((key) => !fields.includes(key));
39
+ if (unknown.length > 0) {
40
+ throw new ExecutableRunPlanError(path, `unknown field '${unknown[0]}'`);
41
+ }
42
+ return value;
43
+ }
44
+ function required(value, key, path) {
45
+ if (!Object.prototype.hasOwnProperty.call(value, key)) {
46
+ throw new ExecutableRunPlanError(path, `missing field '${key}'`);
47
+ }
48
+ return value[key];
49
+ }
50
+ function string(value, path) {
51
+ if (typeof value !== "string" || value.length === 0) {
52
+ throw new ExecutableRunPlanError(path, "must be a non-empty string");
53
+ }
54
+ }
55
+ function boolean(value, path) {
56
+ if (typeof value !== "boolean")
57
+ throw new ExecutableRunPlanError(path, "must be boolean");
58
+ }
59
+ function enumeration(value, values, path) {
60
+ if (typeof value !== "string" || !values.includes(value)) {
61
+ throw new ExecutableRunPlanError(path, `must be one of ${values.join(", ")}`);
62
+ }
63
+ }
64
+ function integer(value, path, minimum = 0) {
65
+ if (!Number.isSafeInteger(value) || value < minimum) {
66
+ throw new ExecutableRunPlanError(path, `must be a safe integer >= ${minimum}`);
67
+ }
68
+ }
69
+ function array(value, path) {
70
+ if (!Array.isArray(value))
71
+ throw new ExecutableRunPlanError(path, "must be an array");
72
+ }
73
+ function stringArray(value, path) {
74
+ array(value, path);
75
+ value.forEach((item, index) => string(item, `${path}[${index}]`));
76
+ }
77
+ function relativePath(value, path) {
78
+ string(value, path);
79
+ if (isAbsolute(value) || win32.isAbsolute(value)) {
80
+ throw new ExecutableRunPlanError(path, "must be relative");
81
+ }
82
+ if (value.includes("\0") || value.includes("\\")) {
83
+ throw new ExecutableRunPlanError(path, "must be a portable relative path");
84
+ }
85
+ const components = value.split("/");
86
+ if (components.includes("..")
87
+ || (value !== "." && components.some((component) => component === "" || component === "."))) {
88
+ throw new ExecutableRunPlanError(path, "must not traverse outside the workspace");
89
+ }
90
+ }
91
+ function digest(value, path) {
92
+ if (typeof value !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(value)) {
93
+ throw new ExecutableRunPlanError(path, "must be a lowercase sha256 digest");
94
+ }
95
+ }
96
+ function unique(values, path) {
97
+ if (new Set(values).size !== values.length) {
98
+ throw new ExecutableRunPlanError(path, "must not contain duplicates");
99
+ }
100
+ }
101
+ function containsUnescapedArguments(value) {
102
+ let offset = value.indexOf("$ARGUMENTS");
103
+ while (offset >= 0) {
104
+ let slashCount = 0;
105
+ for (let index = offset - 1; index >= 0 && value[index] === "\\"; index -= 1) {
106
+ slashCount += 1;
107
+ }
108
+ if (slashCount % 2 === 0)
109
+ return true;
110
+ offset = value.indexOf("$ARGUMENTS", offset + "$ARGUMENTS".length);
111
+ }
112
+ return false;
113
+ }
114
+ function verifierTemplateProjection(verifier) {
115
+ switch (verifier.kind) {
116
+ case "artifact_exists":
117
+ case "artifact_non_empty":
118
+ return { relative_path_template: verifier.relative_path_template };
119
+ case "artifact_contains":
120
+ return { relative_path_template: verifier.relative_path_template };
121
+ case "evaluator_result":
122
+ return { relative_path_template: verifier.relative_path_template };
123
+ case "structured_output":
124
+ return {};
125
+ case "command":
126
+ return {
127
+ command: verifier.command,
128
+ relative_working_directory: verifier.relative_working_directory,
129
+ observation_policy: verifier.observation_policy ?? "historical_execution",
130
+ };
131
+ case "checkpoint":
132
+ return {
133
+ command: verifier.command,
134
+ message: verifier.message,
135
+ observation_policy: verifier.observation_policy ?? "historical_execution",
136
+ };
137
+ }
138
+ }
139
+ function rejectRuntimeTemplates(value, path) {
140
+ const names = new Set();
141
+ collectRuntimeTemplateInputs(value, path, [], names);
142
+ if (names.size > 0) {
143
+ throw new ExecutableRunPlanError(path, `must not contain runtime input templates: ${[...names].sort().join(", ")}`);
144
+ }
145
+ }
146
+ function collectRuntimeTemplateInputs(value, path, reservedTokens, names) {
147
+ if (typeof value === "string") {
148
+ let remaining = value;
149
+ for (const token of reservedTokens) {
150
+ remaining = remaining.split(token).join("");
151
+ }
152
+ remaining = remaining.replace(/\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}/gu, (_match, name) => {
153
+ names.add(name);
154
+ return "";
155
+ });
156
+ if (remaining.includes("{{")) {
157
+ const placeholderStart = remaining.indexOf("{{");
158
+ const placeholderEnd = remaining.indexOf("}}", placeholderStart) + 2;
159
+ const placeholder = remaining.slice(Math.max(0, placeholderStart), placeholderEnd > 1 ? placeholderEnd : undefined);
160
+ throw new ExecutableRunPlanError(path, `contains an unsupported template placeholder '${placeholder}'`);
161
+ }
162
+ if (containsUnescapedArguments(remaining))
163
+ names.add("ARGUMENTS");
164
+ return;
165
+ }
166
+ if (Array.isArray(value)) {
167
+ value.forEach((item, index) => collectRuntimeTemplateInputs(item, `${path}[${index}]`, reservedTokens, names));
168
+ return;
169
+ }
170
+ if (value !== null && typeof value === "object") {
171
+ for (const [key, item] of Object.entries(value)) {
172
+ collectRuntimeTemplateInputs(item, `${path}.${key}`, reservedTokens, names);
173
+ }
174
+ }
175
+ }
176
+ export function deriveWorkerRuntimeInputNames(worker, verifiers, handoffs, path = "$.worker") {
177
+ const reservedTokens = handoffs
178
+ .filter((handoff) => handoff.to_node_id === worker.node_id)
179
+ .flatMap((handoff) => {
180
+ const prefix = `{{handoff.${handoff.handoff_id}.`;
181
+ if (handoff.mode === "reference") {
182
+ if (handoff.artifact_kind === "git_commit") {
183
+ return [`${prefix}commit_oid}}`, `${prefix}artifact_id}}`];
184
+ }
185
+ return [`${prefix}materialized_path}}`, `${prefix}artifact_id}}`];
186
+ }
187
+ if (handoff.mode === "quote")
188
+ return [`${prefix}exact_text}}`];
189
+ return [`${prefix}json}}`];
190
+ });
191
+ const projection = {
192
+ packet_template: {
193
+ standalone_prompt: worker.packet_template.standalone_prompt,
194
+ constraints: worker.packet_template.constraints,
195
+ handoff_input_descriptions: worker.packet_template.handoff_inputs.map((input) => input.description),
196
+ handoff_input_paths: worker.packet_template.handoff_inputs.map((input) => input.relative_path_template),
197
+ output_descriptions: worker.packet_template.output_contract.map((output) => output.description),
198
+ output_paths: worker.packet_template.output_contract.map((output) => output.relative_path_template),
199
+ response_path: worker.packet_template.response_contract.kind === "document"
200
+ ? worker.packet_template.response_contract.relative_path_template
201
+ : null,
202
+ converter_command: worker.packet_template.response_contract.kind === "document"
203
+ ? worker.packet_template.response_contract.converter_command
204
+ : null,
205
+ },
206
+ workspace: {
207
+ relative_working_directory: worker.workspace.relative_working_directory,
208
+ readable_path_templates: worker.workspace.readable_path_templates,
209
+ writable_path_templates: worker.workspace.writable_path_templates,
210
+ },
211
+ verifiers: verifiers.map(verifierTemplateProjection),
212
+ };
213
+ rejectRuntimeTemplates({
214
+ role: worker.role,
215
+ handoff_input_identity: worker.packet_template.handoff_inputs.map((input) => ({
216
+ name: input.name,
217
+ schema_ref: input.schema_ref,
218
+ })),
219
+ output_identity: worker.packet_template.output_contract.map((output) => ({
220
+ name: output.name,
221
+ schema_ref: output.schema_ref,
222
+ })),
223
+ response_schema_ref: worker.packet_template.response_contract.kind === "named"
224
+ ? worker.packet_template.response_contract.schema_ref
225
+ : null,
226
+ response_required_sections: worker.packet_template.response_contract.kind === "document"
227
+ ? worker.packet_template.response_contract.required_sections
228
+ : null,
229
+ tool_policy: worker.tool_policy,
230
+ }, `${path}.non_parameterizable_contract`);
231
+ const names = new Set();
232
+ collectRuntimeTemplateInputs(projection, path, reservedTokens, names);
233
+ return [...names].sort();
234
+ }
235
+ function validateInputContract(value, path) {
236
+ const input = record(value, path, ["inputs", "allow_unknown_inputs"]);
237
+ if (required(input, "allow_unknown_inputs", path) !== false) {
238
+ throw new ExecutableRunPlanError(`${path}.allow_unknown_inputs`, "must be false");
239
+ }
240
+ const inputs = required(input, "inputs", path);
241
+ array(inputs, `${path}.inputs`);
242
+ const names = [];
243
+ inputs.forEach((item, index) => {
244
+ const itemPath = `${path}.inputs[${index}]`;
245
+ const definition = record(item, itemPath, [
246
+ "name", "type", "required", "sensitive", "description", "default_value",
247
+ ]);
248
+ string(required(definition, "name", itemPath), `${itemPath}.name`);
249
+ names.push(definition.name);
250
+ const inputType = required(definition, "type", itemPath);
251
+ enumeration(inputType, [
252
+ "string", "number", "boolean", "json", "reference", "trusted_verifier_command",
253
+ ], `${itemPath}.type`);
254
+ boolean(required(definition, "required", itemPath), `${itemPath}.required`);
255
+ boolean(required(definition, "sensitive", itemPath), `${itemPath}.sensitive`);
256
+ string(required(definition, "description", itemPath), `${itemPath}.description`);
257
+ if (Object.prototype.hasOwnProperty.call(definition, "default_value")) {
258
+ if (definition.sensitive === true) {
259
+ throw new ExecutableRunPlanError(itemPath, "sensitive inputs cannot have defaults");
260
+ }
261
+ canonicalizeJson(definition.default_value);
262
+ if (((inputType === "string" || inputType === "reference" || inputType === "trusted_verifier_command") && typeof definition.default_value !== "string")
263
+ || (inputType === "number" && typeof definition.default_value !== "number")
264
+ || (inputType === "boolean" && typeof definition.default_value !== "boolean")) {
265
+ throw new ExecutableRunPlanError(`${itemPath}.default_value`, `must match input type ${inputType}`);
266
+ }
267
+ }
268
+ });
269
+ unique(names, `${path}.inputs names`);
270
+ }
271
+ function validateNullableRelativePath(value, path) {
272
+ if (value !== null)
273
+ relativePath(value, path);
274
+ }
275
+ function validateNullableString(value, path) {
276
+ if (value !== null)
277
+ string(value, path);
278
+ }
279
+ function expectedModeForArtifact(artifactKind) {
280
+ if (artifactKind === "summary")
281
+ return "quote";
282
+ if (artifactKind === "state")
283
+ return "structured";
284
+ return "reference";
285
+ }
286
+ function expectedOutputKindForArtifact(artifactKind) {
287
+ if (artifactKind === "summary")
288
+ return "text";
289
+ if (artifactKind === "state")
290
+ return "structured";
291
+ return "reference";
292
+ }
293
+ function validateWorkerHandoffInput(value, path) {
294
+ const input = record(value, path, [
295
+ "name",
296
+ "source",
297
+ "mode",
298
+ "artifact_kind",
299
+ "required",
300
+ "description",
301
+ "relative_path_template",
302
+ "schema_ref",
303
+ ]);
304
+ string(required(input, "name", path), `${path}.name`);
305
+ enumeration(required(input, "source", path), ["handoff", "external"], `${path}.source`);
306
+ const mode = required(input, "mode", path);
307
+ enumeration(mode, ["reference", "quote", "structured"], `${path}.mode`);
308
+ const artifactKind = required(input, "artifact_kind", path);
309
+ enumeration(artifactKind, EXECUTABLE_ARTIFACT_KIND_VALUES, `${path}.artifact_kind`);
310
+ if (mode !== expectedModeForArtifact(artifactKind)) {
311
+ throw new ExecutableRunPlanError(`${path}.mode`, `must match artifact kind ${artifactKind}`);
312
+ }
313
+ boolean(required(input, "required", path), `${path}.required`);
314
+ string(required(input, "description", path), `${path}.description`);
315
+ validateNullableRelativePath(required(input, "relative_path_template", path), `${path}.relative_path_template`);
316
+ validateNullableString(required(input, "schema_ref", path), `${path}.schema_ref`);
317
+ }
318
+ function validateWorkerOutput(value, path) {
319
+ const output = record(value, path, [
320
+ "name",
321
+ "kind",
322
+ "artifact_kind",
323
+ "required",
324
+ "description",
325
+ "relative_path_template",
326
+ "schema_ref",
327
+ ]);
328
+ string(required(output, "name", path), `${path}.name`);
329
+ const kind = required(output, "kind", path);
330
+ enumeration(kind, ["reference", "text", "structured"], `${path}.kind`);
331
+ const artifactKind = required(output, "artifact_kind", path);
332
+ enumeration(artifactKind, EXECUTABLE_ARTIFACT_KIND_VALUES, `${path}.artifact_kind`);
333
+ if (kind !== expectedOutputKindForArtifact(artifactKind)) {
334
+ throw new ExecutableRunPlanError(`${path}.kind`, `must match artifact kind ${artifactKind}`);
335
+ }
336
+ boolean(required(output, "required", path), `${path}.required`);
337
+ string(required(output, "description", path), `${path}.description`);
338
+ validateNullableRelativePath(required(output, "relative_path_template", path), `${path}.relative_path_template`);
339
+ validateNullableString(required(output, "schema_ref", path), `${path}.schema_ref`);
340
+ }
341
+ function validateWorkerResponseContract(value, path) {
342
+ const base = record(value, path, [
343
+ "kind",
344
+ "schema_ref",
345
+ "relative_path_template",
346
+ "required_sections",
347
+ "format",
348
+ "converter_command",
349
+ ]);
350
+ const kind = required(base, "kind", path);
351
+ enumeration(kind, ["none", "named", "document"], `${path}.kind`);
352
+ if (kind === "none") {
353
+ record(value, path, ["kind"]);
354
+ return;
355
+ }
356
+ if (kind === "named") {
357
+ const named = record(value, path, ["kind", "schema_ref"]);
358
+ string(required(named, "schema_ref", path), `${path}.schema_ref`);
359
+ return;
360
+ }
361
+ const document = record(value, path, [
362
+ "kind",
363
+ "relative_path_template",
364
+ "required_sections",
365
+ "format",
366
+ "converter_command",
367
+ ]);
368
+ validateNullableRelativePath(required(document, "relative_path_template", path), `${path}.relative_path_template`);
369
+ stringArray(required(document, "required_sections", path), `${path}.required_sections`);
370
+ unique(document.required_sections, `${path}.required_sections`);
371
+ const format = required(document, "format", path);
372
+ if (format !== null) {
373
+ enumeration(format, ["markdown", "pdf", "docx", "xlsx", "json", "csv"], `${path}.format`);
374
+ }
375
+ validateNullableString(required(document, "converter_command", path), `${path}.converter_command`);
376
+ }
377
+ function validateNode(value, path) {
378
+ const base = record(value, path, [
379
+ "kind", "node_id", "depends_on", "role", "packet_template", "workspace", "tool_policy",
380
+ "attempt_policy", "input_ports", "operation", "verifier_ids", "pass_route_ids", "fail_route_id",
381
+ "source", "allowed_values", "interaction_id", "prompt", "response_schema_ref",
382
+ "requires_attested_human", "terminal_id", "output_contract",
383
+ ]);
384
+ string(required(base, "node_id", path), `${path}.node_id`);
385
+ stringArray(required(base, "depends_on", path), `${path}.depends_on`);
386
+ unique(base.depends_on, `${path}.depends_on`);
387
+ const kind = required(base, "kind", path);
388
+ enumeration(kind, ["worker", "system", "gate", "decision", "interaction", "terminal"], `${path}.kind`);
389
+ if (kind === "worker") {
390
+ const exact = record(value, path, ["kind", "node_id", "depends_on", "role", "packet_template", "workspace", "tool_policy", "attempt_policy"]);
391
+ string(required(exact, "role", path), `${path}.role`);
392
+ const packet = record(required(exact, "packet_template", path), `${path}.packet_template`, [
393
+ "standalone_prompt",
394
+ "constraints",
395
+ "runtime_input_names",
396
+ "handoff_inputs",
397
+ "output_contract",
398
+ "response_contract",
399
+ ]);
400
+ string(required(packet, "standalone_prompt", `${path}.packet_template`), `${path}.packet_template.standalone_prompt`);
401
+ stringArray(required(packet, "constraints", `${path}.packet_template`), `${path}.packet_template.constraints`);
402
+ stringArray(required(packet, "runtime_input_names", `${path}.packet_template`), `${path}.packet_template.runtime_input_names`);
403
+ unique(packet.runtime_input_names, `${path}.packet_template.runtime_input_names`);
404
+ const handoffInputs = required(packet, "handoff_inputs", `${path}.packet_template`);
405
+ array(handoffInputs, `${path}.packet_template.handoff_inputs`);
406
+ handoffInputs.forEach((input, index) => validateWorkerHandoffInput(input, `${path}.packet_template.handoff_inputs[${index}]`));
407
+ unique(handoffInputs.map((input) => input.name), `${path}.packet_template.handoff_inputs names`);
408
+ const outputContract = required(packet, "output_contract", `${path}.packet_template`);
409
+ array(outputContract, `${path}.packet_template.output_contract`);
410
+ outputContract.forEach((output, index) => validateWorkerOutput(output, `${path}.packet_template.output_contract[${index}]`));
411
+ unique(outputContract.map((output) => output.name), `${path}.packet_template.output_contract names`);
412
+ validateWorkerResponseContract(required(packet, "response_contract", `${path}.packet_template`), `${path}.packet_template.response_contract`);
413
+ const workspace = record(required(exact, "workspace", path), `${path}.workspace`, [
414
+ "isolation",
415
+ "relative_working_directory",
416
+ "read_only",
417
+ "readable_path_templates",
418
+ "writable_path_templates",
419
+ "scope_relaxation",
420
+ ]);
421
+ enumeration(required(workspace, "isolation", `${path}.workspace`), ["none", "worktree"], `${path}.workspace.isolation`);
422
+ relativePath(required(workspace, "relative_working_directory", `${path}.workspace`), `${path}.workspace.relative_working_directory`);
423
+ boolean(required(workspace, "read_only", `${path}.workspace`), `${path}.workspace.read_only`);
424
+ stringArray(required(workspace, "readable_path_templates", `${path}.workspace`), `${path}.workspace.readable_path_templates`);
425
+ workspace.readable_path_templates.forEach((item, index) => relativePath(item, `${path}.workspace.readable_path_templates[${index}]`));
426
+ unique(workspace.readable_path_templates, `${path}.workspace.readable_path_templates`);
427
+ stringArray(required(workspace, "writable_path_templates", `${path}.workspace`), `${path}.workspace.writable_path_templates`);
428
+ workspace.writable_path_templates.forEach((item, index) => relativePath(item, `${path}.workspace.writable_path_templates[${index}]`));
429
+ unique(workspace.writable_path_templates, `${path}.workspace.writable_path_templates`);
430
+ const scopeRelaxation = required(workspace, "scope_relaxation", `${path}.workspace`);
431
+ if (scopeRelaxation !== null) {
432
+ const relaxation = record(scopeRelaxation, `${path}.workspace.scope_relaxation`, ["from_iteration", "effect"]);
433
+ integer(required(relaxation, "from_iteration", `${path}.workspace.scope_relaxation`), `${path}.workspace.scope_relaxation.from_iteration`, 1);
434
+ if (required(relaxation, "effect", `${path}.workspace.scope_relaxation`)
435
+ !== "disable_path_scope") {
436
+ throw new ExecutableRunPlanError(`${path}.workspace.scope_relaxation.effect`, "must be disable_path_scope");
437
+ }
438
+ }
439
+ if (workspace.read_only === true
440
+ && workspace.writable_path_templates.length > 0) {
441
+ throw new ExecutableRunPlanError(`${path}.workspace.writable_path_templates`, "read-only workers cannot declare writable paths");
442
+ }
443
+ if (workspace.read_only === true && scopeRelaxation !== null) {
444
+ throw new ExecutableRunPlanError(`${path}.workspace.scope_relaxation`, "read-only workers cannot relax scope");
445
+ }
446
+ const toolPolicy = record(required(exact, "tool_policy", path), `${path}.tool_policy`, ["allow", "deny"]);
447
+ const allowedTools = required(toolPolicy, "allow", `${path}.tool_policy`);
448
+ if (allowedTools !== null) {
449
+ stringArray(allowedTools, `${path}.tool_policy.allow`);
450
+ unique(allowedTools, `${path}.tool_policy.allow`);
451
+ }
452
+ stringArray(required(toolPolicy, "deny", `${path}.tool_policy`), `${path}.tool_policy.deny`);
453
+ unique(toolPolicy.deny, `${path}.tool_policy.deny`);
454
+ const deniedTools = new Set(toolPolicy.deny);
455
+ if (allowedTools !== null
456
+ && allowedTools.some((tool) => deniedTools.has(tool))) {
457
+ throw new ExecutableRunPlanError(`${path}.tool_policy`, "tool policy allow and deny lists must not overlap");
458
+ }
459
+ const attempt = record(required(exact, "attempt_policy", path), `${path}.attempt_policy`, ["timeout_ms", "cancellation_grace_ms", "max_attempts", "backoff", "initial_delay_ms", "max_delay_ms", "retryable_outcomes", "retry_segment_id"]);
460
+ if (required(attempt, "timeout_ms", `${path}.attempt_policy`) !== null)
461
+ integer(attempt.timeout_ms, `${path}.attempt_policy.timeout_ms`);
462
+ integer(required(attempt, "cancellation_grace_ms", `${path}.attempt_policy`), `${path}.attempt_policy.cancellation_grace_ms`);
463
+ integer(required(attempt, "max_attempts", `${path}.attempt_policy`), `${path}.attempt_policy.max_attempts`, 1);
464
+ enumeration(required(attempt, "backoff", `${path}.attempt_policy`), ["none", "linear", "exponential"], `${path}.attempt_policy.backoff`);
465
+ integer(required(attempt, "initial_delay_ms", `${path}.attempt_policy`), `${path}.attempt_policy.initial_delay_ms`);
466
+ if (required(attempt, "max_delay_ms", `${path}.attempt_policy`) !== null)
467
+ integer(attempt.max_delay_ms, `${path}.attempt_policy.max_delay_ms`);
468
+ stringArray(required(attempt, "retryable_outcomes", `${path}.attempt_policy`), `${path}.attempt_policy.retryable_outcomes`);
469
+ attempt.retryable_outcomes.forEach((outcome, index) => enumeration(outcome, EXECUTABLE_RETRYABLE_OUTCOME_VALUES, `${path}.attempt_policy.retryable_outcomes[${index}]`));
470
+ unique(attempt.retryable_outcomes, `${path}.attempt_policy.retryable_outcomes`);
471
+ if (required(attempt, "retry_segment_id", `${path}.attempt_policy`) !== null)
472
+ string(attempt.retry_segment_id, `${path}.attempt_policy.retry_segment_id`);
473
+ rejectRuntimeTemplates({
474
+ role: exact.role,
475
+ handoff_input_identity: handoffInputs.map((input) => ({
476
+ name: input.name,
477
+ schema_ref: input.schema_ref,
478
+ })),
479
+ output_identity: outputContract.map((output) => ({
480
+ name: output.name,
481
+ schema_ref: output.schema_ref,
482
+ })),
483
+ response_schema_ref: packet.response_contract.kind === "named"
484
+ ? packet.response_contract.schema_ref
485
+ : null,
486
+ response_required_sections: packet.response_contract.kind === "document"
487
+ ? packet.response_contract.required_sections
488
+ : null,
489
+ tool_policy: toolPolicy,
490
+ }, `${path}.non_parameterizable_contract`);
491
+ }
492
+ else if (kind === "system") {
493
+ const exact = record(value, path, ["kind", "node_id", "depends_on", "input_ports", "output_contract", "operation"]);
494
+ const inputPorts = required(exact, "input_ports", path);
495
+ array(inputPorts, `${path}.input_ports`);
496
+ inputPorts.forEach((item, index) => {
497
+ const portPath = `${path}.input_ports[${index}]`;
498
+ const port = record(item, portPath, ["name", "source"]);
499
+ string(required(port, "name", portPath), `${portPath}.name`);
500
+ enumeration(required(port, "source", portPath), ["run_input", "handoff"], `${portPath}.source`);
501
+ });
502
+ unique(inputPorts.map((port) => port.name), `${path}.input_ports`);
503
+ const outputContract = exact.output_contract ?? [];
504
+ array(outputContract, `${path}.output_contract`);
505
+ outputContract.forEach((item, index) => validateWorkerOutput(item, `${path}.output_contract[${index}]`));
506
+ unique(outputContract.map((output) => output.name), `${path}.output_contract names`);
507
+ const operation = record(required(exact, "operation", path), `${path}.operation`, [
508
+ "kind",
509
+ "source_plan_input",
510
+ "source_result_input",
511
+ "source_reference_input",
512
+ "expected_digest_input",
513
+ "output_name",
514
+ "mode_input",
515
+ "artifacts",
516
+ "required_outputs",
517
+ "source_diagnosis_result_input",
518
+ "source_review_result_input",
519
+ "policy_id",
520
+ "candidates",
521
+ "origin_output_name",
522
+ "handoff_id",
523
+ ]);
524
+ const operationKind = required(operation, "kind", `${path}.operation`);
525
+ enumeration(operationKind, [
526
+ "import_result",
527
+ "adopt_artifact",
528
+ "resolve_diagnosis_source",
529
+ "adopt_artifact_bundle",
530
+ "import_result_bundle",
531
+ "import_review",
532
+ "seal_result_bundle",
533
+ "materialize_handoff",
534
+ ], `${path}.operation.kind`);
535
+ const validateOutputMappings = (value, mappingPath) => {
536
+ array(value, mappingPath);
537
+ value.forEach((item, index) => {
538
+ const itemPath = `${mappingPath}[${index}]`;
539
+ const mapping = record(item, itemPath, ["source_output_name", "output_name", "source_node_id"]);
540
+ if (mapping.source_node_id !== undefined)
541
+ string(mapping.source_node_id, `${itemPath}.source_node_id`);
542
+ string(required(mapping, "source_output_name", itemPath), `${itemPath}.source_output_name`);
543
+ string(required(mapping, "output_name", itemPath), `${itemPath}.output_name`);
544
+ });
545
+ };
546
+ if (operationKind === "import_result" || operationKind === "adopt_artifact") {
547
+ const operationFields = operationKind === "import_result"
548
+ ? ["kind", "source_plan_input", "source_result_input", "output_name"]
549
+ : ["kind", "source_reference_input", "expected_digest_input", "output_name"];
550
+ const strictOperation = record(operation, `${path}.operation`, operationFields);
551
+ operationFields.filter((field) => field !== "kind").forEach((field) => string(required(strictOperation, field, `${path}.operation`), `${path}.operation.${field}`));
552
+ }
553
+ else if (operationKind === "resolve_diagnosis_source") {
554
+ const strictOperation = record(operation, `${path}.operation`, ["kind", "mode_input", "output_name"]);
555
+ string(required(strictOperation, "mode_input", `${path}.operation`), `${path}.operation.mode_input`);
556
+ string(required(strictOperation, "output_name", `${path}.operation`), `${path}.operation.output_name`);
557
+ }
558
+ else if (operationKind === "adopt_artifact_bundle") {
559
+ const strictOperation = record(operation, `${path}.operation`, ["kind", "artifacts"]);
560
+ const artifacts = required(strictOperation, "artifacts", `${path}.operation`);
561
+ array(artifacts, `${path}.operation.artifacts`);
562
+ artifacts.forEach((item, index) => {
563
+ const itemPath = `${path}.operation.artifacts[${index}]`;
564
+ const artifact = record(item, itemPath, ["source_reference_input", "expected_digest_input", "output_name", "artifact_kind"]);
565
+ ["source_reference_input", "expected_digest_input", "output_name"].forEach((field) => string(required(artifact, field, itemPath), `${itemPath}.${field}`));
566
+ enumeration(required(artifact, "artifact_kind", itemPath), ["file", "directory"], `${itemPath}.artifact_kind`);
567
+ });
568
+ }
569
+ else if (operationKind === "import_result_bundle") {
570
+ const strictOperation = record(operation, `${path}.operation`, ["kind", "source_plan_input", "source_result_input", "required_outputs"]);
571
+ string(required(strictOperation, "source_plan_input", `${path}.operation`), `${path}.operation.source_plan_input`);
572
+ string(required(strictOperation, "source_result_input", `${path}.operation`), `${path}.operation.source_result_input`);
573
+ validateOutputMappings(required(strictOperation, "required_outputs", `${path}.operation`), `${path}.operation.required_outputs`);
574
+ }
575
+ else if (operationKind === "import_review") {
576
+ const strictOperation = record(operation, `${path}.operation`, ["kind", "source_plan_input", "source_diagnosis_result_input", "source_review_result_input", "policy_id", "required_outputs"]);
577
+ for (const field of ["source_plan_input", "source_diagnosis_result_input", "source_review_result_input", "policy_id"]) {
578
+ string(required(strictOperation, field, `${path}.operation`), `${path}.operation.${field}`);
579
+ }
580
+ validateOutputMappings(required(strictOperation, "required_outputs", `${path}.operation`), `${path}.operation.required_outputs`);
581
+ }
582
+ else if (operationKind === "seal_result_bundle") {
583
+ const strictOperation = record(operation, `${path}.operation`, ["kind", "candidates", "origin_output_name"]);
584
+ if (strictOperation.origin_output_name !== undefined) {
585
+ string(strictOperation.origin_output_name, `${path}.operation.origin_output_name`);
586
+ }
587
+ const candidates = required(strictOperation, "candidates", `${path}.operation`);
588
+ array(candidates, `${path}.operation.candidates`);
589
+ candidates.forEach((item, index) => {
590
+ const itemPath = `${path}.operation.candidates[${index}]`;
591
+ const candidate = record(item, itemPath, ["required_node_ids", "outputs"]);
592
+ stringArray(required(candidate, "required_node_ids", itemPath), `${itemPath}.required_node_ids`);
593
+ validateOutputMappings(required(candidate, "outputs", itemPath), `${itemPath}.outputs`);
594
+ });
595
+ }
596
+ else {
597
+ const strictOperation = record(operation, `${path}.operation`, ["kind", "handoff_id"]);
598
+ string(required(strictOperation, "handoff_id", `${path}.operation`), `${path}.operation.handoff_id`);
599
+ }
600
+ }
601
+ else if (kind === "gate") {
602
+ const exact = record(value, path, ["kind", "node_id", "depends_on", "verifier_ids", "pass_route_ids", "fail_route_id"]);
603
+ stringArray(required(exact, "verifier_ids", path), `${path}.verifier_ids`);
604
+ unique(exact.verifier_ids, `${path}.verifier_ids`);
605
+ if (exact.verifier_ids.length === 0) {
606
+ throw new ExecutableRunPlanError(`${path}.verifier_ids`, "must not be empty");
607
+ }
608
+ stringArray(required(exact, "pass_route_ids", path), `${path}.pass_route_ids`);
609
+ unique(exact.pass_route_ids, `${path}.pass_route_ids`);
610
+ if (exact.pass_route_ids.length === 0) {
611
+ throw new ExecutableRunPlanError(`${path}.pass_route_ids`, "must not be empty");
612
+ }
613
+ string(required(exact, "fail_route_id", path), `${path}.fail_route_id`);
614
+ }
615
+ else if (kind === "decision") {
616
+ const exact = record(value, path, ["kind", "node_id", "depends_on", "source", "allowed_values"]);
617
+ const source = record(required(exact, "source", path), `${path}.source`, ["node_id", "output_name", "json_pointer"]);
618
+ ["node_id", "output_name", "json_pointer"].forEach((field) => string(required(source, field, `${path}.source`), `${path}.source.${field}`));
619
+ stringArray(required(exact, "allowed_values", path), `${path}.allowed_values`);
620
+ unique(exact.allowed_values, `${path}.allowed_values`);
621
+ }
622
+ else if (kind === "interaction") {
623
+ const exact = record(value, path, ["kind", "node_id", "depends_on", "interaction_id", "prompt", "response_schema_ref", "requires_attested_human"]);
624
+ ["interaction_id", "prompt", "response_schema_ref"].forEach((field) => string(required(exact, field, path), `${path}.${field}`));
625
+ rejectRuntimeTemplates({
626
+ interaction_id: exact.interaction_id,
627
+ prompt: exact.prompt,
628
+ response_schema_ref: exact.response_schema_ref,
629
+ }, `${path}.non_parameterizable_contract`);
630
+ if (required(exact, "requires_attested_human", path) !== true)
631
+ throw new ExecutableRunPlanError(`${path}.requires_attested_human`, "must be true");
632
+ }
633
+ else {
634
+ const exact = record(value, path, ["kind", "node_id", "depends_on", "terminal_id"]);
635
+ string(required(exact, "terminal_id", path), `${path}.terminal_id`);
636
+ }
637
+ }
638
+ function validateRoute(value, path) {
639
+ const route = record(value, path, ["route_id", "from_node_id", "to_node_id", "condition"]);
640
+ ["route_id", "from_node_id", "to_node_id"].forEach((field) => string(required(route, field, path), `${path}.${field}`));
641
+ const condition = record(required(route, "condition", path), `${path}.condition`, ["kind", "gate_node_id", "outcome", "decision_node_id", "value", "interaction_node_id"]);
642
+ const kind = required(condition, "kind", `${path}.condition`);
643
+ enumeration(kind, ["always", "gate", "decision", "interaction"], `${path}.condition.kind`);
644
+ if (kind === "always") {
645
+ record(condition, `${path}.condition`, ["kind"]);
646
+ }
647
+ else if (kind === "gate") {
648
+ const exact = record(condition, `${path}.condition`, ["kind", "gate_node_id", "outcome"]);
649
+ string(required(exact, "gate_node_id", `${path}.condition`), `${path}.condition.gate_node_id`);
650
+ enumeration(required(exact, "outcome", `${path}.condition`), ["pass", "fail"], `${path}.condition.outcome`);
651
+ }
652
+ else if (kind === "decision") {
653
+ const exact = record(condition, `${path}.condition`, ["kind", "decision_node_id", "value"]);
654
+ string(required(exact, "decision_node_id", `${path}.condition`), `${path}.condition.decision_node_id`);
655
+ string(required(exact, "value", `${path}.condition`), `${path}.condition.value`);
656
+ }
657
+ else {
658
+ const exact = record(condition, `${path}.condition`, ["kind", "interaction_node_id", "outcome"]);
659
+ string(required(exact, "interaction_node_id", `${path}.condition`), `${path}.condition.interaction_node_id`);
660
+ enumeration(required(exact, "outcome", `${path}.condition`), ["submitted", "cancelled"], `${path}.condition.outcome`);
661
+ }
662
+ }
663
+ function validateRetry(value, path) {
664
+ const retry = record(value, path, ["retry_segment_id", "node_ids", "retry_from_node_id", "max_rounds", "backoff", "initial_delay_ms", "max_delay_ms", "retryable_outcomes", "continue_route_id"]);
665
+ string(required(retry, "retry_segment_id", path), `${path}.retry_segment_id`);
666
+ stringArray(required(retry, "node_ids", path), `${path}.node_ids`);
667
+ unique(retry.node_ids, `${path}.node_ids`);
668
+ string(required(retry, "retry_from_node_id", path), `${path}.retry_from_node_id`);
669
+ integer(required(retry, "max_rounds", path), `${path}.max_rounds`, 1);
670
+ enumeration(required(retry, "backoff", path), ["none", "linear", "exponential"], `${path}.backoff`);
671
+ integer(required(retry, "initial_delay_ms", path), `${path}.initial_delay_ms`);
672
+ if (required(retry, "max_delay_ms", path) !== null)
673
+ integer(retry.max_delay_ms, `${path}.max_delay_ms`);
674
+ stringArray(required(retry, "retryable_outcomes", path), `${path}.retryable_outcomes`);
675
+ retry.retryable_outcomes.forEach((outcome, index) => enumeration(outcome, EXECUTABLE_RETRYABLE_OUTCOME_VALUES, `${path}.retryable_outcomes[${index}]`));
676
+ unique(retry.retryable_outcomes, `${path}.retryable_outcomes`);
677
+ if (Object.prototype.hasOwnProperty.call(retry, "continue_route_id")) {
678
+ string(retry.continue_route_id, `${path}.continue_route_id`);
679
+ }
680
+ }
681
+ function validateVerifier(value, path) {
682
+ const verifier = record(value, path, ["verifier_id", "subject_node_id", "kind", "relative_path_template", "pattern", "require_score", "node_id", "output_name", "schema_ref", "command", "relative_working_directory", "assertion", "message", "action", "observation_policy"]);
683
+ string(required(verifier, "verifier_id", path), `${path}.verifier_id`);
684
+ string(required(verifier, "subject_node_id", path), `${path}.subject_node_id`);
685
+ const kind = required(verifier, "kind", path);
686
+ enumeration(kind, ["artifact_exists", "artifact_non_empty", "artifact_contains", "evaluator_result", "structured_output", "command", "checkpoint"], `${path}.kind`);
687
+ if (kind === "artifact_exists" || kind === "artifact_non_empty") {
688
+ const exact = record(value, path, ["verifier_id", "subject_node_id", "kind", "relative_path_template"]);
689
+ relativePath(required(exact, "relative_path_template", path), `${path}.relative_path_template`);
690
+ }
691
+ else if (kind === "artifact_contains") {
692
+ const exact = record(value, path, ["verifier_id", "subject_node_id", "kind", "relative_path_template", "pattern"]);
693
+ relativePath(required(exact, "relative_path_template", path), `${path}.relative_path_template`);
694
+ string(required(exact, "pattern", path), `${path}.pattern`);
695
+ }
696
+ else if (kind === "evaluator_result") {
697
+ const exact = record(value, path, ["verifier_id", "subject_node_id", "kind", "relative_path_template", "require_score"]);
698
+ relativePath(required(exact, "relative_path_template", path), `${path}.relative_path_template`);
699
+ boolean(required(exact, "require_score", path), `${path}.require_score`);
700
+ }
701
+ else if (kind === "structured_output") {
702
+ const exact = record(value, path, ["verifier_id", "subject_node_id", "kind", "node_id", "output_name", "schema_ref"]);
703
+ ["node_id", "output_name", "schema_ref"].forEach((field) => string(required(exact, field, path), `${path}.${field}`));
704
+ }
705
+ else if (kind === "command") {
706
+ const exact = record(value, path, ["verifier_id", "subject_node_id", "kind", "command", "relative_working_directory", "observation_policy"]);
707
+ string(required(exact, "command", path), `${path}.command`);
708
+ relativePath(required(exact, "relative_working_directory", path), `${path}.relative_working_directory`);
709
+ if (Object.prototype.hasOwnProperty.call(exact, "observation_policy")) {
710
+ enumeration(exact.observation_policy, ["historical_execution", "current_provenance"], `${path}.observation_policy`);
711
+ }
712
+ }
713
+ else {
714
+ const exact = record(value, path, ["verifier_id", "subject_node_id", "kind", "assertion", "command", "message", "action", "observation_policy"]);
715
+ string(required(exact, "assertion", path), `${path}.assertion`);
716
+ const command = required(exact, "command", path);
717
+ if (command !== null)
718
+ string(command, `${path}.command`);
719
+ string(required(exact, "message", path), `${path}.message`);
720
+ enumeration(required(exact, "action", path), ["block", "warn"], `${path}.action`);
721
+ if (Object.prototype.hasOwnProperty.call(exact, "observation_policy")) {
722
+ enumeration(exact.observation_policy, ["historical_execution", "current_provenance"], `${path}.observation_policy`);
723
+ }
724
+ }
725
+ }
726
+ export function assertExecutableRunPlanPayload(value) {
727
+ canonicalizeJson(value);
728
+ const plan = record(value, "$", ["schema_version", "target", "input_contract", "nodes", "routes", "retry_segments", "terminals", "verifier_plan", "handoff_plan", "workspace_policy", "required_capabilities", "source_provenance"]);
729
+ if (required(plan, "schema_version", "$") !== EXECUTABLE_RUN_PLAN_SCHEMA_VERSION)
730
+ throw new ExecutableRunPlanError("$.schema_version", "unsupported schema version");
731
+ const target = record(required(plan, "target", "$"), "$.target", ["kind", "key"]);
732
+ enumeration(required(target, "kind", "$.target"), ["workflow", "controller"], "$.target.kind");
733
+ string(required(target, "key", "$.target"), "$.target.key");
734
+ validateInputContract(required(plan, "input_contract", "$"), "$.input_contract");
735
+ const nodes = required(plan, "nodes", "$.");
736
+ array(nodes, "$.nodes");
737
+ nodes.forEach((node, index) => validateNode(node, `$.nodes[${index}]`));
738
+ const nodeIds = nodes.map((node) => node.node_id);
739
+ unique(nodeIds, "$.nodes node_id");
740
+ const routes = required(plan, "routes", "$.");
741
+ array(routes, "$.routes");
742
+ routes.forEach((route, index) => validateRoute(route, `$.routes[${index}]`));
743
+ unique(routes.map((route) => route.route_id), "$.routes route_id");
744
+ const retries = required(plan, "retry_segments", "$.");
745
+ array(retries, "$.retry_segments");
746
+ retries.forEach((retry, index) => validateRetry(retry, `$.retry_segments[${index}]`));
747
+ unique(retries.map((retry) => retry.retry_segment_id), "$.retry_segments retry_segment_id");
748
+ const terminals = required(plan, "terminals", "$.");
749
+ array(terminals, "$.terminals");
750
+ terminals.forEach((item, index) => {
751
+ const path = `$.terminals[${index}]`;
752
+ const terminal = record(item, path, ["terminal_id", "status", "reason_code"]);
753
+ string(required(terminal, "terminal_id", path), `${path}.terminal_id`);
754
+ enumeration(required(terminal, "status", path), ["succeeded", "failed", "cancelled", "unsupported"], `${path}.status`);
755
+ string(required(terminal, "reason_code", path), `${path}.reason_code`);
756
+ });
757
+ unique(terminals.map((terminal) => terminal.terminal_id), "$.terminals terminal_id");
758
+ const verifiers = required(plan, "verifier_plan", "$.");
759
+ array(verifiers, "$.verifier_plan");
760
+ verifiers.forEach((verifier, index) => validateVerifier(verifier, `$.verifier_plan[${index}]`));
761
+ unique(verifiers.map((verifier) => verifier.verifier_id), "$.verifier_plan verifier_id");
762
+ const handoffs = required(plan, "handoff_plan", "$.");
763
+ array(handoffs, "$.handoff_plan");
764
+ handoffs.forEach((item, index) => {
765
+ const path = `$.handoff_plan[${index}]`;
766
+ const handoff = record(item, path, [
767
+ "handoff_id",
768
+ "from_node_id",
769
+ "output_name",
770
+ "to_node_id",
771
+ "input_name",
772
+ "mode",
773
+ "artifact_kind",
774
+ "required",
775
+ "schema_ref",
776
+ ]);
777
+ rejectRuntimeTemplates(handoff, `${path}.identity`);
778
+ ["handoff_id", "from_node_id", "output_name", "to_node_id", "input_name"].forEach((field) => string(required(handoff, field, path), `${path}.${field}`));
779
+ const mode = required(handoff, "mode", path);
780
+ enumeration(mode, ["reference", "quote", "structured"], `${path}.mode`);
781
+ const artifactKind = required(handoff, "artifact_kind", path);
782
+ enumeration(artifactKind, EXECUTABLE_ARTIFACT_KIND_VALUES, `${path}.artifact_kind`);
783
+ if (mode !== expectedModeForArtifact(artifactKind)) {
784
+ throw new ExecutableRunPlanError(`${path}.mode`, `must match artifact kind ${artifactKind}`);
785
+ }
786
+ boolean(required(handoff, "required", path), `${path}.required`);
787
+ if (required(handoff, "schema_ref", path) !== null)
788
+ string(handoff.schema_ref, `${path}.schema_ref`);
789
+ });
790
+ unique(handoffs.map((handoff) => handoff.handoff_id), "$.handoff_plan handoff_id");
791
+ const workspace = record(required(plan, "workspace_policy", "$"), "$.workspace_policy", [
792
+ "default_isolation",
793
+ "relative_workspace_root",
794
+ "allow_network",
795
+ "readable_path_templates",
796
+ "writable_path_templates",
797
+ ]);
798
+ enumeration(required(workspace, "default_isolation", "$.workspace_policy"), ["none", "worktree"], "$.workspace_policy.default_isolation");
799
+ relativePath(required(workspace, "relative_workspace_root", "$.workspace_policy"), "$.workspace_policy.relative_workspace_root");
800
+ boolean(required(workspace, "allow_network", "$.workspace_policy"), "$.workspace_policy.allow_network");
801
+ stringArray(required(workspace, "readable_path_templates", "$.workspace_policy"), "$.workspace_policy.readable_path_templates");
802
+ workspace.readable_path_templates.forEach((item, index) => relativePath(item, `$.workspace_policy.readable_path_templates[${index}]`));
803
+ unique(workspace.readable_path_templates, "$.workspace_policy.readable_path_templates");
804
+ stringArray(required(workspace, "writable_path_templates", "$.workspace_policy"), "$.workspace_policy.writable_path_templates");
805
+ workspace.writable_path_templates.forEach((item, index) => relativePath(item, `$.workspace_policy.writable_path_templates[${index}]`));
806
+ unique(workspace.writable_path_templates, "$.workspace_policy.writable_path_templates");
807
+ const capabilities = record(required(plan, "required_capabilities", "$"), "$.required_capabilities", ["worktree_isolation", "parallelism", "cancellation", "hook_identity", "caller_attestation", "human_interaction", "direct_structured_submission", "network_policy_enforcement"]);
808
+ Object.keys(capabilities).forEach((key) => boolean(required(capabilities, key, "$.required_capabilities"), `$.required_capabilities.${key}`));
809
+ if (Object.keys(capabilities).length !== 8)
810
+ throw new ExecutableRunPlanError("$.required_capabilities", "all capabilities are required");
811
+ if (workspace.allow_network === false && capabilities.network_policy_enforcement !== true) {
812
+ throw new ExecutableRunPlanError("$.required_capabilities.network_policy_enforcement", "must be true when workspace network access is denied");
813
+ }
814
+ const provenance = record(required(plan, "source_provenance", "$"), "$.source_provenance", ["compiler", "sources"]);
815
+ const compiler = record(required(provenance, "compiler", "$.source_provenance"), "$.source_provenance.compiler", ["name", "version"]);
816
+ string(required(compiler, "name", "$.source_provenance.compiler"), "$.source_provenance.compiler.name");
817
+ string(required(compiler, "version", "$.source_provenance.compiler"), "$.source_provenance.compiler.version");
818
+ const sources = required(provenance, "sources", "$.source_provenance");
819
+ array(sources, "$.source_provenance.sources");
820
+ if (sources.length === 0) {
821
+ throw new ExecutableRunPlanError("$.source_provenance.sources", "must contain at least one source");
822
+ }
823
+ sources.forEach((item, index) => {
824
+ const path = `$.source_provenance.sources[${index}]`;
825
+ const source = record(item, path, ["source_id", "relative_ref", "content_digest"]);
826
+ string(required(source, "source_id", path), `${path}.source_id`);
827
+ relativePath(required(source, "relative_ref", path), `${path}.relative_ref`);
828
+ digest(required(source, "content_digest", path), `${path}.content_digest`);
829
+ });
830
+ unique(sources.map((source) => source.source_id), "$.source_provenance.sources source_id");
831
+ const nodeById = new Map(nodes.map((node) => {
832
+ const item = node;
833
+ return [item.node_id, item];
834
+ }));
835
+ const workerNodesForPolicy = nodes.filter((node) => node.kind === "worker");
836
+ if (workerNodesForPolicy.length > 0) {
837
+ const union = (field) => {
838
+ const values = workerNodesForPolicy.flatMap((node) => node.workspace[field]);
839
+ return [...new Set(values)];
840
+ };
841
+ const expectedReadablePaths = union("readable_path_templates");
842
+ const expectedWritablePaths = union("writable_path_templates");
843
+ const expectedIsolation = workerNodesForPolicy.some((node) => node.workspace.isolation === "worktree") ? "worktree" : "none";
844
+ const requireExactPolicyProjection = (actual, expected, path) => {
845
+ if (actual.length !== expected.length
846
+ || actual.some((item, index) => item !== expected[index])) {
847
+ throw new ExecutableRunPlanError(path, "must exactly equal the ordered union of worker policies");
848
+ }
849
+ };
850
+ if (workspace.default_isolation !== expectedIsolation) {
851
+ throw new ExecutableRunPlanError("$.workspace_policy.default_isolation", "must equal the strongest worker isolation policy");
852
+ }
853
+ requireExactPolicyProjection(workspace.readable_path_templates, expectedReadablePaths, "$.workspace_policy.readable_path_templates");
854
+ requireExactPolicyProjection(workspace.writable_path_templates, expectedWritablePaths, "$.workspace_policy.writable_path_templates");
855
+ }
856
+ const routeById = new Map(routes.map((route) => {
857
+ const item = route;
858
+ return [item.route_id, item];
859
+ }));
860
+ const routeIds = new Set(routeById.keys());
861
+ const retryById = new Map(retries.map((retry) => {
862
+ const item = retry;
863
+ return [item.retry_segment_id, item];
864
+ }));
865
+ const retryIds = new Set(retryById.keys());
866
+ const terminalIds = new Set(terminals.map((terminal) => terminal.terminal_id));
867
+ const verifierById = new Map(verifiers.map((verifier) => {
868
+ const item = verifier;
869
+ return [item.verifier_id, item];
870
+ }));
871
+ const verifierIds = new Set(verifiers.map((verifier) => verifier.verifier_id));
872
+ const handoffIds = new Set(handoffs.map((handoff) => handoff.handoff_id));
873
+ const handoffsByTargetInput = new Map();
874
+ for (const handoff of handoffs) {
875
+ const key = `${handoff.to_node_id}\0${handoff.input_name}`;
876
+ const matches = handoffsByTargetInput.get(key) ?? [];
877
+ matches.push(handoff);
878
+ handoffsByTargetInput.set(key, matches);
879
+ }
880
+ const runInputDefinitions = new Map(plan.input_contract.inputs.map((input) => [input.name, input]));
881
+ const runInputNames = new Set(runInputDefinitions.keys());
882
+ const requireReference = (reference, available, path) => {
883
+ if (typeof reference !== "string" || !available.has(reference)) {
884
+ throw new ExecutableRunPlanError(path, `references unknown id '${String(reference)}'`);
885
+ }
886
+ };
887
+ const requireNodeKind = (reference, kind, path) => {
888
+ requireReference(reference, nodeById, path);
889
+ if (nodeById.get(reference)?.kind !== kind) {
890
+ throw new ExecutableRunPlanError(path, `must reference a ${kind} node`);
891
+ }
892
+ };
893
+ const verifierOwnerGateIds = new Map();
894
+ const producedOutputs = (node) => {
895
+ if (node.kind === "worker") {
896
+ return new Map(node.packet_template.output_contract
897
+ .map((output) => [output.name, output]));
898
+ }
899
+ if (node.kind === "system") {
900
+ const outputContract = node.output_contract;
901
+ if (outputContract !== undefined) {
902
+ return new Map(outputContract.map((output) => [output.name, output]));
903
+ }
904
+ const outputName = node.operation.output_name;
905
+ return typeof outputName === "string"
906
+ ? new Map([[outputName, { name: outputName }]])
907
+ : new Map();
908
+ }
909
+ return null;
910
+ };
911
+ const consumedInputs = (node) => {
912
+ if (node.kind === "worker") {
913
+ return new Map(node.packet_template.handoff_inputs
914
+ .map((input) => [input.name, input]));
915
+ }
916
+ if (node.kind !== "system")
917
+ return null;
918
+ return new Map(node.input_ports.map((port) => [port.name, port]));
919
+ };
920
+ const requireProducedOutput = (nodeReference, outputReference, path) => {
921
+ requireReference(nodeReference, nodeById, path);
922
+ const outputs = producedOutputs(nodeById.get(nodeReference));
923
+ if (typeof outputReference !== "string" || outputs === null || !outputs.has(outputReference)) {
924
+ throw new ExecutableRunPlanError(path, `references undeclared output '${String(outputReference)}'`);
925
+ }
926
+ };
927
+ nodes.forEach((node, index) => {
928
+ const item = node;
929
+ item.depends_on.forEach((dependency, dependencyIndex) => {
930
+ requireReference(dependency, nodeById, `$.nodes[${index}].depends_on[${dependencyIndex}]`);
931
+ if (dependency === item.node_id)
932
+ throw new ExecutableRunPlanError(`$.nodes[${index}].depends_on`, "cannot reference itself");
933
+ });
934
+ if (item.kind === "worker") {
935
+ const retryId = item.attempt_policy.retry_segment_id;
936
+ if (retryId !== null) {
937
+ requireReference(retryId, retryIds, `$.nodes[${index}].attempt_policy.retry_segment_id`);
938
+ if (!retryById.get(retryId).node_ids.includes(item.node_id)) {
939
+ throw new ExecutableRunPlanError(`$.nodes[${index}].attempt_policy.retry_segment_id`, "must reference a retry segment containing this worker");
940
+ }
941
+ }
942
+ const packet = item.packet_template;
943
+ (packet.handoff_inputs ?? []).forEach((input, inputIndex) => {
944
+ const matches = handoffsByTargetInput.get(`${item.node_id}\0${input.name}`) ?? [];
945
+ if (input.source === "handoff" && matches.length !== 1) {
946
+ throw new ExecutableRunPlanError(`$.nodes[${index}].packet_template.handoff_inputs[${inputIndex}].name`, `must have exactly one handoff definition (found ${matches.length})`);
947
+ }
948
+ if (input.source === "external" && matches.length !== 0) {
949
+ throw new ExecutableRunPlanError(`$.nodes[${index}].packet_template.handoff_inputs[${inputIndex}].source`, "external inputs must not be targeted by a committed handoff");
950
+ }
951
+ });
952
+ }
953
+ else if (item.kind === "system") {
954
+ const operation = item.operation;
955
+ rejectRuntimeTemplates({
956
+ input_ports: item.input_ports,
957
+ output_identity: item.output_contract?.map((output) => ({
958
+ name: output.name,
959
+ schema_ref: output.schema_ref,
960
+ })) ?? [],
961
+ output_name: operation.output_name,
962
+ handoff_id: operation.handoff_id,
963
+ }, `$.nodes[${index}].system_identity`);
964
+ if (operation.kind === "materialize_handoff") {
965
+ requireReference(operation.handoff_id, handoffIds, `$.nodes[${index}].operation.handoff_id`);
966
+ }
967
+ const operationInputNames = operation.kind === "import_result"
968
+ ? [operation.source_plan_input, operation.source_result_input]
969
+ : operation.kind === "adopt_artifact"
970
+ ? [operation.source_reference_input, operation.expected_digest_input]
971
+ : operation.kind === "resolve_diagnosis_source"
972
+ ? [operation.mode_input]
973
+ : operation.kind === "adopt_artifact_bundle"
974
+ ? operation.artifacts.flatMap((artifact) => [
975
+ artifact.source_reference_input,
976
+ artifact.expected_digest_input,
977
+ ])
978
+ : operation.kind === "import_result_bundle"
979
+ ? [operation.source_plan_input, operation.source_result_input]
980
+ : operation.kind === "import_review"
981
+ ? [
982
+ operation.source_plan_input,
983
+ operation.source_diagnosis_result_input,
984
+ operation.source_review_result_input,
985
+ ]
986
+ : [];
987
+ const ports = item.input_ports;
988
+ const portNames = ports.map((port) => port.name);
989
+ if (operationInputNames.length !== portNames.length
990
+ || operationInputNames.some((name) => !portNames.includes(name))) {
991
+ throw new ExecutableRunPlanError(`$.nodes[${index}].input_ports`, "must exactly declare the operation input names");
992
+ }
993
+ ports.forEach((port, portIndex) => {
994
+ const portPath = `$.nodes[${index}].input_ports[${portIndex}]`;
995
+ const name = port.name;
996
+ if (port.source === "run_input") {
997
+ requireReference(name, runInputNames, `${portPath}.name`);
998
+ const handoffMatches = handoffsByTargetInput.get(`${item.node_id}\0${name}`) ?? [];
999
+ if (handoffMatches.length !== 0) {
1000
+ throw new ExecutableRunPlanError(`${portPath}.name`, "run_input ports must not also be targeted by a handoff");
1001
+ }
1002
+ if (runInputDefinitions.get(name)?.sensitive === true) {
1003
+ throw new ExecutableRunPlanError(`${portPath}.name`, "sensitive inputs require a typed secret delivery mode and cannot enter system templates");
1004
+ }
1005
+ return;
1006
+ }
1007
+ const matches = handoffsByTargetInput.get(`${item.node_id}\0${name}`) ?? [];
1008
+ if (matches.length !== 1) {
1009
+ throw new ExecutableRunPlanError(`${portPath}.name`, `must reference exactly one handoff targeting this system input (found ${matches.length})`);
1010
+ }
1011
+ });
1012
+ const outputContract = item.output_contract ?? [];
1013
+ const contractOutputNames = outputContract.map((output) => output.name);
1014
+ const mappedOutputNames = (mappings) => (mappings.map((mapping) => mapping.output_name));
1015
+ let operationOutputNames;
1016
+ if (operation.kind === "import_result"
1017
+ || operation.kind === "adopt_artifact"
1018
+ || operation.kind === "resolve_diagnosis_source") {
1019
+ operationOutputNames = [operation.output_name];
1020
+ }
1021
+ else if (operation.kind === "adopt_artifact_bundle") {
1022
+ operationOutputNames = operation.artifacts.map((artifact) => artifact.output_name);
1023
+ }
1024
+ else if (operation.kind === "import_result_bundle" || operation.kind === "import_review") {
1025
+ operationOutputNames = mappedOutputNames(operation.required_outputs);
1026
+ }
1027
+ else if (operation.kind === "seal_result_bundle") {
1028
+ const candidates = operation.candidates;
1029
+ if (candidates.length === 0) {
1030
+ throw new ExecutableRunPlanError(`$.nodes[${index}].operation.candidates`, "must not be empty");
1031
+ }
1032
+ let expectedCandidateOutputs = null;
1033
+ candidates.forEach((candidate, candidateIndex) => {
1034
+ const candidatePath = `$.nodes[${index}].operation.candidates[${candidateIndex}]`;
1035
+ const requiredNodeIds = candidate.required_node_ids;
1036
+ if (requiredNodeIds.length === 0) {
1037
+ throw new ExecutableRunPlanError(`${candidatePath}.required_node_ids`, "must not be empty");
1038
+ }
1039
+ requiredNodeIds.forEach((nodeId, requiredIndex) => requireReference(nodeId, nodeById, `${candidatePath}.required_node_ids[${requiredIndex}]`));
1040
+ const outputs = candidate.outputs;
1041
+ if (outputs.length === 0) {
1042
+ throw new ExecutableRunPlanError(`${candidatePath}.outputs`, "must not be empty");
1043
+ }
1044
+ outputs.forEach((mapping, mappingIndex) => {
1045
+ const mappingPath = `${candidatePath}.outputs[${mappingIndex}]`;
1046
+ requireReference(mapping.source_node_id, nodeById, `${mappingPath}.source_node_id`);
1047
+ if (!requiredNodeIds.includes(mapping.source_node_id)) {
1048
+ throw new ExecutableRunPlanError(`${mappingPath}.source_node_id`, "must also appear in required_node_ids");
1049
+ }
1050
+ requireProducedOutput(mapping.source_node_id, mapping.source_output_name, mappingPath);
1051
+ });
1052
+ const candidateOutputs = mappedOutputNames(outputs).sort();
1053
+ if (new Set(candidateOutputs).size !== candidateOutputs.length) {
1054
+ throw new ExecutableRunPlanError(`${candidatePath}.outputs`, "output_name values must be unique");
1055
+ }
1056
+ if (expectedCandidateOutputs !== null
1057
+ && canonicalizeJson(candidateOutputs) !== canonicalizeJson(expectedCandidateOutputs)) {
1058
+ throw new ExecutableRunPlanError(`${candidatePath}.outputs`, "all seal candidates must produce the same output names");
1059
+ }
1060
+ expectedCandidateOutputs = candidateOutputs;
1061
+ });
1062
+ operationOutputNames = [
1063
+ ...(expectedCandidateOutputs ?? []),
1064
+ ...(typeof operation.origin_output_name === "string"
1065
+ ? [operation.origin_output_name]
1066
+ : []),
1067
+ ];
1068
+ }
1069
+ else {
1070
+ operationOutputNames = [];
1071
+ }
1072
+ const sortedContractOutputs = [...contractOutputNames].sort();
1073
+ const sortedOperationOutputs = [...operationOutputNames].sort();
1074
+ const legacyImplicitOutputContract = item.output_contract === undefined
1075
+ && new Set(["import_result", "adopt_artifact"]).has(operation.kind);
1076
+ if (!legacyImplicitOutputContract
1077
+ && (new Set(sortedOperationOutputs).size !== sortedOperationOutputs.length
1078
+ || canonicalizeJson(sortedContractOutputs) !== canonicalizeJson(sortedOperationOutputs))) {
1079
+ throw new ExecutableRunPlanError(`$.nodes[${index}].output_contract`, "must exactly match the outputs produced by the system operation");
1080
+ }
1081
+ }
1082
+ else if (item.kind === "gate") {
1083
+ item.verifier_ids.forEach((verifierId, verifierIndex) => {
1084
+ requireReference(verifierId, verifierIds, `$.nodes[${index}].verifier_ids[${verifierIndex}]`);
1085
+ const owners = verifierOwnerGateIds.get(verifierId) ?? [];
1086
+ owners.push(item.node_id);
1087
+ verifierOwnerGateIds.set(verifierId, owners);
1088
+ });
1089
+ item.pass_route_ids.forEach((routeId, routeIndex) => {
1090
+ requireReference(routeId, routeIds, `$.nodes[${index}].pass_route_ids[${routeIndex}]`);
1091
+ const route = routeById.get(routeId);
1092
+ const condition = route.condition;
1093
+ if (route.from_node_id !== item.node_id
1094
+ || condition.kind !== "gate"
1095
+ || condition.gate_node_id !== item.node_id
1096
+ || condition.outcome !== "pass") {
1097
+ throw new ExecutableRunPlanError(`$.nodes[${index}].pass_route_ids[${routeIndex}]`, "must reference this gate's pass route");
1098
+ }
1099
+ });
1100
+ requireReference(item.fail_route_id, routeIds, `$.nodes[${index}].fail_route_id`);
1101
+ const failRoute = routeById.get(item.fail_route_id);
1102
+ const failCondition = failRoute.condition;
1103
+ if (failRoute.from_node_id !== item.node_id
1104
+ || failCondition.kind !== "gate"
1105
+ || failCondition.gate_node_id !== item.node_id
1106
+ || failCondition.outcome !== "fail") {
1107
+ throw new ExecutableRunPlanError(`$.nodes[${index}].fail_route_id`, "must reference this gate's fail route");
1108
+ }
1109
+ }
1110
+ else if (item.kind === "decision") {
1111
+ const source = item.source;
1112
+ requireProducedOutput(source.node_id, source.output_name, `$.nodes[${index}].source`);
1113
+ const sourceNode = nodeById.get(source.node_id);
1114
+ const sourceOutput = producedOutputs(sourceNode)?.get(source.output_name);
1115
+ if (sourceOutput?.kind !== undefined && sourceOutput.kind !== "structured") {
1116
+ throw new ExecutableRunPlanError(`$.nodes[${index}].source.output_name`, "must reference a structured output");
1117
+ }
1118
+ }
1119
+ else if (item.kind === "interaction") {
1120
+ if (capabilities.human_interaction !== true)
1121
+ throw new ExecutableRunPlanError("$.required_capabilities.human_interaction", "must be true when interaction nodes exist");
1122
+ }
1123
+ else if (item.kind === "terminal") {
1124
+ requireReference(item.terminal_id, terminalIds, `$.nodes[${index}].terminal_id`);
1125
+ }
1126
+ });
1127
+ const dependencyState = new Map();
1128
+ const visitDependency = (nodeId) => {
1129
+ const state = dependencyState.get(nodeId);
1130
+ if (state === "visiting") {
1131
+ throw new ExecutableRunPlanError("$.nodes", `dependency cycle includes '${nodeId}'`);
1132
+ }
1133
+ if (state === "visited")
1134
+ return;
1135
+ dependencyState.set(nodeId, "visiting");
1136
+ const node = nodeById.get(nodeId);
1137
+ for (const dependency of node.depends_on) {
1138
+ visitDependency(dependency);
1139
+ }
1140
+ dependencyState.set(nodeId, "visited");
1141
+ };
1142
+ for (const nodeId of nodeById.keys())
1143
+ visitDependency(nodeId);
1144
+ verifiers.forEach((verifier, index) => {
1145
+ const item = verifier;
1146
+ const verifierId = item.verifier_id;
1147
+ const subjectNodeId = item.subject_node_id;
1148
+ requireReference(subjectNodeId, nodeById, `$.verifier_plan[${index}].subject_node_id`);
1149
+ rejectRuntimeTemplates({
1150
+ verifier_id: item.verifier_id,
1151
+ subject_node_id: item.subject_node_id,
1152
+ pattern: item.pattern,
1153
+ require_score: item.require_score,
1154
+ node_id: item.node_id,
1155
+ output_name: item.output_name,
1156
+ schema_ref: item.schema_ref,
1157
+ assertion: item.assertion,
1158
+ action: item.action,
1159
+ }, `$.verifier_plan[${index}].authority`);
1160
+ const owners = verifierOwnerGateIds.get(verifierId) ?? [];
1161
+ if (owners.length !== 1) {
1162
+ throw new ExecutableRunPlanError(`$.verifier_plan[${index}].verifier_id`, `verifier '${verifierId}' must have exactly one gate owner`);
1163
+ }
1164
+ if (item.kind === "structured_output" && item.node_id !== subjectNodeId) {
1165
+ throw new ExecutableRunPlanError(`$.verifier_plan[${index}].node_id`, "must match subject_node_id");
1166
+ }
1167
+ if (!nodeById.get(owners[0]).depends_on.includes(subjectNodeId)) {
1168
+ throw new ExecutableRunPlanError(`$.verifier_plan[${index}].subject_node_id`, `must be a direct dependency of owner gate '${owners[0]}'`);
1169
+ }
1170
+ if (item.kind !== "structured_output" && nodeById.get(subjectNodeId).kind !== "worker") {
1171
+ throw new ExecutableRunPlanError(`$.verifier_plan[${index}].subject_node_id`, "workspace verifiers must reference a worker subject");
1172
+ }
1173
+ if (item.kind === "command" || item.kind === "checkpoint") {
1174
+ const command = item.command;
1175
+ if (typeof command === "string") {
1176
+ const trimmed = command.trim();
1177
+ const named = /^\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}$/u.exec(trimmed);
1178
+ const wholeCommandInput = named?.[1] ?? (trimmed === "$ARGUMENTS" ? "ARGUMENTS" : null);
1179
+ if (wholeCommandInput !== null
1180
+ && runInputDefinitions.get(wholeCommandInput)?.type !== "trusted_verifier_command") {
1181
+ throw new ExecutableRunPlanError(`$.verifier_plan[${index}].command`, `whole-command input '${wholeCommandInput}' must have type trusted_verifier_command`);
1182
+ }
1183
+ }
1184
+ }
1185
+ });
1186
+ nodes.forEach((node, index) => {
1187
+ const item = node;
1188
+ if (item.kind !== "worker")
1189
+ return;
1190
+ const subjectVerifiers = [...verifierById.values()]
1191
+ .filter((verifier) => verifier.subject_node_id === item.node_id);
1192
+ const expectedRuntimeInputs = deriveWorkerRuntimeInputNames(item, subjectVerifiers, handoffs, `$.nodes[${index}]`);
1193
+ const packet = item.packet_template;
1194
+ const actualRuntimeInputs = packet.runtime_input_names;
1195
+ if (actualRuntimeInputs.length !== expectedRuntimeInputs.length
1196
+ || actualRuntimeInputs.some((name, inputIndex) => (name !== expectedRuntimeInputs[inputIndex]))) {
1197
+ throw new ExecutableRunPlanError(`$.nodes[${index}].packet_template.runtime_input_names`, `must exactly match derived runtime inputs: ${expectedRuntimeInputs.join(", ") || "(none)"}`);
1198
+ }
1199
+ expectedRuntimeInputs.forEach((inputName, inputIndex) => requireReference(inputName, runInputNames, `$.nodes[${index}].packet_template.runtime_input_names[${inputIndex}]`));
1200
+ expectedRuntimeInputs.forEach((inputName, inputIndex) => {
1201
+ if (runInputDefinitions.get(inputName)?.sensitive === true) {
1202
+ throw new ExecutableRunPlanError(`$.nodes[${index}].packet_template.runtime_input_names[${inputIndex}]`, "sensitive inputs require a typed secret delivery mode and cannot enter worker templates");
1203
+ }
1204
+ });
1205
+ });
1206
+ const actualRunInputNames = new Set();
1207
+ for (const node of nodes) {
1208
+ if (node.kind === "worker") {
1209
+ for (const name of node.packet_template.runtime_input_names) {
1210
+ actualRunInputNames.add(name);
1211
+ }
1212
+ }
1213
+ else if (node.kind === "system") {
1214
+ for (const port of node.input_ports) {
1215
+ if (port.source === "run_input")
1216
+ actualRunInputNames.add(port.name);
1217
+ }
1218
+ }
1219
+ }
1220
+ const missingInputs = [...actualRunInputNames].filter((name) => !runInputNames.has(name));
1221
+ const unusedInputs = [...runInputNames].filter((name) => !actualRunInputNames.has(name));
1222
+ if (missingInputs.length > 0 || unusedInputs.length > 0) {
1223
+ throw new ExecutableRunPlanError("$.input_contract.inputs", `must exactly equal worker and system run inputs; missing: ${missingInputs.join(", ") || "(none)"}; unused: ${unusedInputs.join(", ") || "(none)"}`);
1224
+ }
1225
+ routes.forEach((route, index) => {
1226
+ const item = route;
1227
+ requireReference(item.from_node_id, nodeById, `$.routes[${index}].from_node_id`);
1228
+ requireReference(item.to_node_id, nodeById, `$.routes[${index}].to_node_id`);
1229
+ const condition = item.condition;
1230
+ if (condition.kind === "gate") {
1231
+ requireNodeKind(condition.gate_node_id, "gate", `$.routes[${index}].condition.gate_node_id`);
1232
+ if (item.from_node_id !== condition.gate_node_id)
1233
+ throw new ExecutableRunPlanError(`$.routes[${index}].from_node_id`, "must match the gate condition node");
1234
+ }
1235
+ if (condition.kind === "decision") {
1236
+ requireNodeKind(condition.decision_node_id, "decision", `$.routes[${index}].condition.decision_node_id`);
1237
+ if (item.from_node_id !== condition.decision_node_id)
1238
+ throw new ExecutableRunPlanError(`$.routes[${index}].from_node_id`, "must match the decision condition node");
1239
+ const decision = nodeById.get(condition.decision_node_id);
1240
+ if (!decision.allowed_values.includes(condition.value))
1241
+ throw new ExecutableRunPlanError(`$.routes[${index}].condition.value`, "is not declared by the decision node");
1242
+ }
1243
+ if (condition.kind === "interaction") {
1244
+ requireNodeKind(condition.interaction_node_id, "interaction", `$.routes[${index}].condition.interaction_node_id`);
1245
+ if (item.from_node_id !== condition.interaction_node_id)
1246
+ throw new ExecutableRunPlanError(`$.routes[${index}].from_node_id`, "must match the interaction condition node");
1247
+ }
1248
+ });
1249
+ nodes.forEach((node, nodeIndex) => {
1250
+ const item = node;
1251
+ if (item.kind !== "decision" && item.kind !== "interaction" && item.kind !== "gate") {
1252
+ return;
1253
+ }
1254
+ const outgoing = routes.filter((route) => route.from_node_id === item.node_id);
1255
+ const expectedConditionKind = item.kind;
1256
+ outgoing.forEach((route) => {
1257
+ const condition = route.condition;
1258
+ if (condition.kind !== expectedConditionKind) {
1259
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}]`, `${item.kind} nodes may only use ${expectedConditionKind} routes`);
1260
+ }
1261
+ });
1262
+ if (item.kind === "decision") {
1263
+ for (const value of item.allowed_values) {
1264
+ const matches = outgoing.filter((route) => route.condition.value === value);
1265
+ if (matches.length !== 1) {
1266
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].allowed_values`, `decision value '${value}' must have exactly one route`);
1267
+ }
1268
+ }
1269
+ }
1270
+ else if (item.kind === "interaction") {
1271
+ for (const outcome of ["submitted", "cancelled"]) {
1272
+ const matches = outgoing.filter((route) => route.condition.outcome === outcome);
1273
+ if (matches.length !== 1) {
1274
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].interaction_id`, `interaction outcome '${outcome}' must have exactly one route`);
1275
+ }
1276
+ }
1277
+ }
1278
+ else {
1279
+ const passRoutes = outgoing.filter((route) => route.condition.outcome === "pass");
1280
+ const failRoutes = outgoing.filter((route) => route.condition.outcome === "fail");
1281
+ const declaredPassRouteIds = item.pass_route_ids;
1282
+ const actualPassRouteIds = passRoutes.map((route) => route.route_id);
1283
+ if (declaredPassRouteIds.length !== actualPassRouteIds.length
1284
+ || declaredPassRouteIds.some((routeId, index) => routeId !== actualPassRouteIds[index])) {
1285
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].pass_route_ids`, "must exactly match this gate's pass routes in route-table order");
1286
+ }
1287
+ if (failRoutes.length !== 1) {
1288
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].fail_route_id`, "gate outcome 'fail' must have exactly one route");
1289
+ }
1290
+ const passTargets = actualPassRouteIds.map((routeId) => routeById.get(routeId).to_node_id);
1291
+ if (new Set(passTargets).size !== passTargets.length) {
1292
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].pass_route_ids`, "gate pass routes must have unique targets");
1293
+ }
1294
+ const consumesGateAsSealCandidate = (targetId) => {
1295
+ const target = nodeById.get(targetId);
1296
+ if (target.kind !== "system")
1297
+ return false;
1298
+ const operation = target.operation;
1299
+ return operation.kind === "seal_result_bundle"
1300
+ && operation.candidates.some((candidate) => (candidate.required_node_ids.includes(item.node_id)));
1301
+ };
1302
+ for (const targetId of passTargets) {
1303
+ if (!nodeById.get(targetId).depends_on.includes(item.node_id)
1304
+ && !consumesGateAsSealCandidate(targetId)) {
1305
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].pass_route_ids`, `pass route target '${targetId}' must directly depend on this gate`);
1306
+ }
1307
+ }
1308
+ const directDependents = nodes
1309
+ .map((candidate) => candidate)
1310
+ .filter((candidate) => candidate.depends_on.includes(item.node_id))
1311
+ .map((candidate) => candidate.node_id);
1312
+ const routedSealConsumers = passTargets.filter(consumesGateAsSealCandidate);
1313
+ const gateConsumers = [...directDependents, ...routedSealConsumers];
1314
+ if (gateConsumers.length !== passTargets.length
1315
+ || gateConsumers.some((targetId) => !passTargets.includes(targetId))) {
1316
+ throw new ExecutableRunPlanError(`$.nodes[${nodeIndex}].pass_route_ids`, "must route exactly once to every direct dependent of this gate");
1317
+ }
1318
+ }
1319
+ });
1320
+ const routeTargets = new Map();
1321
+ for (const route of routes) {
1322
+ const targets = routeTargets.get(route.from_node_id) ?? [];
1323
+ targets.push(route.to_node_id);
1324
+ routeTargets.set(route.from_node_id, targets);
1325
+ }
1326
+ const routeState = new Map();
1327
+ const visitRoute = (nodeId) => {
1328
+ const state = routeState.get(nodeId);
1329
+ if (state === "visiting") {
1330
+ throw new ExecutableRunPlanError("$.routes", `route cycle includes '${nodeId}'`);
1331
+ }
1332
+ if (state === "visited")
1333
+ return;
1334
+ routeState.set(nodeId, "visiting");
1335
+ for (const target of routeTargets.get(nodeId) ?? [])
1336
+ visitRoute(target);
1337
+ routeState.set(nodeId, "visited");
1338
+ };
1339
+ for (const nodeId of nodeById.keys())
1340
+ visitRoute(nodeId);
1341
+ const activationTargets = new Map();
1342
+ const addActivationEdge = (fromNodeId, toNodeId) => {
1343
+ const targets = activationTargets.get(fromNodeId) ?? [];
1344
+ if (!targets.includes(toNodeId))
1345
+ targets.push(toNodeId);
1346
+ activationTargets.set(fromNodeId, targets);
1347
+ };
1348
+ for (const node of nodes) {
1349
+ for (const dependency of node.depends_on) {
1350
+ addActivationEdge(dependency, node.node_id);
1351
+ }
1352
+ }
1353
+ for (const route of routes) {
1354
+ addActivationEdge(route.from_node_id, route.to_node_id);
1355
+ }
1356
+ const activationState = new Map();
1357
+ const visitActivation = (nodeId) => {
1358
+ const state = activationState.get(nodeId);
1359
+ if (state === "visiting") {
1360
+ throw new ExecutableRunPlanError("$.nodes", `activation cycle includes '${nodeId}'`);
1361
+ }
1362
+ if (state === "visited")
1363
+ return;
1364
+ activationState.set(nodeId, "visiting");
1365
+ for (const target of activationTargets.get(nodeId) ?? [])
1366
+ visitActivation(target);
1367
+ activationState.set(nodeId, "visited");
1368
+ };
1369
+ for (const nodeId of nodeById.keys())
1370
+ visitActivation(nodeId);
1371
+ const isDependencyAncestor = (ancestorId, nodeId) => {
1372
+ const pending = [...nodeById.get(nodeId).depends_on];
1373
+ const visited = new Set();
1374
+ while (pending.length > 0) {
1375
+ const candidate = pending.pop();
1376
+ if (candidate === ancestorId)
1377
+ return true;
1378
+ if (visited.has(candidate))
1379
+ continue;
1380
+ visited.add(candidate);
1381
+ pending.push(...nodeById.get(candidate).depends_on);
1382
+ }
1383
+ return false;
1384
+ };
1385
+ nodes.forEach((node, index) => {
1386
+ const item = node;
1387
+ if (item.kind !== "decision")
1388
+ return;
1389
+ const source = item.source;
1390
+ if (!isDependencyAncestor(source.node_id, item.node_id)) {
1391
+ throw new ExecutableRunPlanError(`$.nodes[${index}].source.node_id`, "must be a dependency ancestor of the decision node");
1392
+ }
1393
+ });
1394
+ retries.forEach((retry, index) => {
1395
+ const item = retry;
1396
+ item.node_ids.forEach((nodeId, nodeIndex) => requireReference(nodeId, nodeById, `$.retry_segments[${index}].node_ids[${nodeIndex}]`));
1397
+ requireReference(item.retry_from_node_id, nodeById, `$.retry_segments[${index}].retry_from_node_id`);
1398
+ if (!item.node_ids.includes(item.retry_from_node_id))
1399
+ throw new ExecutableRunPlanError(`$.retry_segments[${index}].retry_from_node_id`, "must belong to the retry segment");
1400
+ if (typeof item.continue_route_id === "string") {
1401
+ requireReference(item.continue_route_id, routeById, `$.retry_segments[${index}].continue_route_id`);
1402
+ const route = routeById.get(item.continue_route_id);
1403
+ const condition = route.condition;
1404
+ if (condition.kind !== "decision") {
1405
+ throw new ExecutableRunPlanError(`$.retry_segments[${index}].continue_route_id`, "must reference a typed decision route");
1406
+ }
1407
+ if (nodeById.get(route.to_node_id)?.kind !== "terminal") {
1408
+ throw new ExecutableRunPlanError(`$.retry_segments[${index}].continue_route_id`, "must route to the segment exhaustion terminal");
1409
+ }
1410
+ }
1411
+ });
1412
+ verifiers.forEach((verifier, index) => {
1413
+ const item = verifier;
1414
+ if (item.kind === "structured_output") {
1415
+ requireProducedOutput(item.node_id, item.output_name, `$.verifier_plan[${index}]`);
1416
+ }
1417
+ });
1418
+ handoffs.forEach((handoff, index) => {
1419
+ const item = handoff;
1420
+ requireProducedOutput(item.from_node_id, item.output_name, `$.handoff_plan[${index}]`);
1421
+ requireReference(item.to_node_id, nodeById, `$.handoff_plan[${index}].to_node_id`);
1422
+ if (!isDependencyAncestor(item.from_node_id, item.to_node_id)) {
1423
+ throw new ExecutableRunPlanError(`$.handoff_plan[${index}].from_node_id`, "must be a dependency ancestor of the target node");
1424
+ }
1425
+ const targetNode = nodeById.get(item.to_node_id);
1426
+ const inputNames = consumedInputs(targetNode);
1427
+ const isMaterializerForHandoff = targetNode.kind === "system"
1428
+ && targetNode.operation.kind === "materialize_handoff"
1429
+ && targetNode.operation.handoff_id === item.handoff_id;
1430
+ if (inputNames === null
1431
+ || (!inputNames.has(item.input_name) && !isMaterializerForHandoff)) {
1432
+ throw new ExecutableRunPlanError(`$.handoff_plan[${index}].input_name`, `references undeclared target input '${String(item.input_name)}'`);
1433
+ }
1434
+ const sourceNode = nodeById.get(item.from_node_id);
1435
+ const sourceOutput = producedOutputs(sourceNode)?.get(item.output_name);
1436
+ if (sourceOutput?.artifact_kind !== undefined) {
1437
+ if (sourceOutput.artifact_kind !== item.artifact_kind
1438
+ || expectedOutputKindForArtifact(item.artifact_kind)
1439
+ !== sourceOutput.kind) {
1440
+ throw new ExecutableRunPlanError(`$.handoff_plan[${index}].artifact_kind`, "must match the declared source output contract");
1441
+ }
1442
+ }
1443
+ const targetInput = inputNames?.get(item.input_name);
1444
+ if (targetNode.kind === "worker" && targetInput?.source !== undefined && (targetInput.source !== "handoff"
1445
+ || targetInput.mode !== item.mode
1446
+ || targetInput.artifact_kind !== item.artifact_kind
1447
+ || targetInput.required !== item.required
1448
+ || targetInput.schema_ref !== item.schema_ref)) {
1449
+ throw new ExecutableRunPlanError(`$.handoff_plan[${index}].input_name`, "must match the declared target handoff input contract");
1450
+ }
1451
+ });
1452
+ }
1453
+ export function parseExecutableRunPlanPayload(value) {
1454
+ assertExecutableRunPlanPayload(value);
1455
+ return value;
1456
+ }
1457
+ export function executableRunPlanDigest(plan) {
1458
+ assertExecutableRunPlanPayload(plan);
1459
+ return `sha256:${createHash("sha256").update(canonicalizeJson(plan), "utf8").digest("hex")}`;
1460
+ }
1461
+ export function createStoredExecutableRunPlan(plan) {
1462
+ const canonicalPlan = JSON.parse(canonicalizeJson(plan));
1463
+ assertExecutableRunPlanPayload(canonicalPlan);
1464
+ const planDigest = executableRunPlanDigest(canonicalPlan);
1465
+ return {
1466
+ schema_version: STORED_EXECUTABLE_RUN_PLAN_SCHEMA_VERSION,
1467
+ plan_id: planDigest,
1468
+ plan_digest: planDigest,
1469
+ plan: canonicalPlan,
1470
+ };
1471
+ }
1472
+ export function parseStoredExecutableRunPlan(value) {
1473
+ canonicalizeJson(value);
1474
+ const stored = record(value, "$", ["schema_version", "plan_id", "plan_digest", "plan"]);
1475
+ if (required(stored, "schema_version", "$") !== STORED_EXECUTABLE_RUN_PLAN_SCHEMA_VERSION)
1476
+ throw new ExecutableRunPlanError("$.schema_version", "unsupported stored plan schema version");
1477
+ digest(required(stored, "plan_id", "$"), "$.plan_id");
1478
+ digest(required(stored, "plan_digest", "$"), "$.plan_digest");
1479
+ const plan = parseExecutableRunPlanPayload(required(stored, "plan", "$"));
1480
+ const recomputed = executableRunPlanDigest(plan);
1481
+ if (stored.plan_id !== stored.plan_digest || stored.plan_digest !== recomputed) {
1482
+ throw new ExecutableRunPlanError("$", "plan_id, plan_digest, and canonical payload digest must match");
1483
+ }
1484
+ return value;
1485
+ }