cool-workflow 0.1.97 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +12 -5
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +4 -0
  139. package/docs/cli-mcp-parity.7.md +284 -211
  140. package/docs/contract-migration-tooling.7.md +4 -0
  141. package/docs/control-plane-scheduling.7.md +4 -0
  142. package/docs/cross-agent-ledger.7.md +217 -0
  143. package/docs/designs/handoff-ledger.md +145 -0
  144. package/docs/durable-state-and-locking.7.md +4 -0
  145. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  146. package/docs/execution-backends.7.md +4 -0
  147. package/docs/handoff-setup.md +120 -0
  148. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  149. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  150. package/docs/multi-agent-operator-ux.7.md +4 -0
  151. package/docs/node-snapshot-diff-replay.7.md +4 -0
  152. package/docs/observability-cost-accounting.7.md +4 -0
  153. package/docs/project-index.md +143 -71
  154. package/docs/real-execution-backends.7.md +4 -0
  155. package/docs/release-and-migration.7.md +4 -0
  156. package/docs/release-tooling.7.md +4 -0
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/state-explosion-management.7.md +4 -0
  160. package/docs/team-collaboration.7.md +4 -0
  161. package/docs/web-desktop-workbench.7.md +4 -0
  162. package/manifest/plugin.manifest.json +1 -1
  163. package/manifest/source-context-profiles.json +9 -13
  164. package/package.json +1 -1
  165. package/scripts/agents/codex-agent.js +34 -4
  166. package/scripts/agents/cw-attest-wrap.js +2 -2
  167. package/scripts/bump-version.js +4 -3
  168. package/scripts/canonical-apps.js +4 -4
  169. package/scripts/children/batch-delegate-child.js +30 -10
  170. package/scripts/dogfood-architecture-review.js +2 -3
  171. package/scripts/dogfood-release.js +3 -3
  172. package/scripts/gen-parity-doc.js +15 -2
  173. package/scripts/golden-path.js +4 -4
  174. package/scripts/onramp-check.js +1 -1
  175. package/scripts/parity-check.js +38 -21
  176. package/scripts/release-flow.js +9 -3
  177. package/scripts/sync-project-index.js +51 -27
  178. package/scripts/validate-run-state-schema.js +2 -2
  179. package/scripts/version-sync-check.js +30 -30
  180. package/dist/candidate-scoring.js +0 -729
  181. package/dist/capability-core.js +0 -1186
  182. package/dist/capability-registry.js +0 -879
  183. package/dist/cli/command-surface.js +0 -490
  184. package/dist/cli/format.js +0 -56
  185. package/dist/cli/handlers/audit.js +0 -82
  186. package/dist/cli/handlers/blackboard.js +0 -81
  187. package/dist/cli/handlers/candidate.js +0 -40
  188. package/dist/cli/handlers/clones.js +0 -34
  189. package/dist/cli/handlers/collaboration.js +0 -61
  190. package/dist/cli/handlers/eval.js +0 -40
  191. package/dist/cli/handlers/maintenance.js +0 -107
  192. package/dist/cli/handlers/multi-agent.js +0 -165
  193. package/dist/cli/handlers/node.js +0 -41
  194. package/dist/cli/handlers/operational.js +0 -155
  195. package/dist/cli/handlers/operator.js +0 -146
  196. package/dist/cli/handlers/registry.js +0 -68
  197. package/dist/cli/handlers/run.js +0 -153
  198. package/dist/cli/handlers/scheduling.js +0 -132
  199. package/dist/cli/handlers/workbench.js +0 -41
  200. package/dist/cli/handlers/worker.js +0 -45
  201. package/dist/cli/run-summary.js +0 -45
  202. package/dist/clones.js +0 -162
  203. package/dist/collaboration.js +0 -726
  204. package/dist/commit.js +0 -592
  205. package/dist/compare.js +0 -18
  206. package/dist/coordinator/classify.js +0 -45
  207. package/dist/coordinator/paths.js +0 -42
  208. package/dist/coordinator/util.js +0 -126
  209. package/dist/coordinator.js +0 -990
  210. package/dist/daemon.js +0 -44
  211. package/dist/dispatch.js +0 -250
  212. package/dist/drive.js +0 -827
  213. package/dist/error-feedback.js +0 -419
  214. package/dist/evidence-grounding.js +0 -184
  215. package/dist/execution-backend/agent.js +0 -294
  216. package/dist/execution-backend/probes.js +0 -112
  217. package/dist/execution-backend/util.js +0 -47
  218. package/dist/execution-backend.js +0 -961
  219. package/dist/gates.js +0 -48
  220. package/dist/harness.js +0 -61
  221. package/dist/loop-expansion.js +0 -60
  222. package/dist/mcp/tool-call.js +0 -434
  223. package/dist/mcp/tool-definitions.js +0 -1040
  224. package/dist/mcp-surface.js +0 -30
  225. package/dist/multi-agent/graph.js +0 -84
  226. package/dist/multi-agent/helpers.js +0 -141
  227. package/dist/multi-agent/ids.js +0 -20
  228. package/dist/multi-agent/paths.js +0 -22
  229. package/dist/multi-agent-eval/normalize.js +0 -51
  230. package/dist/multi-agent-eval.js +0 -678
  231. package/dist/multi-agent-host.js +0 -777
  232. package/dist/multi-agent.js +0 -984
  233. package/dist/node-projection.js +0 -59
  234. package/dist/node-snapshot.js +0 -260
  235. package/dist/operator-ux.js +0 -631
  236. package/dist/orchestrator/app-operations.js +0 -211
  237. package/dist/orchestrator/audit-operations.js +0 -182
  238. package/dist/orchestrator/candidate-operations.js +0 -117
  239. package/dist/orchestrator/cli-options.js +0 -294
  240. package/dist/orchestrator/collaboration-operations.js +0 -86
  241. package/dist/orchestrator/feedback-operations.js +0 -81
  242. package/dist/orchestrator/host-operations.js +0 -78
  243. package/dist/orchestrator/lifecycle-operations.js +0 -626
  244. package/dist/orchestrator/migration-operations.js +0 -44
  245. package/dist/orchestrator/multi-agent-operations.js +0 -362
  246. package/dist/orchestrator/topology-operations.js +0 -84
  247. package/dist/orchestrator.js +0 -917
  248. package/dist/pipeline-runner.js +0 -285
  249. package/dist/reclamation/hash.js +0 -72
  250. package/dist/reclamation.js +0 -812
  251. package/dist/reporter.js +0 -67
  252. package/dist/run-export.js +0 -784
  253. package/dist/run-registry/derive.js +0 -175
  254. package/dist/run-registry/format.js +0 -124
  255. package/dist/run-registry/gc.js +0 -251
  256. package/dist/run-registry/policy.js +0 -16
  257. package/dist/run-registry/queue.js +0 -115
  258. package/dist/run-registry.js +0 -850
  259. package/dist/run-state-schema.js +0 -68
  260. package/dist/scheduling.js +0 -184
  261. package/dist/state-explosion.js +0 -1014
  262. package/dist/state.js +0 -367
  263. package/dist/telemetry-ledger.js +0 -196
  264. package/dist/topology.js +0 -565
  265. package/dist/triggers.js +0 -184
  266. package/dist/trust-audit.js +0 -644
  267. package/dist/types/blackboard.js +0 -2
  268. package/dist/types/candidate.js +0 -2
  269. package/dist/types/collaboration.js +0 -2
  270. package/dist/types/core.js +0 -2
  271. package/dist/types/drive.js +0 -10
  272. package/dist/types/error-feedback.js +0 -2
  273. package/dist/types/evidence-reasoning.js +0 -2
  274. package/dist/types/execution-backend.js +0 -2
  275. package/dist/types/multi-agent.js +0 -2
  276. package/dist/types/observability.js +0 -2
  277. package/dist/types/pipeline.js +0 -2
  278. package/dist/types/reclamation.js +0 -8
  279. package/dist/types/report-bundle.js +0 -6
  280. package/dist/types/result.js +0 -2
  281. package/dist/types/run-registry.js +0 -2
  282. package/dist/types/run.js +0 -2
  283. package/dist/types/sandbox.js +0 -2
  284. package/dist/types/schedule.js +0 -2
  285. package/dist/types/state-node.js +0 -2
  286. package/dist/types/topology.js +0 -2
  287. package/dist/types/trust.js +0 -2
  288. package/dist/types/workbench.js +0 -2
  289. package/dist/types/worker.js +0 -2
  290. package/dist/types/workflow-app.js +0 -2
  291. package/dist/types.js +0 -44
  292. package/dist/util/fingerprint.js +0 -19
  293. package/dist/util/fingerprint.test.js +0 -27
  294. package/dist/verifier.js +0 -78
  295. package/dist/version.js +0 -8
  296. package/dist/workbench-host.js +0 -182
  297. package/dist/workbench.js +0 -192
  298. package/dist/worker-accept/acceptance.js +0 -114
  299. package/dist/worker-accept/blackboard-fanout.js +0 -80
  300. package/dist/worker-accept/blackboard-linkage.js +0 -19
  301. package/dist/worker-accept/context.js +0 -2
  302. package/dist/worker-accept/telemetry-ledger.js +0 -126
  303. package/dist/worker-accept/validation.js +0 -77
  304. package/dist/worker-accept/verifier-completion.js +0 -73
  305. package/dist/worker-isolation/helpers.js +0 -51
  306. package/dist/worker-isolation/paths.js +0 -46
  307. package/dist/worker-isolation.js +0 -656
  308. package/dist/workflow-api.js +0 -131
  309. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,680 @@
