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
@@ -1,7 +1,14 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { chmod, link, lstat, mkdir, open, readFile, unlink, } from "node:fs/promises";
3
+ import { join, resolve } from "node:path";
4
+ import { CALLER_ATTESTATION_SCHEMA_VERSION, CANONICAL_RESULT_SCHEMA_VERSION, } from "./run-contracts.js";
5
+ import { validateCanonicalAttemptOutcome } from "./canonical-attempt-outcome.js";
6
+ import { canonicalizeJson } from "./canonical-json.js";
7
+ import { workerStopReceiptDigest } from "./execution-authority.js";
1
8
  export class ResultStoreError extends Error {
2
9
  code;
3
- constructor(code, message) {
4
- super(message);
10
+ constructor(code, message, options) {
11
+ super(message, options);
5
12
  this.name = "ResultStoreError";
6
13
  this.code = code;
7
14
  }
@@ -128,3 +135,1067 @@ export class ImmutableResultStore {
128
135
  return results;
129
136
  }
130
137
  }
138
+ const DIRECTORY_MODE = 0o700;
139
+ const TEMPORARY_FILE_MODE = 0o600;
140
+ const STORED_FILE_MODE = 0o400;
141
+ const SHA256_DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
142
+ function isErrno(error, code) {
143
+ return error instanceof Error
144
+ && "code" in error
145
+ && error.code === code;
146
+ }
147
+ function exactObjectKeys(value, expected) {
148
+ const actual = Object.keys(value).sort();
149
+ const wanted = [...expected].sort();
150
+ return actual.length === wanted.length
151
+ && actual.every((key, index) => key === wanted[index]);
152
+ }
153
+ function assertRecord(value, label) {
154
+ if (typeof value !== "object"
155
+ || value === null
156
+ || Array.isArray(value)
157
+ || Object.getPrototypeOf(value) !== Object.prototype) {
158
+ throw new ResultStoreError("invalid_result", `${label} must be an object`);
159
+ }
160
+ }
161
+ function assertExactKeys(value, expected, label) {
162
+ if (!exactObjectKeys(value, expected)) {
163
+ throw new ResultStoreError("invalid_result", `${label} has unexpected or missing fields`);
164
+ }
165
+ }
166
+ function assertCanonicalNonEmptyString(value, label) {
167
+ if (typeof value !== "string" || value.length === 0) {
168
+ throw new ResultStoreError("invalid_result", `${label} must be a non-empty string`);
169
+ }
170
+ }
171
+ const RFC3339_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](\d{2}):(\d{2}))$/u;
172
+ function parseRfc3339(value, label) {
173
+ assertCanonicalNonEmptyString(value, label);
174
+ const match = value.match(RFC3339_PATTERN);
175
+ if (!match) {
176
+ throw new ResultStoreError("invalid_result", `${label} must be RFC3339`);
177
+ }
178
+ const [, yearText, monthText, dayText, hourText, minuteText, secondText, offsetHourText, offsetMinuteText] = match;
179
+ const year = Number(yearText);
180
+ const month = Number(monthText);
181
+ const day = Number(dayText);
182
+ const hour = Number(hourText);
183
+ const minute = Number(minuteText);
184
+ const second = Number(secondText);
185
+ const offsetHour = Number(offsetHourText ?? 0);
186
+ const offsetMinute = Number(offsetMinuteText ?? 0);
187
+ const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
188
+ const monthDays = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
189
+ if (month < 1 || month > 12
190
+ || day < 1 || day > monthDays[month - 1]
191
+ || hour > 23
192
+ || minute > 59
193
+ || second > 59
194
+ || offsetHour > 23
195
+ || offsetMinute > 59) {
196
+ throw new ResultStoreError("invalid_result", `${label} must be valid RFC3339`);
197
+ }
198
+ const instant = Date.parse(value);
199
+ if (!Number.isFinite(instant)) {
200
+ throw new ResultStoreError("invalid_result", `${label} must be valid RFC3339`);
201
+ }
202
+ return instant;
203
+ }
204
+ function assertArtifactOutput(output, result, label) {
205
+ assertExactKeys(output, ["name", "kind", "capture"], label);
206
+ assertArtifactCapture(output.capture, result, `${label}.capture`, output.name);
207
+ }
208
+ function assertArtifactCapture(value, result, label, outputName) {
209
+ assertRecord(value, label);
210
+ const capture = value;
211
+ assertExactKeys(capture, [
212
+ "capture_id",
213
+ "artifact_id",
214
+ "artifact_kind",
215
+ "run_id",
216
+ "activation_id",
217
+ "attempt_id",
218
+ "output_name",
219
+ "source_workspace_id",
220
+ "source_path",
221
+ "captured_at",
222
+ ], label);
223
+ for (const field of [
224
+ "capture_id",
225
+ "artifact_id",
226
+ "run_id",
227
+ "activation_id",
228
+ "attempt_id",
229
+ "output_name",
230
+ "source_workspace_id",
231
+ "source_path",
232
+ "captured_at",
233
+ ]) {
234
+ assertCanonicalNonEmptyString(capture[field], `${label}.${field}`);
235
+ }
236
+ if (!SHA256_DIGEST_PATTERN.test(capture.artifact_id)) {
237
+ throw new ResultStoreError("invalid_result", `${label}.artifact_id must be a lowercase sha256 digest`);
238
+ }
239
+ if (!new Set(["file", "directory", "git_commit"]).has(capture.artifact_kind)) {
240
+ throw new ResultStoreError("invalid_result", `${label}.artifact_kind is invalid`);
241
+ }
242
+ const capturedAt = parseRfc3339(capture.captured_at, `${label}.captured_at`);
243
+ const committedAt = parseRfc3339(result.committed_at, "result.committed_at");
244
+ if (capture.run_id !== result.run_id
245
+ || capture.activation_id !== result.activation_id
246
+ || capture.attempt_id !== result.attempt_id
247
+ || (outputName !== undefined && capture.output_name !== outputName)) {
248
+ throw new ResultStoreError("invalid_result", `${label} does not match its result and output identity`);
249
+ }
250
+ if (capturedAt > committedAt) {
251
+ throw new ResultStoreError("invalid_result", `${label}.captured_at must not be later than result.committed_at`);
252
+ }
253
+ }
254
+ function assertCanonicalOutput(value, index, result) {
255
+ const label = `result.outputs[${index}]`;
256
+ assertRecord(value, label);
257
+ assertCanonicalNonEmptyString(value.name, `${label}.name`);
258
+ if (value.kind === "artifact") {
259
+ assertArtifactOutput(value, result, label);
260
+ return;
261
+ }
262
+ if (value.kind === "text") {
263
+ assertExactKeys(value, ["name", "kind", "text"], label);
264
+ if (typeof value.text !== "string") {
265
+ throw new ResultStoreError("invalid_result", `${label}.text must be a string`);
266
+ }
267
+ return;
268
+ }
269
+ if (value.kind === "structured") {
270
+ assertExactKeys(value, ["name", "kind", "value", "schema_ref"], label);
271
+ if (value.schema_ref !== null && typeof value.schema_ref !== "string") {
272
+ throw new ResultStoreError("invalid_result", `${label}.schema_ref must be a string or null`);
273
+ }
274
+ assertJsonValue(value.value, `${label}.value`);
275
+ return;
276
+ }
277
+ throw new ResultStoreError("invalid_result", `${label}.kind must be artifact, text, or structured`);
278
+ }
279
+ function assertWorkerStopReceipt(value, result, label) {
280
+ assertRecord(value, label);
281
+ assertExactKeys(value, ["receipt_digest", "payload"], label);
282
+ if (!SHA256_DIGEST_PATTERN.test(value.receipt_digest)) {
283
+ throw new ResultStoreError("invalid_result", `${label}.receipt_digest must be a lowercase sha256 digest`);
284
+ }
285
+ assertRecord(value.payload, `${label}.payload`);
286
+ assertExactKeys(value.payload, [
287
+ "schema_version",
288
+ "run_id",
289
+ "step_id",
290
+ "activation_id",
291
+ "attempt_id",
292
+ "claim_epoch",
293
+ "worker_session_id",
294
+ "execution_instance_id",
295
+ "authority_kind",
296
+ "authority_instance_id",
297
+ "fencing_epoch",
298
+ "started_at",
299
+ "stopped_at",
300
+ "terminal_outcome",
301
+ "process_tree_drained",
302
+ "source_evidence_digest",
303
+ ], `${label}.payload`);
304
+ const payload = value.payload;
305
+ if (payload.schema_version !== "intentdna.worker_stop_receipt.v1") {
306
+ throw new ResultStoreError("invalid_result", `${label}.payload.schema_version is unsupported`);
307
+ }
308
+ for (const field of [
309
+ "run_id",
310
+ "step_id",
311
+ "activation_id",
312
+ "attempt_id",
313
+ "worker_session_id",
314
+ "execution_instance_id",
315
+ "authority_instance_id",
316
+ "started_at",
317
+ "stopped_at",
318
+ ]) {
319
+ assertCanonicalNonEmptyString(payload[field], `${label}.payload.${field}`);
320
+ }
321
+ if (!Number.isSafeInteger(payload.claim_epoch) || payload.claim_epoch <= 0
322
+ || !Number.isSafeInteger(payload.fencing_epoch) || payload.fencing_epoch <= 0) {
323
+ throw new ResultStoreError("invalid_result", `${label}.payload epochs must be positive safe integers`);
324
+ }
325
+ if (!["local_process", "host_attested"].includes(payload.authority_kind)) {
326
+ throw new ResultStoreError("invalid_result", `${label}.payload.authority_kind is invalid`);
327
+ }
328
+ if (payload.process_tree_drained !== true) {
329
+ throw new ResultStoreError("invalid_result", `${label}.payload must prove a drained process tree`);
330
+ }
331
+ if (!SHA256_DIGEST_PATTERN.test(payload.source_evidence_digest)) {
332
+ throw new ResultStoreError("invalid_result", `${label}.payload.source_evidence_digest is invalid`);
333
+ }
334
+ if (payload.run_id !== result.run_id
335
+ || payload.step_id !== result.step_id
336
+ || payload.activation_id !== result.activation_id
337
+ || payload.attempt_id !== result.attempt_id
338
+ || result.executor.kind !== "worker"
339
+ || payload.worker_session_id !== result.executor.worker_session_id
340
+ || payload.started_at !== result.started_at
341
+ || payload.stopped_at !== result.completed_at
342
+ || canonicalizeJson(payload.terminal_outcome) !== canonicalizeJson(result.terminal_outcome)) {
343
+ throw new ResultStoreError("invalid_result", `${label} does not match its worker result`);
344
+ }
345
+ parseRfc3339(payload.started_at, `${label}.payload.started_at`);
346
+ parseRfc3339(payload.stopped_at, `${label}.payload.stopped_at`);
347
+ const receipt = value;
348
+ if (receipt.receipt_digest !== workerStopReceiptDigest(receipt.payload)) {
349
+ throw new ResultStoreError("invalid_result", `${label}.receipt_digest does not match its payload`);
350
+ }
351
+ }
352
+ function assertVerifierProcessOutcome(value, label) {
353
+ assertRecord(value, label);
354
+ assertExactKeys(value, ["kind", "exit_code", "signal", "timed_out", "launch_error"], label);
355
+ if (value.kind === "exited") {
356
+ if (!Number.isSafeInteger(value.exit_code)
357
+ || value.exit_code < 0
358
+ || value.signal !== null
359
+ || value.timed_out !== false
360
+ || value.launch_error !== null)
361
+ throw new ResultStoreError("invalid_result", `${label} has an invalid exited tuple`);
362
+ return;
363
+ }
364
+ if (value.kind === "signaled") {
365
+ if (value.exit_code !== null || value.timed_out !== false || value.launch_error !== null) {
366
+ throw new ResultStoreError("invalid_result", `${label} has an invalid signaled tuple`);
367
+ }
368
+ assertCanonicalNonEmptyString(value.signal, `${label}.signal`);
369
+ return;
370
+ }
371
+ if (value.kind === "timed_out") {
372
+ if ((value.exit_code !== null && (!Number.isSafeInteger(value.exit_code) || value.exit_code < 0))
373
+ || (value.exit_code === 0)
374
+ || (value.signal !== null && (typeof value.signal !== "string" || value.signal.length === 0))
375
+ || value.timed_out !== true
376
+ || value.launch_error !== null)
377
+ throw new ResultStoreError("invalid_result", `${label} has an invalid timed_out tuple`);
378
+ return;
379
+ }
380
+ if (value.kind === "launch_error") {
381
+ if (value.exit_code !== null || value.signal !== null || value.timed_out !== false) {
382
+ throw new ResultStoreError("invalid_result", `${label} has an invalid launch_error tuple`);
383
+ }
384
+ assertRecord(value.launch_error, `${label}.launch_error`);
385
+ assertExactKeys(value.launch_error, ["code", "message_digest"], `${label}.launch_error`);
386
+ assertCanonicalNonEmptyString(value.launch_error.code, `${label}.launch_error.code`);
387
+ if (!/^[A-Z][A-Z0-9_]*$/u.test(value.launch_error.code)
388
+ || !SHA256_DIGEST_PATTERN.test(value.launch_error.message_digest)) {
389
+ throw new ResultStoreError("invalid_result", `${label}.launch_error is invalid`);
390
+ }
391
+ return;
392
+ }
393
+ throw new ResultStoreError("invalid_result", `${label}.kind is invalid`);
394
+ }
395
+ function assertVerifierExecutionReceipt(value, verifierId, result, label) {
396
+ assertRecord(value, label);
397
+ assertExactKeys(value, [
398
+ "schema_version",
399
+ "evidence_mode",
400
+ "plan_id",
401
+ "verifier_id",
402
+ "verifier_definition_digest",
403
+ "resolved_command_digest",
404
+ "environment_binding_digest",
405
+ "cwd_identity",
406
+ "started_at",
407
+ "requested_at",
408
+ "completed_at",
409
+ "process_outcome",
410
+ "stdout",
411
+ "stderr",
412
+ "truncation_policy",
413
+ "observation_policy_digest",
414
+ "workspace_observation_digest",
415
+ "service_invocation",
416
+ "request_identity",
417
+ "supplied_environment",
418
+ "shell_policy",
419
+ "network_policy",
420
+ "execution_authority",
421
+ ], label);
422
+ if (value.schema_version !== "intentdna.verifier_execution_receipt.v1"
423
+ || value.evidence_mode !== "historical_execution") {
424
+ throw new ResultStoreError("invalid_result", `${label} has an unsupported receipt contract`);
425
+ }
426
+ assertCanonicalNonEmptyString(value.verifier_id, `${label}.verifier_id`);
427
+ if (value.verifier_id !== verifierId) {
428
+ throw new ResultStoreError("invalid_result", `${label}.verifier_id must match its evidence`);
429
+ }
430
+ for (const field of [
431
+ "plan_id",
432
+ "verifier_definition_digest",
433
+ "resolved_command_digest",
434
+ "environment_binding_digest",
435
+ "observation_policy_digest",
436
+ "workspace_observation_digest",
437
+ ]) {
438
+ if (!SHA256_DIGEST_PATTERN.test(value[field])) {
439
+ throw new ResultStoreError("invalid_result", `${label}.${field} must be a lowercase sha256 digest`);
440
+ }
441
+ }
442
+ assertRecord(value.cwd_identity, `${label}.cwd_identity`);
443
+ assertExactKeys(value.cwd_identity, ["workspace_id", "workspace_allocation_digest", "relative_working_directory", "absolute_path_digest"], `${label}.cwd_identity`);
444
+ assertCanonicalNonEmptyString(value.cwd_identity.workspace_id, `${label}.cwd_identity.workspace_id`);
445
+ assertCanonicalNonEmptyString(value.cwd_identity.relative_working_directory, `${label}.cwd_identity.relative_working_directory`);
446
+ if (value.cwd_identity.workspace_id !== result.attempt_id
447
+ || !SHA256_DIGEST_PATTERN.test(value.cwd_identity.workspace_allocation_digest)
448
+ || !SHA256_DIGEST_PATTERN.test(value.cwd_identity.absolute_path_digest)) {
449
+ throw new ResultStoreError("invalid_result", `${label}.cwd_identity does not match the attempt workspace`);
450
+ }
451
+ const relativeWorkingDirectory = value.cwd_identity.relative_working_directory;
452
+ if (relativeWorkingDirectory.startsWith("/")
453
+ || relativeWorkingDirectory.includes("\0")
454
+ || relativeWorkingDirectory.split(/[\\/]+/u).includes("..")) {
455
+ throw new ResultStoreError("invalid_result", `${label}.cwd_identity must be relative and contained`);
456
+ }
457
+ const requestedAt = parseRfc3339(value.requested_at, `${label}.requested_at`);
458
+ const startedAt = parseRfc3339(value.started_at, `${label}.started_at`);
459
+ const completedAt = parseRfc3339(value.completed_at, `${label}.completed_at`);
460
+ if (requestedAt > startedAt
461
+ || startedAt > completedAt
462
+ || startedAt < parseRfc3339(result.completed_at, "result.completed_at")
463
+ || completedAt > parseRfc3339(result.committed_at, "result.committed_at")) {
464
+ throw new ResultStoreError("invalid_result", `${label} must execute after worker stop and before result commit`);
465
+ }
466
+ assertVerifierProcessOutcome(value.process_outcome, `${label}.process_outcome`);
467
+ for (const streamName of ["stdout", "stderr"]) {
468
+ const stream = value[streamName];
469
+ assertRecord(stream, `${label}.${streamName}`);
470
+ assertExactKeys(stream, ["digest", "byte_length", "truncated"], `${label}.${streamName}`);
471
+ if (!SHA256_DIGEST_PATTERN.test(stream.digest)) {
472
+ throw new ResultStoreError("invalid_result", `${label}.${streamName}.digest is invalid`);
473
+ }
474
+ if (!Number.isSafeInteger(stream.byte_length) || stream.byte_length < 0) {
475
+ throw new ResultStoreError("invalid_result", `${label}.${streamName}.byte_length is invalid`);
476
+ }
477
+ if (stream.truncated !== (stream.byte_length > 0)) {
478
+ throw new ResultStoreError("invalid_result", `${label}.${streamName}.truncated violates retention policy`);
479
+ }
480
+ }
481
+ assertRecord(value.truncation_policy, `${label}.truncation_policy`);
482
+ assertExactKeys(value.truncation_policy, ["kind", "raw_output_retained_bytes", "diagnostic_summary_max_bytes", "diagnostic_line_max_bytes"], `${label}.truncation_policy`);
483
+ if (value.truncation_policy.kind !== "digest_full_stream_diagnostics_only"
484
+ || value.truncation_policy.raw_output_retained_bytes !== 0
485
+ || !Number.isSafeInteger(value.truncation_policy.diagnostic_summary_max_bytes)
486
+ || value.truncation_policy.diagnostic_summary_max_bytes <= 0
487
+ || !Number.isSafeInteger(value.truncation_policy.diagnostic_line_max_bytes)
488
+ || value.truncation_policy.diagnostic_line_max_bytes <= 0) {
489
+ throw new ResultStoreError("invalid_result", `${label}.truncation_policy is invalid`);
490
+ }
491
+ assertRecord(value.service_invocation, `${label}.service_invocation`);
492
+ assertExactKeys(value.service_invocation, ["service_instance_id", "invocation_id"], `${label}.service_invocation`);
493
+ assertCanonicalNonEmptyString(value.service_invocation.service_instance_id, `${label}.service_invocation.service_instance_id`);
494
+ assertCanonicalNonEmptyString(value.service_invocation.invocation_id, `${label}.service_invocation.invocation_id`);
495
+ assertRecord(value.request_identity, `${label}.request_identity`);
496
+ assertExactKeys(value.request_identity, ["request_digest", "command_digest", "environment_digest", "authority_descriptor_digest", "launch_descriptor_digest"], `${label}.request_identity`);
497
+ for (const field of ["request_digest", "command_digest", "environment_digest", "authority_descriptor_digest", "launch_descriptor_digest"]) {
498
+ if (!SHA256_DIGEST_PATTERN.test(value.request_identity[field])) {
499
+ throw new ResultStoreError("invalid_result", `${label}.request_identity.${field} is invalid`);
500
+ }
501
+ }
502
+ if (value.request_identity.command_digest !== value.resolved_command_digest
503
+ || value.request_identity.environment_digest !== value.environment_binding_digest) {
504
+ throw new ResultStoreError("invalid_result", `${label}.request_identity does not bind command and environment`);
505
+ }
506
+ assertRecord(value.supplied_environment, `${label}.supplied_environment`);
507
+ assertExactKeys(value.supplied_environment, ["inherit_parent", "startup_files", "variable_names"], `${label}.supplied_environment`);
508
+ if (value.supplied_environment.inherit_parent !== false
509
+ || value.supplied_environment.startup_files !== false
510
+ || !Array.isArray(value.supplied_environment.variable_names)
511
+ || value.supplied_environment.variable_names.some((name) => typeof name !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(name))
512
+ || canonicalizeJson(value.supplied_environment.variable_names)
513
+ !== canonicalizeJson([...value.supplied_environment.variable_names].sort())) {
514
+ throw new ResultStoreError("invalid_result", `${label}.supplied_environment is invalid`);
515
+ }
516
+ assertRecord(value.shell_policy, `${label}.shell_policy`);
517
+ assertExactKeys(value.shell_policy, ["executable", "arguments"], `${label}.shell_policy`);
518
+ if (value.shell_policy.executable !== "bash"
519
+ || canonicalizeJson(value.shell_policy.arguments) !== canonicalizeJson(["--noprofile", "--norc", "-c"])) {
520
+ throw new ResultStoreError("invalid_result", `${label}.shell_policy is invalid`);
521
+ }
522
+ assertRecord(value.network_policy, `${label}.network_policy`);
523
+ assertExactKeys(value.network_policy, ["mode", "enforced", "enforcement_kind", "enforcement_digest", "enforcement_descriptor"], `${label}.network_policy`);
524
+ const denyAll = value.network_policy.mode === "deny_all";
525
+ const allow = value.network_policy.mode === "allow";
526
+ if ((!denyAll && !allow)
527
+ || (denyAll && (value.network_policy.enforced !== true
528
+ || value.network_policy.enforcement_kind !== "configured_adapter"
529
+ || !SHA256_DIGEST_PATTERN.test(value.network_policy.enforcement_digest)
530
+ || value.network_policy.enforcement_descriptor === null))
531
+ || (allow && (value.network_policy.enforced !== false
532
+ || value.network_policy.enforcement_kind !== "not_required"
533
+ || value.network_policy.enforcement_digest !== null
534
+ || value.network_policy.enforcement_descriptor !== null))) {
535
+ throw new ResultStoreError("invalid_result", `${label}.network_policy is invalid`);
536
+ }
537
+ if (denyAll) {
538
+ assertRecord(value.network_policy.enforcement_descriptor, `${label}.network_policy.enforcement_descriptor`);
539
+ const enforcementDescriptor = value.network_policy.enforcement_descriptor;
540
+ assertExactKeys(enforcementDescriptor, ["schema_version", "mode", "adapter_kind", "adapter_version", "configuration_digest", "authority_descriptor_digest", "request_digest"], `${label}.network_policy.enforcement_descriptor`);
541
+ if (enforcementDescriptor.schema_version !== "intentdna.verifier_network_enforcement.v1"
542
+ || enforcementDescriptor.mode !== "deny_all"
543
+ || !SHA256_DIGEST_PATTERN.test(enforcementDescriptor.configuration_digest)
544
+ || !SHA256_DIGEST_PATTERN.test(enforcementDescriptor.authority_descriptor_digest)
545
+ || !SHA256_DIGEST_PATTERN.test(enforcementDescriptor.request_digest)
546
+ || value.network_policy.enforcement_digest !== canonicalResultDigest(Buffer.from(canonicalizeJson(enforcementDescriptor), "utf8")))
547
+ throw new ResultStoreError("invalid_result", `${label}.network_policy.enforcement_descriptor is invalid`);
548
+ }
549
+ assertRecord(value.execution_authority, `${label}.execution_authority`);
550
+ assertExactKeys(value.execution_authority, ["authority_instance_id", "authority_descriptor_digest", "adapter_kind", "adapter_version", "configuration_digest", "process_tree_drained"], `${label}.execution_authority`);
551
+ assertCanonicalNonEmptyString(value.execution_authority.authority_instance_id, `${label}.execution_authority.authority_instance_id`);
552
+ assertCanonicalNonEmptyString(value.execution_authority.adapter_kind, `${label}.execution_authority.adapter_kind`);
553
+ assertCanonicalNonEmptyString(value.execution_authority.adapter_version, `${label}.execution_authority.adapter_version`);
554
+ if (!SHA256_DIGEST_PATTERN.test(value.execution_authority.authority_descriptor_digest)
555
+ || !SHA256_DIGEST_PATTERN.test(value.execution_authority.configuration_digest)
556
+ || value.execution_authority.process_tree_drained !== true
557
+ || value.execution_authority.authority_descriptor_digest !== value.request_identity.authority_descriptor_digest
558
+ || value.request_identity.launch_descriptor_digest !== value.request_identity.request_digest
559
+ || (denyAll && (value.network_policy.enforcement_descriptor.adapter_kind !== value.execution_authority.adapter_kind
560
+ || value.network_policy.enforcement_descriptor.adapter_version !== value.execution_authority.adapter_version
561
+ || value.network_policy.enforcement_descriptor.configuration_digest !== value.execution_authority.configuration_digest
562
+ || value.network_policy.enforcement_descriptor.authority_descriptor_digest !== value.request_identity.authority_descriptor_digest
563
+ || value.network_policy.enforcement_descriptor.request_digest !== value.request_identity.request_digest))) {
564
+ throw new ResultStoreError("invalid_result", `${label}.execution_authority is invalid`);
565
+ }
566
+ }
567
+ function assertWorkspaceObservation(value, evidenceDigest, label, artifactCapture, result, executionReceipt) {
568
+ if (value === null)
569
+ return;
570
+ assertRecord(value, `${label}.workspace_observation`);
571
+ assertExactKeys(value, [
572
+ "schema_version",
573
+ "workspace_id",
574
+ "scope",
575
+ "observed_digest",
576
+ "entry_count",
577
+ "byte_count",
578
+ "git",
579
+ ], `${label}.workspace_observation`);
580
+ if (value.schema_version !== "intentdna.workspace_observation.v2") {
581
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.schema_version is unsupported`);
582
+ }
583
+ assertCanonicalNonEmptyString(value.workspace_id, `${label}.workspace_observation.workspace_id`);
584
+ if (value.workspace_id !== result.attempt_id) {
585
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.workspace_id must match result.attempt_id`);
586
+ }
587
+ if (!SHA256_DIGEST_PATTERN.test(value.observed_digest)) {
588
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.observed_digest must be a lowercase sha256 digest`);
589
+ }
590
+ const expectedObservationDigest = executionReceipt?.workspace_observation_digest
591
+ ?? (artifactCapture === null ? evidenceDigest : value.observed_digest);
592
+ if (value.observed_digest !== expectedObservationDigest) {
593
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.observed_digest does not match its evidence binding`);
594
+ }
595
+ assertRecord(value.scope, `${label}.workspace_observation.scope`);
596
+ assertExactKeys(value.scope, ["kind", "relative_root", "excluded_relative_paths"], `${label}.workspace_observation.scope`);
597
+ if (value.scope.kind !== "attempt_workspace"
598
+ || value.scope.relative_root !== "."
599
+ || !Array.isArray(value.scope.excluded_relative_paths)
600
+ || canonicalizeJson(value.scope.excluded_relative_paths)
601
+ !== canonicalizeJson([".dna/runtime", ".dna/worktrees/runtime"])) {
602
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.scope is not canonical`);
603
+ }
604
+ for (const field of ["entry_count", "byte_count"]) {
605
+ if (!Number.isSafeInteger(value[field]) || value[field] < 0) {
606
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.${field} must be a non-negative safe integer`);
607
+ }
608
+ }
609
+ if (value.git !== null) {
610
+ assertRecord(value.git, `${label}.workspace_observation.git`);
611
+ assertExactKeys(value.git, [
612
+ "repository_identity_digest",
613
+ "head",
614
+ "symbolic_branch",
615
+ "porcelain_v2_digest",
616
+ "local_config_digest",
617
+ ], `${label}.workspace_observation.git`);
618
+ for (const field of [
619
+ "repository_identity_digest",
620
+ "porcelain_v2_digest",
621
+ "local_config_digest",
622
+ ]) {
623
+ if (!SHA256_DIGEST_PATTERN.test(value.git[field])) {
624
+ throw new ResultStoreError("invalid_result", `${label}.workspace_observation.git.${field} must be a lowercase sha256 digest`);
625
+ }
626
+ }
627
+ assertCanonicalNonEmptyString(value.git.head, `${label}.workspace_observation.git.head`);
628
+ if (value.git.symbolic_branch !== null) {
629
+ assertCanonicalNonEmptyString(value.git.symbolic_branch, `${label}.workspace_observation.git.symbolic_branch`);
630
+ }
631
+ }
632
+ }
633
+ function assertVerifierEvidence(value, label, result, expectedVerifierId) {
634
+ assertExactKeys(value, [
635
+ "kind",
636
+ "verifier_id",
637
+ "evidence_digest",
638
+ "artifact_capture",
639
+ "workspace_observation",
640
+ "execution_receipt",
641
+ ], label);
642
+ assertCanonicalNonEmptyString(value.verifier_id, `${label}.verifier_id`);
643
+ if (expectedVerifierId !== undefined && value.verifier_id !== expectedVerifierId) {
644
+ throw new ResultStoreError("invalid_result", `${label}.verifier_id must match its verification result`);
645
+ }
646
+ if (!SHA256_DIGEST_PATTERN.test(value.evidence_digest)) {
647
+ throw new ResultStoreError("invalid_result", `${label}.evidence_digest must be a lowercase sha256 digest`);
648
+ }
649
+ if (value.artifact_capture !== null) {
650
+ assertArtifactCapture(value.artifact_capture, result, `${label}.artifact_capture`);
651
+ }
652
+ if (value.execution_receipt !== null) {
653
+ if (value.artifact_capture !== null || value.workspace_observation === null) {
654
+ throw new ResultStoreError("invalid_result", `${label}.execution_receipt requires exactly one workspace observation`);
655
+ }
656
+ assertVerifierExecutionReceipt(value.execution_receipt, value.verifier_id, result, `${label}.execution_receipt`);
657
+ const expectedEvidenceDigest = canonicalResultDigest(Buffer.from(canonicalizeJson({
658
+ execution_receipt: value.execution_receipt,
659
+ workspace_observation: value.workspace_observation,
660
+ }), "utf8"));
661
+ if (value.evidence_digest !== expectedEvidenceDigest) {
662
+ throw new ResultStoreError("invalid_result", `${label}.evidence_digest does not bind its receipt and observation`);
663
+ }
664
+ }
665
+ if (value.execution_receipt === null
666
+ && value.artifact_capture !== null
667
+ && value.workspace_observation !== null) {
668
+ const expectedEvidenceDigest = canonicalResultDigest(Buffer.from(canonicalizeJson({
669
+ artifact_capture: value.artifact_capture,
670
+ workspace_observation: value.workspace_observation,
671
+ }), "utf8"));
672
+ if (value.evidence_digest !== expectedEvidenceDigest) {
673
+ throw new ResultStoreError("invalid_result", `${label}.evidence_digest does not bind its artifact capture and workspace observation`);
674
+ }
675
+ }
676
+ if (value.artifact_capture === null
677
+ && value.workspace_observation === null
678
+ && value.execution_receipt === null) {
679
+ throw new ResultStoreError("invalid_result", `${label} must bind immutable verifier evidence`);
680
+ }
681
+ assertWorkspaceObservation(value.workspace_observation, value.evidence_digest, label, value.artifact_capture, result, value.execution_receipt);
682
+ }
683
+ function assertEvidenceRef(value, index, result) {
684
+ const label = typeof index === "number"
685
+ ? `result.evidence_refs[${index}]`
686
+ : `result.${index}`;
687
+ assertRecord(value, label);
688
+ if (value.kind === "artifact_capture") {
689
+ assertExactKeys(value, ["kind", "capture"], label);
690
+ assertArtifactCapture(value.capture, result, `${label}.capture`);
691
+ return;
692
+ }
693
+ if (value.kind === "submission") {
694
+ assertExactKeys(value, ["kind", "submission_key_digest", "submission_digest"], label);
695
+ for (const field of [
696
+ "submission_key_digest",
697
+ "submission_digest",
698
+ ]) {
699
+ if (!SHA256_DIGEST_PATTERN.test(value[field])) {
700
+ throw new ResultStoreError("invalid_result", `${label}.${field} must be a lowercase sha256 digest`);
701
+ }
702
+ }
703
+ return;
704
+ }
705
+ if (value.kind === "result") {
706
+ assertExactKeys(value, ["kind", "result"], label);
707
+ assertRecord(value.result, `${label}.result`);
708
+ assertExactKeys(value.result, [
709
+ "result_id",
710
+ "run_id",
711
+ "step_id",
712
+ "activation_id",
713
+ "attempt_id",
714
+ "result_digest",
715
+ ], `${label}.result`);
716
+ for (const field of [
717
+ "result_id",
718
+ "run_id",
719
+ "step_id",
720
+ "activation_id",
721
+ "attempt_id",
722
+ ]) {
723
+ assertCanonicalNonEmptyString(value.result[field], `${label}.result.${field}`);
724
+ }
725
+ if (!SHA256_DIGEST_PATTERN.test(value.result.result_digest)) {
726
+ throw new ResultStoreError("invalid_result", `${label}.result.result_digest must be a lowercase sha256 digest`);
727
+ }
728
+ return;
729
+ }
730
+ if (value.kind === "caller_attestation") {
731
+ assertExactKeys(value, value.payload === undefined
732
+ ? ["kind", "attestation_ref", "attestation_digest"]
733
+ : ["kind", "attestation_ref", "attestation_digest", "payload"], label);
734
+ assertCanonicalNonEmptyString(value.attestation_ref, `${label}.attestation_ref`);
735
+ if (!SHA256_DIGEST_PATTERN.test(value.attestation_digest)) {
736
+ throw new ResultStoreError("invalid_result", `${label}.attestation_digest must be a lowercase sha256 digest`);
737
+ }
738
+ if (value.payload !== undefined) {
739
+ const payloadRecord = value.payload;
740
+ assertRecord(payloadRecord, `${label}.payload`);
741
+ assertExactKeys(payloadRecord, [
742
+ "schema_version",
743
+ "authority_id",
744
+ "agent_id",
745
+ "session_id",
746
+ "run_id",
747
+ "step_id",
748
+ "activation_id",
749
+ "attempt_id",
750
+ "claim_epoch",
751
+ "worker_session_id",
752
+ "submission_key_digest",
753
+ "execution_digest",
754
+ ], `${label}.payload`);
755
+ if (payloadRecord.schema_version !== CALLER_ATTESTATION_SCHEMA_VERSION) {
756
+ throw new ResultStoreError("invalid_result", `${label}.payload.schema_version is unsupported`);
757
+ }
758
+ for (const field of [
759
+ "authority_id",
760
+ "agent_id",
761
+ "session_id",
762
+ "run_id",
763
+ "step_id",
764
+ "activation_id",
765
+ "attempt_id",
766
+ "worker_session_id",
767
+ ]) {
768
+ assertCanonicalNonEmptyString(payloadRecord[field], `${label}.payload.${field}`);
769
+ }
770
+ if (!Number.isSafeInteger(payloadRecord.claim_epoch)
771
+ || payloadRecord.claim_epoch <= 0) {
772
+ throw new ResultStoreError("invalid_result", `${label}.payload.claim_epoch must be a positive safe integer`);
773
+ }
774
+ for (const field of ["submission_key_digest", "execution_digest"]) {
775
+ if (typeof payloadRecord[field] !== "string"
776
+ || !SHA256_DIGEST_PATTERN.test(payloadRecord[field])) {
777
+ throw new ResultStoreError("invalid_result", `${label}.payload.${field} must be a lowercase sha256 digest`);
778
+ }
779
+ }
780
+ const payload = payloadRecord;
781
+ if (payload.run_id !== result.run_id
782
+ || payload.step_id !== result.step_id
783
+ || payload.activation_id !== result.activation_id
784
+ || payload.attempt_id !== result.attempt_id
785
+ || result.executor.kind !== "worker"
786
+ || payload.worker_session_id !== result.executor.worker_session_id) {
787
+ throw new ResultStoreError("invalid_result", `${label}.payload does not match the committed worker result`);
788
+ }
789
+ const expectedDigest = canonicalResultDigest(Buffer.from(canonicalizeJson(payload), "utf8"));
790
+ if (value.attestation_digest !== expectedDigest) {
791
+ throw new ResultStoreError("invalid_result", `${label}.attestation_digest does not match its payload`);
792
+ }
793
+ }
794
+ return;
795
+ }
796
+ if (value.kind === "worker_stopped") {
797
+ assertExactKeys(value, ["kind", "receipt"], label);
798
+ assertWorkerStopReceipt(value.receipt, result, `${label}.receipt`);
799
+ return;
800
+ }
801
+ if (value.kind === "verifier_evidence") {
802
+ assertVerifierEvidence(value, label, result);
803
+ return;
804
+ }
805
+ throw new ResultStoreError("invalid_result", `${label}.kind is not a supported immutable evidence reference`);
806
+ }
807
+ function assertVerificationResult(value, index, result) {
808
+ const label = `result.verification_results[${index}]`;
809
+ assertRecord(value, label);
810
+ assertExactKeys(value, ["verifier_id", "authority", "passed", "reason_code", "evidence_refs", "domain_value"], label);
811
+ assertCanonicalNonEmptyString(value.verifier_id, `${label}.verifier_id`);
812
+ if (value.authority !== "service" && value.authority !== "additional") {
813
+ throw new ResultStoreError("invalid_result", `${label}.authority is invalid`);
814
+ }
815
+ if (typeof value.passed !== "boolean") {
816
+ throw new ResultStoreError("invalid_result", `${label}.passed must be a boolean`);
817
+ }
818
+ if (typeof value.reason_code !== "string") {
819
+ throw new ResultStoreError("invalid_result", `${label}.reason_code must be a string`);
820
+ }
821
+ assertJsonValue(value.domain_value, `${label}.domain_value`);
822
+ if (!Array.isArray(value.evidence_refs)) {
823
+ throw new ResultStoreError("invalid_result", `${label}.evidence_refs must be an array`);
824
+ }
825
+ value.evidence_refs.forEach((evidence, evidenceIndex) => {
826
+ const evidenceLabel = `${label}.evidence_refs[${evidenceIndex}]`;
827
+ assertRecord(evidence, evidenceLabel);
828
+ if (evidence.kind !== "verifier_evidence") {
829
+ throw new ResultStoreError("invalid_result", `${evidenceLabel}.kind must be verifier_evidence`);
830
+ }
831
+ assertVerifierEvidence(evidence, evidenceLabel, result, value.verifier_id);
832
+ const executionReceipt = evidence.execution_receipt;
833
+ if (executionReceipt !== null
834
+ && value.authority !== "service") {
835
+ throw new ResultStoreError("invalid_result", `${label}.execution_receipt requires service authority`);
836
+ }
837
+ if (executionReceipt !== null
838
+ && value.passed !== (executionReceipt.process_outcome.kind === "exited"
839
+ && executionReceipt.process_outcome.exit_code === 0)) {
840
+ throw new ResultStoreError("invalid_result", `${label}.passed contradicts its verifier execution receipt`);
841
+ }
842
+ });
843
+ }
844
+ function assertCanonicalCommittedResult(value) {
845
+ assertRecord(value, "result");
846
+ assertExactKeys(value, [
847
+ "schema_version",
848
+ "result_id",
849
+ "run_id",
850
+ "step_id",
851
+ "activation_id",
852
+ "attempt_id",
853
+ "executor",
854
+ "terminal_outcome",
855
+ "outputs",
856
+ "started_at",
857
+ "completed_at",
858
+ "committed_at",
859
+ "stdout_reference",
860
+ "stderr_reference",
861
+ "evidence_refs",
862
+ "verification_results",
863
+ ...(Object.prototype.hasOwnProperty.call(value, "system_origin") ? ["system_origin"] : []),
864
+ ], "result");
865
+ if (value.schema_version !== CANONICAL_RESULT_SCHEMA_VERSION) {
866
+ throw new ResultStoreError("invalid_result", `result.schema_version must be ${CANONICAL_RESULT_SCHEMA_VERSION}`);
867
+ }
868
+ for (const field of [
869
+ "result_id",
870
+ "run_id",
871
+ "step_id",
872
+ "activation_id",
873
+ "attempt_id",
874
+ ]) {
875
+ assertCanonicalNonEmptyString(value[field], `result.${field}`);
876
+ }
877
+ try {
878
+ validateCanonicalAttemptOutcome(value.executor, value.terminal_outcome);
879
+ }
880
+ catch (error) {
881
+ throw new ResultStoreError("invalid_result", "result executor and terminal_outcome are invalid", { cause: error });
882
+ }
883
+ const startedAt = parseRfc3339(value.started_at, "result.started_at");
884
+ const completedAt = parseRfc3339(value.completed_at, "result.completed_at");
885
+ const committedAt = parseRfc3339(value.committed_at, "result.committed_at");
886
+ if (startedAt > completedAt || completedAt > committedAt) {
887
+ throw new ResultStoreError("invalid_result", "result timestamps must satisfy started_at <= completed_at <= committed_at");
888
+ }
889
+ if (!Array.isArray(value.outputs)) {
890
+ throw new ResultStoreError("invalid_result", "result.outputs must be an array");
891
+ }
892
+ const result = value;
893
+ if (value.system_origin !== undefined && value.system_origin !== null) {
894
+ assertRecord(value.system_origin, "result.system_origin");
895
+ const origin = value.system_origin;
896
+ if (origin.kind === "run_input") {
897
+ assertExactKeys(origin, ["kind", "input_names"], "result.system_origin");
898
+ if (!Array.isArray(origin.input_names) || origin.input_names.length === 0) {
899
+ throw new ResultStoreError("invalid_result", "result.system_origin.input_names must be a non-empty array");
900
+ }
901
+ origin.input_names.forEach((item, index) => assertCanonicalNonEmptyString(item, `result.system_origin.input_names[${index}]`));
902
+ }
903
+ else if (origin.kind === "legacy_external") {
904
+ assertExactKeys(origin, ["kind", "sources"], "result.system_origin");
905
+ if (!Array.isArray(origin.sources) || origin.sources.length === 0) {
906
+ throw new ResultStoreError("invalid_result", "result.system_origin.sources must be a non-empty array");
907
+ }
908
+ origin.sources.forEach((source, index) => {
909
+ assertRecord(source, `result.system_origin.sources[${index}]`);
910
+ assertExactKeys(source, ["source_path", "expected_digest", "artifact_id"], `result.system_origin.sources[${index}]`);
911
+ assertCanonicalNonEmptyString(source.source_path, `result.system_origin.sources[${index}].source_path`);
912
+ assertCanonicalNonEmptyString(source.artifact_id, `result.system_origin.sources[${index}].artifact_id`);
913
+ if (!SHA256_DIGEST_PATTERN.test(source.expected_digest)) {
914
+ throw new ResultStoreError("invalid_result", `result.system_origin.sources[${index}].expected_digest must be a lowercase sha256 digest`);
915
+ }
916
+ });
917
+ }
918
+ else if (origin.kind === "canonical_import") {
919
+ assertExactKeys(origin, ["kind", "origin_plan_id", "origin_run_ids", "origin_result_ids", "origin_artifact_ids", "policy_id"], "result.system_origin");
920
+ if (!SHA256_DIGEST_PATTERN.test(origin.origin_plan_id)) {
921
+ throw new ResultStoreError("invalid_result", "result.system_origin.origin_plan_id must be a lowercase sha256 digest");
922
+ }
923
+ for (const field of ["origin_run_ids", "origin_result_ids", "origin_artifact_ids"]) {
924
+ if (!Array.isArray(origin[field]) || origin[field].length === 0) {
925
+ throw new ResultStoreError("invalid_result", `result.system_origin.${field} must be a non-empty array`);
926
+ }
927
+ origin[field].forEach((item, index) => assertCanonicalNonEmptyString(item, `result.system_origin.${field}[${index}]`));
928
+ }
929
+ if (origin.policy_id !== null)
930
+ assertCanonicalNonEmptyString(origin.policy_id, "result.system_origin.policy_id");
931
+ }
932
+ else if (origin.kind === "current_run_results") {
933
+ assertExactKeys(origin, ["kind", "source_result_ids"], "result.system_origin");
934
+ if (!Array.isArray(origin.source_result_ids) || origin.source_result_ids.length === 0) {
935
+ throw new ResultStoreError("invalid_result", "result.system_origin.source_result_ids must be a non-empty array");
936
+ }
937
+ origin.source_result_ids.forEach((item, index) => assertCanonicalNonEmptyString(item, `result.system_origin.source_result_ids[${index}]`));
938
+ }
939
+ else {
940
+ throw new ResultStoreError("invalid_result", "result.system_origin.kind is unsupported");
941
+ }
942
+ }
943
+ const names = new Set();
944
+ value.outputs.forEach((output, index) => {
945
+ assertCanonicalOutput(output, index, result);
946
+ if (names.has(output.name)) {
947
+ throw new ResultStoreError("invalid_result", `result has duplicate output ${output.name}`);
948
+ }
949
+ names.add(output.name);
950
+ });
951
+ if (!Array.isArray(value.evidence_refs)) {
952
+ throw new ResultStoreError("invalid_result", "result.evidence_refs must be an array");
953
+ }
954
+ value.evidence_refs.forEach((evidence, index) => {
955
+ assertEvidenceRef(evidence, index, result);
956
+ });
957
+ const workerStopEvidence = value.evidence_refs.filter((evidence) => (typeof evidence === "object" && evidence !== null && evidence.kind === "worker_stopped"));
958
+ if ((result.executor.kind === "worker" && workerStopEvidence.length !== 1)
959
+ || (result.executor.kind !== "worker" && workerStopEvidence.length !== 0)) {
960
+ throw new ResultStoreError("invalid_result", "worker results require exactly one service-owned stop receipt");
961
+ }
962
+ if (!Array.isArray(value.verification_results)) {
963
+ throw new ResultStoreError("invalid_result", "result.verification_results must be an array");
964
+ }
965
+ const verifierIds = new Set();
966
+ value.verification_results.forEach((verification, index) => {
967
+ assertVerificationResult(verification, index, result);
968
+ if (verifierIds.has(verification.verifier_id)) {
969
+ throw new ResultStoreError("invalid_result", `result has duplicate verifier ${verification.verifier_id}`);
970
+ }
971
+ verifierIds.add(verification.verifier_id);
972
+ });
973
+ if (value.stdout_reference !== null) {
974
+ assertEvidenceRef(value.stdout_reference, "stdout_reference", result);
975
+ }
976
+ if (value.stderr_reference !== null) {
977
+ assertEvidenceRef(value.stderr_reference, "stderr_reference", result);
978
+ }
979
+ try {
980
+ canonicalizeJson(value);
981
+ }
982
+ catch (error) {
983
+ throw new ResultStoreError("invalid_result", "result is not canonical JSON data", { cause: error });
984
+ }
985
+ }
986
+ function canonicalResultDigest(bytes) {
987
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
988
+ }
989
+ function resultFileName(resultId) {
990
+ return `${createHash("sha256").update(resultId, "utf8").digest("hex")}.json`;
991
+ }
992
+ function createCanonicalResultRef(result, resultDigest) {
993
+ return {
994
+ result_id: result.result_id,
995
+ run_id: result.run_id,
996
+ step_id: result.step_id,
997
+ activation_id: result.activation_id,
998
+ attempt_id: result.attempt_id,
999
+ result_digest: resultDigest,
1000
+ };
1001
+ }
1002
+ function assertCanonicalResultRef(reference) {
1003
+ if (typeof reference !== "object"
1004
+ || reference === null
1005
+ || Array.isArray(reference)
1006
+ || Object.getPrototypeOf(reference) !== Object.prototype
1007
+ || !exactObjectKeys(reference, [
1008
+ "result_id",
1009
+ "run_id",
1010
+ "step_id",
1011
+ "activation_id",
1012
+ "attempt_id",
1013
+ "result_digest",
1014
+ ])) {
1015
+ throw new ResultStoreError("invalid_reference", "reference has unexpected or missing fields");
1016
+ }
1017
+ for (const field of [
1018
+ "result_id",
1019
+ "run_id",
1020
+ "step_id",
1021
+ "activation_id",
1022
+ "attempt_id",
1023
+ ]) {
1024
+ if (typeof reference[field] !== "string" || !reference[field]) {
1025
+ throw new ResultStoreError("invalid_reference", `reference.${field} must be a non-empty string`);
1026
+ }
1027
+ }
1028
+ if (!SHA256_DIGEST_PATTERN.test(reference.result_digest)) {
1029
+ throw new ResultStoreError("invalid_reference", "reference.result_digest must be a lowercase sha256 digest");
1030
+ }
1031
+ }
1032
+ async function ensurePrivateDirectory(path) {
1033
+ await mkdir(path, { recursive: true, mode: DIRECTORY_MODE });
1034
+ const metadata = await lstat(path);
1035
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
1036
+ throw new ResultStoreError("immutable_conflict", `canonical result store path is not a real directory: ${path}`);
1037
+ }
1038
+ await chmod(path, DIRECTORY_MODE);
1039
+ }
1040
+ function isUnsupportedDirectorySync(error) {
1041
+ if (!(error instanceof Error) || !("code" in error))
1042
+ return false;
1043
+ const code = error.code;
1044
+ return code === "EINVAL"
1045
+ || code === "ENOTSUP"
1046
+ || code === "EISDIR"
1047
+ || (code === "EPERM" && process.platform === "win32");
1048
+ }
1049
+ async function syncDirectory(path) {
1050
+ let handle;
1051
+ try {
1052
+ handle = await open(path, "r");
1053
+ await handle.sync();
1054
+ }
1055
+ catch (error) {
1056
+ if (!isUnsupportedDirectorySync(error))
1057
+ throw error;
1058
+ }
1059
+ finally {
1060
+ await handle?.close().catch(() => undefined);
1061
+ }
1062
+ }
1063
+ /**
1064
+ * Independent immutable blob store for v2 canonical results.
1065
+ *
1066
+ * Publishing preserves bytes and returns a reference; it does not grant ledger
1067
+ * authority. The run store/service must separately commit that reference.
1068
+ */
1069
+ export class ImmutableCanonicalResultStore {
1070
+ rootDirectory;
1071
+ constructor(options) {
1072
+ if (!options.root_directory.trim()) {
1073
+ throw new ResultStoreError("invalid_options", "root_directory must not be empty");
1074
+ }
1075
+ this.rootDirectory = resolve(options.root_directory);
1076
+ }
1077
+ resultsDirectory() {
1078
+ return join(this.rootDirectory, "results");
1079
+ }
1080
+ resultPath(resultId) {
1081
+ return join(this.resultsDirectory(), resultFileName(resultId));
1082
+ }
1083
+ async readByResultId(resultId) {
1084
+ const path = this.resultPath(resultId);
1085
+ let metadata;
1086
+ try {
1087
+ metadata = await lstat(path);
1088
+ }
1089
+ catch (error) {
1090
+ if (isErrno(error, "ENOENT"))
1091
+ return null;
1092
+ throw error;
1093
+ }
1094
+ if (!metadata.isFile() || metadata.isSymbolicLink()) {
1095
+ throw new ResultStoreError("corrupt_result", `stored result ${resultId} is not a regular file`);
1096
+ }
1097
+ if ((metadata.mode & 0o222) !== 0) {
1098
+ throw new ResultStoreError("corrupt_result", `stored result ${resultId} must be read-only`);
1099
+ }
1100
+ let bytes;
1101
+ try {
1102
+ bytes = await readFile(path);
1103
+ }
1104
+ catch (error) {
1105
+ if (isErrno(error, "ENOENT"))
1106
+ return null;
1107
+ throw error;
1108
+ }
1109
+ let value;
1110
+ try {
1111
+ value = JSON.parse(bytes.toString("utf8"));
1112
+ assertCanonicalCommittedResult(value);
1113
+ }
1114
+ catch (error) {
1115
+ throw new ResultStoreError("corrupt_result", `stored result ${resultId} failed schema validation`, { cause: error });
1116
+ }
1117
+ const canonicalBytes = Buffer.from(canonicalizeJson(value), "utf8");
1118
+ if (!bytes.equals(canonicalBytes)) {
1119
+ throw new ResultStoreError("corrupt_result", `stored result ${resultId} is not encoded as canonical JSON`);
1120
+ }
1121
+ if (value.result_id !== resultId) {
1122
+ throw new ResultStoreError("corrupt_result", `stored result identity ${value.result_id} does not match ${resultId}`);
1123
+ }
1124
+ return { result: value, bytes };
1125
+ }
1126
+ async publish(result) {
1127
+ assertCanonicalCommittedResult(result);
1128
+ const bytes = Buffer.from(canonicalizeJson(result), "utf8");
1129
+ const reference = createCanonicalResultRef(result, canonicalResultDigest(bytes));
1130
+ await ensurePrivateDirectory(this.rootDirectory);
1131
+ await ensurePrivateDirectory(this.resultsDirectory());
1132
+ const destination = this.resultPath(result.result_id);
1133
+ const temporaryPath = join(this.resultsDirectory(), `.${resultFileName(result.result_id)}.tmp.${process.pid}.${randomUUID()}`);
1134
+ let handle;
1135
+ try {
1136
+ handle = await open(temporaryPath, "wx", TEMPORARY_FILE_MODE);
1137
+ await handle.writeFile(bytes);
1138
+ await handle.chmod(STORED_FILE_MODE);
1139
+ await handle.sync();
1140
+ await handle.close();
1141
+ handle = undefined;
1142
+ while (true) {
1143
+ try {
1144
+ await link(temporaryPath, destination);
1145
+ await syncDirectory(this.resultsDirectory());
1146
+ return { result, reference, published: true };
1147
+ }
1148
+ catch (error) {
1149
+ if (!isErrno(error, "EEXIST"))
1150
+ throw error;
1151
+ await syncDirectory(this.resultsDirectory());
1152
+ const existing = await this.readByResultId(result.result_id);
1153
+ if (!existing)
1154
+ continue;
1155
+ if (!existing.bytes.equals(bytes)) {
1156
+ throw new ResultStoreError("immutable_conflict", `result ${result.result_id} is already published with different bytes`);
1157
+ }
1158
+ return { result: existing.result, reference, published: false };
1159
+ }
1160
+ }
1161
+ }
1162
+ finally {
1163
+ await handle?.close().catch(() => undefined);
1164
+ await unlink(temporaryPath).catch((error) => {
1165
+ if (!isErrno(error, "ENOENT"))
1166
+ throw error;
1167
+ });
1168
+ }
1169
+ }
1170
+ async loadPublished(resultId) {
1171
+ assertCanonicalNonEmptyString(resultId, "result_id");
1172
+ if (!resultId.trim()) {
1173
+ throw new ResultStoreError("invalid_result", "result_id must be a non-empty string");
1174
+ }
1175
+ const stored = await this.readByResultId(resultId);
1176
+ if (!stored)
1177
+ return null;
1178
+ return {
1179
+ result: stored.result,
1180
+ reference: createCanonicalResultRef(stored.result, canonicalResultDigest(stored.bytes)),
1181
+ };
1182
+ }
1183
+ async read(reference) {
1184
+ assertCanonicalResultRef(reference);
1185
+ const stored = await this.readByResultId(reference.result_id);
1186
+ if (!stored)
1187
+ return null;
1188
+ const actualReference = createCanonicalResultRef(stored.result, canonicalResultDigest(stored.bytes));
1189
+ if (canonicalizeJson(actualReference) !== canonicalizeJson(reference)) {
1190
+ throw new ResultStoreError("corrupt_result", `stored result ${reference.result_id} does not match its reference`);
1191
+ }
1192
+ return stored.result;
1193
+ }
1194
+ async require(reference) {
1195
+ const result = await this.read(reference);
1196
+ if (!result) {
1197
+ throw new ResultStoreError("result_not_found", `canonical result ${reference.result_id} was not found`);
1198
+ }
1199
+ return result;
1200
+ }
1201
+ }