1
+ "use strict";
2
+ // shell/run-export.ts — portable run archive format: exportRun,
3
+ // importRun, inspectArchive, verifyImportedRun, verifyReportBundle.
4
+ //
5
+ // MILESTONE 8. Byte-exact port of the old build's src/run-export.ts.
6
+ // Impure (fs, tmpdir, os) by nature — the archive/bundle mechanism.
7
+ //
8
+ // Evidence: SPEC/ledger-trust.md "`cw report verify-bundle` JSON", "Files
9
+ // on disk" (bundle shape), invariants 16-17, rebuild risk 8;
10
+ // plugins/cool-workflow/src/run-export.ts:1-925 (byte-exact source).
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.exportRun = exportRun;
46
+ exports.importRun = importRun;
47
+ exports.verifyImportedRun = verifyImportedRun;
48
+ exports.inspectArchive = inspectArchive;
49
+ exports.importManifestPath = importManifestPath;
50
+ exports.verifyReportBundle = verifyReportBundle;
51
+ const fs = __importStar(require("node:fs"));
52
+ const os = __importStar(require("node:os"));
53
+ const path = __importStar(require("node:path"));
54
+ const hash_1 = require("../core/hash");
55
+ const version_1 = require("../core/version");
56
+ const fs_atomic_1 = require("./fs-atomic");
57
+ const run_paths_1 = require("../core/state/run-paths");
58
+ const run_store_1 = require("./run-store");
59
+ const telemetry_ledger_io_1 = require("./telemetry-ledger-io");
60
+ const telemetry_attestation_1 = require("../core/trust/telemetry-attestation");
61
+ const trust_audit_1 = require("./trust-audit");
62
+ /** Export a run to a portable JSON archive with run-local bytes and
63
+ * digests. */
64
+ function exportRun(run, outputPath, options = {}) {
65
+ const exportedAt = new Date().toISOString();
66
+ const files = collectArchiveFiles(run);
67
+ const manifestSha256 = digestManifest(files);
68
+ const trustPublicKeyPem = (0, telemetry_attestation_1.resolveTrustPublicKey)(options.trustPublicKey);
69
+ const exported = {
70
+ schemaVersion: 1,
71
+ exportedAt,
72
+ sourceVersion: version_1.CURRENT_COOL_WORKFLOW_VERSION,
73
+ run,
74
+ files,
75
+ integrity: { fileCount: files.length, manifestSha256 },
76
+ ...(trustPublicKeyPem ? { trust: { publicKeyPem: trustPublicKeyPem, algorithm: "ed25519" } } : {}),
77
+ artifacts: files
78
+ .filter((file) => file.role === "artifact")
79
+ .map((file) => ({ path: file.relativePath, contentBase64: file.contentBase64, sha256: file.sha256, sizeBytes: file.sizeBytes })),
80
+ audit: files.filter((file) => file.role === "audit").map((file) => file.relativePath),
81
+ };
82
+ (0, fs_atomic_1.writeJson)(outputPath, exported);
83
+ const archiveSha256 = (0, hash_1.sha256Bytes)(fs.readFileSync(outputPath));
84
+ return {
85
+ runId: run.id,
86
+ exportedAt,
87
+ path: outputPath,
88
+ taskCount: run.tasks.length,
89
+ commitCount: run.commits.length,
90
+ fileCount: files.length,
91
+ artifactCount: files.filter((file) => file.role === "artifact").length,
92
+ auditFileCount: files.filter((file) => file.role === "audit").length,
93
+ telemetryIncluded: files.some((file) => file.role === "telemetry"),
94
+ trustKeyEmbedded: Boolean(trustPublicKeyPem),
95
+ manifestSha256,
96
+ archiveSha256,
97
+ };
98
+ }
99
+ /** Import a run from a portable JSON file into a target directory. */
100
+ function importRun(exportPath, targetDir) {
101
+ const raw = (0, fs_atomic_1.readJson)(exportPath);
102
+ if (raw.schemaVersion !== 1)
103
+ throw new Error(`Unsupported export schema version: ${raw.schemaVersion}`);
104
+ const archiveSha256 = (0, hash_1.sha256Bytes)(fs.readFileSync(exportPath));
105
+ const files = normalizeArchiveFiles(raw);
106
+ verifyArchiveFileDigests(files, raw.integrity);
107
+ if (!raw.run || typeof raw.run !== "object") {
108
+ throw new Error("Invalid run export: missing run object");
109
+ }
110
+ const runId = (0, fs_atomic_1.assertSafeRunId)(raw.run.id);
111
+ const runsRoot = path.join(targetDir, ".cw", "runs");
112
+ const runDir = path.join(runsRoot, runId);
113
+ if (!(0, fs_atomic_1.isContainedPath)(runDir, runsRoot)) {
114
+ throw new Error(`Run id escapes the runs directory: ${JSON.stringify(raw.run.id)}`);
115
+ }
116
+ const oldRunDir = raw.run.paths.runDir;
117
+ const oldCwd = raw.run.cwd;
118
+ const paths = (0, run_paths_1.createRunPaths)(runDir);
119
+ (0, run_paths_1.ensureRunDirs)(paths);
120
+ for (const file of files) {
121
+ const destination = path.join(runDir, file.relativePath);
122
+ if (!(0, fs_atomic_1.isContainedPath)(destination, runDir)) {
123
+ throw new Error(`Archive file escapes restore directory: ${file.relativePath}`);
124
+ }
125
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
126
+ fs.writeFileSync(destination, decodeBase64Strict(file.contentBase64, file.relativePath));
127
+ }
128
+ const externalPathMap = new Map();
129
+ for (const file of files) {
130
+ if (file.sourcePath)
131
+ externalPathMap.set(file.sourcePath, path.join(runDir, file.relativePath));
132
+ }
133
+ const run = rebaseRun(raw.run, { oldRunDir, newRunDir: runDir, oldCwd, newCwd: targetDir, paths, externalPathMap });
134
+ (0, run_store_1.saveCheckpoint)(run);
135
+ const manifest = {
136
+ schemaVersion: 1,
137
+ runId: run.id,
138
+ importedAt: new Date().toISOString(),
139
+ sourceVersion: raw.sourceVersion,
140
+ archiveSha256,
141
+ manifestSha256: digestManifest(files),
142
+ files: files.map(({ contentBase64: _contentBase64, ...file }) => file),
143
+ };
144
+ const manifestPath = importManifestPath(run);
145
+ (0, fs_atomic_1.writeJson)(manifestPath, manifest, { durable: true });
146
+ const verification = verifyImportedRun(run);
147
+ return {
148
+ run,
149
+ runDir,
150
+ statePath: paths.state,
151
+ manifestPath,
152
+ verifyCommand: `cw run verify-import ${run.id} --cwd ${targetDir} --json`,
153
+ verification,
154
+ };
155
+ }
156
+ /** Verify an imported run against its restore manifest and telemetry
157
+ * chain. */
158
+ function verifyImportedRun(run) {
159
+ const manifestPath = importManifestPath(run);
160
+ const checks = [];
161
+ if (!fs.existsSync(manifestPath)) {
162
+ return {
163
+ runId: run.id,
164
+ ok: false,
165
+ manifestPath,
166
+ checkedFiles: 0,
167
+ checks: [{ name: "import-manifest", pass: false, code: "missing-import-manifest", path: manifestPath }],
168
+ };
169
+ }
170
+ let manifest;
171
+ try {
172
+ manifest = (0, fs_atomic_1.readJson)(manifestPath);
173
+ }
174
+ catch (error) {
175
+ return {
176
+ runId: run.id,
177
+ ok: false,
178
+ manifestPath,
179
+ checkedFiles: 0,
180
+ checks: [{ name: "import-manifest", pass: false, code: "invalid-import-manifest", path: manifestPath, actual: messageOf(error) }],
181
+ };
182
+ }
183
+ const currentManifestDigest = digestManifest(manifest.files.map((file) => ({ ...file, contentBase64: "" })));
184
+ checks.push({
185
+ name: "import-manifest",
186
+ pass: manifest.runId === run.id && manifest.manifestSha256 === currentManifestDigest,
187
+ code: manifest.runId !== run.id ? "run-id-mismatch" : manifest.manifestSha256 === currentManifestDigest ? undefined : "manifest-digest-mismatch",
188
+ expected: manifest.manifestSha256,
189
+ actual: currentManifestDigest,
190
+ });
191
+ let filesOk = true;
192
+ for (const file of manifest.files) {
193
+ const restoredPath = path.join(run.paths.runDir, file.relativePath);
194
+ if (!(0, fs_atomic_1.isContainedPath)(restoredPath, run.paths.runDir)) {
195
+ filesOk = false;
196
+ checks.push({ name: "archive-file", pass: false, code: "path-escape", path: file.relativePath });
197
+ continue;
198
+ }
199
+ if (!fs.existsSync(restoredPath)) {
200
+ filesOk = false;
201
+ checks.push({ name: "archive-file", pass: false, code: "missing-file", path: file.relativePath, expected: file.sha256 });
202
+ continue;
203
+ }
204
+ const actual = (0, hash_1.sha256Bytes)(fs.readFileSync(restoredPath));
205
+ const pass = actual === file.sha256;
206
+ if (!pass)
207
+ filesOk = false;
208
+ checks.push({ name: "archive-file", pass, code: pass ? undefined : "digest-mismatch", path: file.relativePath, expected: file.sha256, actual });
209
+ }
210
+ checks.push({ name: "archive-files", pass: filesOk, code: filesOk ? undefined : "archive-files-invalid" });
211
+ const telemetry = (0, telemetry_ledger_io_1.verifyTelemetryLedger)(run);
212
+ checks.push({ name: "telemetry-ledger", pass: telemetry.verified, code: telemetry.verified ? undefined : "telemetry-ledger-invalid" });
213
+ const audit = (0, trust_audit_1.verifyTrustAudit)(run);
214
+ checks.push({ name: "trust-audit", pass: audit.verified, code: audit.verified ? undefined : "trust-audit-invalid" });
215
+ return { runId: run.id, ok: checks.every((check) => check.pass), manifestPath, checkedFiles: manifest.files.length, checks };
216
+ }
217
+ /** Read-only integrity inspection of a portable archive WITHOUT
218
+ * importing it. Never throws. */
219
+ function inspectArchive(archivePath) {
220
+ const base = {
221
+ schemaVersion: 1,
222
+ archivePath,
223
+ ok: false,
224
+ schemaSupported: false,
225
+ runId: null,
226
+ fileCount: 0,
227
+ manifestSha256: null,
228
+ archiveSha256: null,
229
+ checks: [],
230
+ };
231
+ let bytes;
232
+ try {
233
+ bytes = fs.readFileSync(archivePath);
234
+ }
235
+ catch (error) {
236
+ return { ...base, checks: [{ name: "archive", pass: false, code: "archive-unreadable", path: archivePath, actual: messageOf(error) }] };
237
+ }
238
+ base.archiveSha256 = (0, hash_1.sha256Bytes)(bytes);
239
+ let raw;
240
+ try {
241
+ raw = JSON.parse(bytes.toString("utf8"));
242
+ }
243
+ catch (error) {
244
+ return { ...base, checks: [{ name: "archive", pass: false, code: "archive-invalid-json", path: archivePath, actual: messageOf(error) }] };
245
+ }
246
+ if (raw.schemaVersion !== 1) {
247
+ return {
248
+ ...base,
249
+ schemaVersion: typeof raw.schemaVersion === "number" ? raw.schemaVersion : base.schemaVersion,
250
+ checks: [{ name: "schema", pass: false, code: "unsupported-schema", expected: "1", actual: String(raw.schemaVersion) }],
251
+ };
252
+ }
253
+ try {
254
+ const files = normalizeArchiveFiles(raw);
255
+ const { checks } = collectArchiveDigestChecks(files, raw.integrity);
256
+ if (!raw.integrity && /^(1|true|yes|on)$/i.test(process.env.CW_REQUIRE_ARCHIVE_INTEGRITY || "")) {
257
+ checks.push({ name: "archive-integrity", pass: false, code: "archive-integrity-required" });
258
+ }
259
+ return {
260
+ schemaVersion: 1,
261
+ archivePath,
262
+ ok: checks.every((c) => c.pass),
263
+ schemaSupported: true,
264
+ runId: raw.run && raw.run.id ? raw.run.id : null,
265
+ fileCount: files.length,
266
+ manifestSha256: raw.integrity ? digestManifest(files) : null,
267
+ archiveSha256: base.archiveSha256,
268
+ checks,
269
+ };
270
+ }
271
+ catch (error) {
272
+ return { ...base, schemaSupported: true, checks: [{ name: "archive", pass: false, code: "archive-malformed", path: archivePath, actual: messageOf(error) }] };
273
+ }
274
+ }
275
+ function importManifestPath(run) {
276
+ return path.join(run.paths.runDir, "import-manifest.json");
277
+ }
278
+ /** True when report.md embeds `expected` (the trimmed result) at the
279
+ * task's OWN section: a `### <taskId>` heading, a `Result: <path>`
280
+ * line, then the result body — and the body STARTS WITH `expected`. */
281
+ function reportSectionEmbedsResult(reportMd, taskId, expected) {
282
+ const needle = `### ${taskId}\n`;
283
+ for (let from = reportMd.indexOf(needle); from >= 0; from = reportMd.indexOf(needle, from + 1)) {
284
+ const after = reportMd.slice(from);
285
+ const prefix = after.match(/^### [^\n]*\n\nResult: [^\n]*\n\n/);
286
+ if (prefix && after.slice(prefix[0].length).startsWith(expected))
287
+ return true;
288
+ }
289
+ return false;
290
+ }
291
+ /** Verify a portable run bundle OFFLINE and SELF-CONTAINED: prove the
292
+ * archive bytes, the telemetry hash chain, the trust-audit chain, and
293
+ * (with the bundle's embedded public key) the ed25519 signatures.
294
+ * Never throws — every failure is a structured check and a false `ok`.
295
+ *
296
+ * Key precedence is bundle > argument > environment. */
297
+ function verifyReportBundle(archivePath, options = {}) {
298
+ const inspect = inspectArchive(archivePath);
299
+ const failedChecks = inspect.checks.filter((check) => !check.pass).map((check) => ({ name: check.name, code: check.code }));
300
+ const base = {
301
+ schemaVersion: 1,
302
+ archivePath,
303
+ runId: inspect.runId,
304
+ ok: false,
305
+ archiveOk: inspect.ok,
306
+ telemetryVerified: false,
307
+ trustAuditVerified: false,
308
+ trustKeySource: "none",
309
+ signatureKeyProvided: false,
310
+ signaturesChecked: 0,
311
+ signaturesReverified: 0,
312
+ signaturesFailed: 0,
313
+ trustLevel: "unsigned",
314
+ reportFindingsVerified: false,
315
+ failedChecks,
316
+ };
317
+ if (!inspect.schemaSupported)
318
+ return base;
319
+ let bundleKey;
320
+ let reportContent;
321
+ try {
322
+ const raw = JSON.parse(fs.readFileSync(archivePath, "utf8"));
323
+ bundleKey = raw.trust?.publicKeyPem;
324
+ if (options.extractReportTo) {
325
+ const reportFile = (raw.files || []).find((file) => file.relativePath === "report.md");
326
+ if (reportFile) {
327
+ const decoded = decodeBase64StrictResult(reportFile.contentBase64, reportFile.relativePath);
328
+ if (decoded.ok)
329
+ reportContent = decoded.bytes.toString("utf8");
330
+ }
331
+ }
332
+ }
333
+ catch {
334
+ /* inspect already recorded the parse failure; treat key as absent */
335
+ }
336
+ const trustKeySource = bundleKey ? "bundle" : options.pubkey ? "argument" : process.env.CW_AGENT_ATTEST_PUBKEY ? "environment" : "none";
337
+ const trustKey = (0, telemetry_attestation_1.resolveTrustPublicKey)(bundleKey || options.pubkey || process.env.CW_AGENT_ATTEST_PUBKEY);
338
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "cw-verify-bundle-"));
339
+ let telemetryVerified = false;
340
+ let trustAuditVerified = false;
341
+ let signaturesChecked = 0;
342
+ let signaturesReverified = 0;
343
+ let signaturesFailed = 0;
344
+ let signaturesResultBound = 0;
345
+ let reportFindingsOk = true;
346
+ let reportExtractedTo;
347
+ try {
348
+ const imported = importRun(archivePath, tmpDir);
349
+ for (const check of imported.verification.checks) {
350
+ if (check.name === "telemetry-ledger")
351
+ telemetryVerified = check.pass;
352
+ if (check.name === "trust-audit")
353
+ trustAuditVerified = check.pass;
354
+ if (!check.pass)
355
+ failedChecks.push({ name: check.name, code: check.code });
356
+ }
357
+ const ledger = (0, telemetry_ledger_io_1.verifyTelemetryLedger)(imported.run);
358
+ const sig = (0, telemetry_attestation_1.verifyTelemetrySignatures)(ledger.records, trustKey);
359
+ signaturesChecked = sig.checked;
360
+ signaturesReverified = sig.reverified;
361
+ signaturesFailed = sig.failed;
362
+ signaturesResultBound = sig.resultBound.length;
363
+ for (const check of sig.checks)
364
+ if (!check.pass)
365
+ failedChecks.push({ name: check.name, code: check.code });
366
+ // Report ⇄ result ⇄ signature cross-check — driven by sig.resultBound
367
+ // (records whose signature actually COVERED the result), NEVER
368
+ // run.tasks (unbound data an attacker can edit).
369
+ const reportPath = imported.run.paths.report;
370
+ const reportMd = reportPath && fs.existsSync(reportPath) ? fs.readFileSync(reportPath, "utf8") : "";
371
+ const completedById = new Map(imported.run.tasks.filter((task) => task.status === "completed").map((task) => [task.id, task]));
372
+ for (const bound of sig.resultBound) {
373
+ const failBound = (code) => {
374
+ reportFindingsOk = false;
375
+ failedChecks.push({ name: "report-findings", code: `${code}:${bound.taskId}` });
376
+ };
377
+ const task = completedById.get(bound.taskId);
378
+ if (!task || !task.resultPath || !fs.existsSync(task.resultPath)) {
379
+ failBound("result-missing");
380
+ continue;
381
+ }
382
+ const resultRaw = fs.readFileSync(task.resultPath, "utf8");
383
+ if ((0, hash_1.sha256)(resultRaw) !== bound.resultDigest) {
384
+ failBound("result-digest-mismatch");
385
+ continue;
386
+ }
387
+ if (!reportSectionEmbedsResult(reportMd, bound.taskId, resultRaw.trim())) {
388
+ failBound("report-result-mismatch");
389
+ }
390
+ }
391
+ if (options.extractReportTo && reportContent !== undefined) {
392
+ reportExtractedTo = path.resolve(options.extractReportTo);
393
+ if (options.cwd) {
394
+ const baseCwd = path.resolve(options.cwd);
395
+ if (!(0, fs_atomic_1.isContainedPath)(reportExtractedTo, baseCwd)) {
396
+ failedChecks.push({ name: "extract-report", code: "path-outside-working-directory" });
397
+ reportExtractedTo = undefined;
398
+ }
399
+ }
400
+ if (reportExtractedTo)
401
+ fs.writeFileSync(reportExtractedTo, reportContent);
402
+ }
403
+ }
404
+ catch (error) {
405
+ failedChecks.push({ name: "restore", code: messageOf(error) });
406
+ }
407
+ finally {
408
+ fs.rmSync(tmpDir, { recursive: true, force: true });
409
+ }
410
+ const strictShortfall = Boolean(options.strictSignatures) && signaturesChecked > 0 && !trustKey;
411
+ if (strictShortfall)
412
+ failedChecks.push({ name: "signatures", code: "signature-key-required" });
413
+ const trustLevel = signaturesResultBound > 0 && signaturesFailed === 0 && reportFindingsOk ? "signed" : "unsigned";
414
+ const unsignedShortfall = Boolean(options.requireSignatures) && trustLevel === "unsigned";
415
+ if (unsignedShortfall)
416
+ failedChecks.push({ name: "signatures", code: "signatures-required" });
417
+ const extractShortfall = Boolean(options.extractReportTo) && !reportExtractedTo;
418
+ if (extractShortfall)
419
+ failedChecks.push({ name: "extract-report", code: "report-md-unavailable" });
420
+ return {
421
+ schemaVersion: 1,
422
+ archivePath,
423
+ runId: inspect.runId,
424
+ ok: inspect.ok && telemetryVerified && trustAuditVerified && signaturesFailed === 0 && reportFindingsOk && !strictShortfall && !extractShortfall && !unsignedShortfall,
425
+ archiveOk: inspect.ok,
426
+ telemetryVerified,
427
+ trustAuditVerified,
428
+ trustKeySource,
429
+ signatureKeyProvided: Boolean(trustKey),
430
+ signaturesChecked,
431
+ signaturesReverified,
432
+ signaturesFailed,
433
+ trustLevel,
434
+ reportFindingsVerified: reportFindingsOk,
435
+ reportExtractedTo,
436
+ failedChecks,
437
+ };
438
+ }
439
+ function collectArchiveFiles(run) {
440
+ const entries = new Map();
441
+ for (const file of walkFiles(run.paths.runDir)) {
442
+ const relativePath = toArchivePath(path.relative(run.paths.runDir, file));
443
+ if (!relativePath || relativePath === "state.json" || relativePath === "import-manifest.json" || relativePath.endsWith(".lock"))
444
+ continue;
445
+ addFile(entries, file, roleForRelativePath(relativePath), run.paths.runDir);
446
+ }
447
+ for (const artifactPath of collectReferencedArtifactPaths(run)) {
448
+ if (!artifactPath || !fs.existsSync(artifactPath) || !fs.statSync(artifactPath).isFile())
449
+ continue;
450
+ if ((0, fs_atomic_1.isContainedPath)(artifactPath, run.paths.runDir)) {
451
+ addFile(entries, artifactPath, "artifact", run.paths.runDir);
452
+ continue;
453
+ }
454
+ if ((0, fs_atomic_1.isContainedPath)(artifactPath, run.cwd))
455
+ addExternalArtifactFile(entries, artifactPath);
456
+ }
457
+ return [...entries.values()].sort((left, right) => (left.relativePath < right.relativePath ? -1 : left.relativePath > right.relativePath ? 1 : 0));
458
+ }
459
+ function addFile(entries, file, role, runDir) {
460
+ const relativePath = toArchivePath(path.relative(runDir, file));
461
+ if (relativePath === "state.json" || relativePath === "import-manifest.json")
462
+ return;
463
+ if (!relativePath || relativePath.startsWith("../"))
464
+ return;
465
+ const bytes = fs.readFileSync(file);
466
+ entries.set(relativePath, { relativePath, role, contentBase64: bytes.toString("base64"), sha256: (0, hash_1.sha256Bytes)(bytes), sizeBytes: bytes.length });
467
+ }
468
+ function addExternalArtifactFile(entries, file) {
469
+ const sourcePath = path.resolve(file);
470
+ const bytes = fs.readFileSync(sourcePath);
471
+ const relativePath = `external-artifacts/${(0, hash_1.sha256Bytes)(Buffer.from(sourcePath, "utf8")).slice(0, 16)}-${safeArchiveBasename(path.basename(sourcePath))}`;
472
+ entries.set(relativePath, { relativePath, role: "artifact", contentBase64: bytes.toString("base64"), sha256: (0, hash_1.sha256Bytes)(bytes), sizeBytes: bytes.length, sourcePath });
473
+ }
474
+ function collectReferencedArtifactPaths(run) {
475
+ const paths = new Set();
476
+ for (const node of run.nodes || []) {
477
+ for (const artifact of node.artifacts || [])
478
+ addArtifactPath(paths, run, artifact.path);
479
+ }
480
+ const candidates = (run.candidates || []);
481
+ for (const candidate of candidates) {
482
+ for (const artifact of candidate.artifacts || [])
483
+ addArtifactPath(paths, run, artifact.path);
484
+ }
485
+ const selections = (run.candidateSelections || []);
486
+ for (const selection of selections) {
487
+ for (const artifact of selection.artifacts || [])
488
+ addArtifactPath(paths, run, artifact.path);
489
+ }
490
+ const bbArtifacts = (run.blackboard?.artifacts || []);
491
+ for (const artifact of bbArtifacts)
492
+ addArtifactPath(paths, run, artifact.path);
493
+ return [...paths].sort();
494
+ }
495
+ function addArtifactPath(paths, run, value) {
496
+ if (!value)
497
+ return;
498
+ paths.add(path.isAbsolute(value) ? value : path.resolve(run.cwd, value));
499
+ }
500
+ function walkFiles(root) {
501
+ if (!fs.existsSync(root))
502
+ return [];
503
+ const found = [];
504
+ for (const name of fs.readdirSync(root)) {
505
+ const file = path.join(root, name);
506
+ const stat = fs.lstatSync(file);
507
+ if (stat.isSymbolicLink())
508
+ continue;
509
+ if (stat.isDirectory())
510
+ found.push(...walkFiles(file));
511
+ else if (stat.isFile())
512
+ found.push(file);
513
+ }
514
+ return found;
515
+ }
516
+ function roleForRelativePath(relativePath) {
517
+ if (relativePath === "telemetry.json")
518
+ return "telemetry";
519
+ if (relativePath === "audit" || relativePath.startsWith("audit/"))
520
+ return "audit";
521
+ if (relativePath === "artifacts" || relativePath.startsWith("artifacts/"))
522
+ return "artifact";
523
+ return "run-file";
524
+ }
525
+ function normalizeArchiveFiles(raw) {
526
+ const modern = raw.files || [];
527
+ if (modern.length) {
528
+ return modern.map((file) => ({
529
+ relativePath: cleanArchiveRelativePath(file.relativePath),
530
+ role: file.role,
531
+ contentBase64: file.contentBase64,
532
+ sha256: file.sha256,
533
+ sizeBytes: file.sizeBytes,
534
+ sourcePath: file.sourcePath,
535
+ }));
536
+ }
537
+ return (raw.artifacts || []).map((artifact) => {
538
+ const contentBase64 = artifact.contentBase64 || Buffer.from("", "utf8").toString("base64");
539
+ const bytes = Buffer.from(contentBase64, "base64");
540
+ return {
541
+ relativePath: cleanArchiveRelativePath(artifact.path),
542
+ role: "artifact",
543
+ contentBase64,
544
+ sha256: artifact.sha256 || (0, hash_1.sha256Bytes)(bytes),
545
+ sizeBytes: artifact.sizeBytes ?? bytes.length,
546
+ };
547
+ });
548
+ }
549
+ function decodeBase64StrictResult(value, relativePath) {
550
+ if (typeof value !== "string") {
551
+ return { ok: false, check: { name: "archive-file", pass: false, code: "archive-bad-base64", path: relativePath, actual: "contentBase64 is not a string" } };
552
+ }
553
+ const compact = value.replace(/\s+/g, "");
554
+ if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(compact)) {
555
+ return { ok: false, check: { name: "archive-file", pass: false, code: "archive-bad-base64", path: relativePath, actual: "invalid base64 encoding" } };
556
+ }
557
+ const bytes = Buffer.from(compact, "base64");
558
+ const expected = compact.replace(/=+$/, "");
559
+ const actual = bytes.toString("base64").replace(/=+$/, "");
560
+ if (actual !== expected) {
561
+ return { ok: false, check: { name: "archive-file", pass: false, code: "archive-bad-base64", path: relativePath, actual: "non-canonical base64 encoding" } };
562
+ }
563
+ return { ok: true, bytes };
564
+ }
565
+ function decodeBase64Strict(value, relativePath) {
566
+ const decoded = decodeBase64StrictResult(value, relativePath);
567
+ if (!decoded.ok)
568
+ throw new Error(archiveCheckMessage(decoded.check));
569
+ return decoded.bytes;
570
+ }
571
+ function collectArchiveDigestChecks(files, integrity) {
572
+ const checks = [];
573
+ for (const file of files) {
574
+ const decoded = decodeBase64StrictResult(file.contentBase64, file.relativePath);
575
+ if (!decoded.ok) {
576
+ checks.push(decoded.check);
577
+ continue;
578
+ }
579
+ const bytes = decoded.bytes;
580
+ const actual = (0, hash_1.sha256Bytes)(bytes);
581
+ const digestOk = actual === file.sha256;
582
+ checks.push(digestOk ? { name: "archive-file", pass: true, path: file.relativePath } : { name: "archive-file", pass: false, code: "digest-mismatch", path: file.relativePath, expected: file.sha256, actual });
583
+ const sizeOk = bytes.length === file.sizeBytes;
584
+ checks.push(sizeOk ? { name: "archive-file", pass: true, path: file.relativePath } : { name: "archive-file", pass: false, code: "size-mismatch", path: file.relativePath, expected: String(file.sizeBytes), actual: String(bytes.length) });
585
+ }
586
+ if (integrity) {
587
+ const countOk = integrity.fileCount === files.length;
588
+ checks.push(countOk ? { name: "archive-file-count", pass: true } : { name: "archive-file-count", pass: false, code: "file-count-mismatch", expected: String(integrity.fileCount), actual: String(files.length) });
589
+ const actualManifest = digestManifest(files);
590
+ const manifestOk = integrity.manifestSha256 === actualManifest;
591
+ checks.push(manifestOk ? { name: "archive-manifest", pass: true } : { name: "archive-manifest", pass: false, code: "manifest-digest-mismatch", expected: integrity.manifestSha256, actual: actualManifest });
592
+ }
593
+ return { checks, ok: checks.every((c) => c.pass) };
594
+ }
595
+ function archiveCheckMessage(check) {
596
+ switch (check.code) {
597
+ case "digest-mismatch":
598
+ return `Archive digest mismatch for ${check.path}: expected ${check.expected}, got ${check.actual}`;
599
+ case "size-mismatch":
600
+ return `Archive size mismatch for ${check.path}: expected ${check.expected}, got ${check.actual}`;
601
+ case "file-count-mismatch":
602
+ return `Archive file count mismatch: expected ${check.expected}, got ${check.actual}`;
603
+ case "manifest-digest-mismatch":
604
+ return `Archive manifest digest mismatch: expected ${check.expected}, got ${check.actual}`;
605
+ case "archive-bad-base64":
606
+ return `Archive base64 invalid for ${check.path}: ${check.actual}`;
607
+ default:
608
+ return `Archive verification failed: ${check.name}`;
609
+ }
610
+ }
611
+ function verifyArchiveFileDigests(files, integrity) {
612
+ if (!integrity && /^(1|true|yes|on)$/i.test(process.env.CW_REQUIRE_ARCHIVE_INTEGRITY || "")) {
613
+ throw new Error("Archive integrity block required but absent (CW_REQUIRE_ARCHIVE_INTEGRITY=1)");
614
+ }
615
+ const failed = collectArchiveDigestChecks(files, integrity).checks.find((c) => !c.pass);
616
+ if (failed)
617
+ throw new Error(archiveCheckMessage(failed));
618
+ }
619
+ function digestManifest(files) {
620
+ const manifest = files
621
+ .map((file) => ({ relativePath: file.relativePath, role: file.role, sha256: file.sha256, sizeBytes: file.sizeBytes }))
622
+ .sort((left, right) => (left.relativePath < right.relativePath ? -1 : left.relativePath > right.relativePath ? 1 : 0));
623
+ return (0, hash_1.sha256Bytes)(Buffer.from(JSON.stringify(manifest), "utf8"));
624
+ }
625
+ function rebaseRun(source, context) {
626
+ const cloned = deepRebase(JSON.parse(JSON.stringify(source)), context);
627
+ cloned.cwd = context.newCwd;
628
+ cloned.paths = context.paths;
629
+ cloned.updatedAt = new Date().toISOString();
630
+ cloned.audit = cloned.audit
631
+ ? {
632
+ schemaVersion: 1,
633
+ eventLogPath: path.join(context.paths.auditDir || path.join(context.paths.runDir, "audit"), "events.jsonl"),
634
+ summaryPath: path.join(context.paths.auditDir || path.join(context.paths.runDir, "audit"), "summary.json"),
635
+ indexPath: path.join(context.paths.auditDir || path.join(context.paths.runDir, "audit"), "index.json"),
636
+ }
637
+ : cloned.audit;
638
+ return cloned;
639
+ }
640
+ function deepRebase(value, context) {
641
+ if (typeof value === "string")
642
+ return rebaseString(value, context);
643
+ if (Array.isArray(value))
644
+ return value.map((entry) => deepRebase(entry, context));
645
+ if (value && typeof value === "object") {
646
+ const out = {};
647
+ for (const [key, entry] of Object.entries(value))
648
+ out[key] = deepRebase(entry, context);
649
+ return out;
650
+ }
651
+ return value;
652
+ }
653
+ function rebaseString(value, context) {
654
+ const archivedExternal = context.externalPathMap?.get(value);
655
+ if (archivedExternal)
656
+ return archivedExternal;
657
+ if (value === context.oldRunDir || value.startsWith(context.oldRunDir + path.sep)) {
658
+ return context.newRunDir + value.slice(context.oldRunDir.length);
659
+ }
660
+ if (value === context.oldCwd || value.startsWith(context.oldCwd + path.sep)) {
661
+ return context.newCwd + value.slice(context.oldCwd.length);
662
+ }
663
+ return value;
664
+ }
665
+ function cleanArchiveRelativePath(value) {
666
+ const cleaned = toArchivePath(value).replace(/^\/+/, "");
667
+ if (!cleaned || cleaned === "." || cleaned.startsWith("../") || cleaned.includes("/../")) {
668
+ throw new Error(`Invalid archive relative path: ${value}`);
669
+ }
670
+ return cleaned;
671
+ }
672
+ function toArchivePath(value) {
673
+ return value.split(path.sep).join("/");
674
+ }
675
+ function safeArchiveBasename(value) {
676
+ return value.replace(/[^A-Za-z0-9._-]/g, "_") || "artifact";
677
+ }
678
+ function messageOf(error) {
679
+ return error instanceof Error ? error.message : String(error);
680
+ }