cool-workflow 0.1.98 → 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 (306) 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} +2 -2
  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 +2 -0
  139. package/docs/cli-mcp-parity.7.md +280 -219
  140. package/docs/contract-migration-tooling.7.md +2 -0
  141. package/docs/control-plane-scheduling.7.md +2 -0
  142. package/docs/durable-state-and-locking.7.md +2 -0
  143. package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
  144. package/docs/execution-backends.7.md +2 -0
  145. package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
  146. package/docs/multi-agent-eval-replay-harness.7.md +2 -0
  147. package/docs/multi-agent-operator-ux.7.md +2 -0
  148. package/docs/node-snapshot-diff-replay.7.md +2 -0
  149. package/docs/observability-cost-accounting.7.md +2 -0
  150. package/docs/project-index.md +132 -71
  151. package/docs/real-execution-backends.7.md +2 -0
  152. package/docs/release-and-migration.7.md +2 -0
  153. package/docs/release-tooling.7.md +2 -0
  154. package/docs/run-registry-control-plane.7.md +2 -0
  155. package/docs/run-retention-reclamation.7.md +23 -0
  156. package/docs/state-explosion-management.7.md +2 -0
  157. package/docs/team-collaboration.7.md +2 -0
  158. package/docs/web-desktop-workbench.7.md +2 -0
  159. package/manifest/plugin.manifest.json +1 -1
  160. package/manifest/source-context-profiles.json +9 -13
  161. package/package.json +1 -1
  162. package/scripts/agents/cw-attest-wrap.js +2 -2
  163. package/scripts/bump-version.js +4 -3
  164. package/scripts/canonical-apps.js +4 -4
  165. package/scripts/dogfood-architecture-review.js +2 -3
  166. package/scripts/dogfood-release.js +3 -3
  167. package/scripts/gen-parity-doc.js +15 -2
  168. package/scripts/golden-path.js +4 -4
  169. package/scripts/onramp-check.js +1 -1
  170. package/scripts/parity-check.js +38 -21
  171. package/scripts/release-flow.js +2 -2
  172. package/scripts/sync-project-index.js +51 -27
  173. package/scripts/validate-run-state-schema.js +2 -2
  174. package/scripts/version-sync-check.js +30 -30
  175. package/dist/candidate-scoring.js +0 -729
  176. package/dist/capability-core.js +0 -1189
  177. package/dist/capability-registry.js +0 -885
  178. package/dist/cli/command-surface.js +0 -494
  179. package/dist/cli/format.js +0 -56
  180. package/dist/cli/handlers/audit.js +0 -82
  181. package/dist/cli/handlers/blackboard.js +0 -81
  182. package/dist/cli/handlers/candidate.js +0 -40
  183. package/dist/cli/handlers/clones.js +0 -34
  184. package/dist/cli/handlers/collaboration.js +0 -61
  185. package/dist/cli/handlers/eval.js +0 -40
  186. package/dist/cli/handlers/ledger.js +0 -169
  187. package/dist/cli/handlers/maintenance.js +0 -107
  188. package/dist/cli/handlers/multi-agent.js +0 -165
  189. package/dist/cli/handlers/node.js +0 -41
  190. package/dist/cli/handlers/operational.js +0 -155
  191. package/dist/cli/handlers/operator.js +0 -146
  192. package/dist/cli/handlers/registry.js +0 -68
  193. package/dist/cli/handlers/run.js +0 -153
  194. package/dist/cli/handlers/scheduling.js +0 -132
  195. package/dist/cli/handlers/workbench.js +0 -41
  196. package/dist/cli/handlers/worker.js +0 -45
  197. package/dist/cli/run-summary.js +0 -45
  198. package/dist/clones.js +0 -162
  199. package/dist/collaboration.js +0 -726
  200. package/dist/commit.js +0 -592
  201. package/dist/compare.js +0 -18
  202. package/dist/coordinator/classify.js +0 -45
  203. package/dist/coordinator/paths.js +0 -42
  204. package/dist/coordinator/util.js +0 -126
  205. package/dist/coordinator.js +0 -990
  206. package/dist/daemon.js +0 -44
  207. package/dist/dispatch.js +0 -250
  208. package/dist/drive.js +0 -864
  209. package/dist/error-feedback.js +0 -419
  210. package/dist/evidence-grounding.js +0 -184
  211. package/dist/execution-backend/agent.js +0 -354
  212. package/dist/execution-backend/probes.js +0 -112
  213. package/dist/execution-backend/util.js +0 -47
  214. package/dist/execution-backend.js +0 -961
  215. package/dist/gates.js +0 -48
  216. package/dist/harness.js +0 -61
  217. package/dist/ledger.js +0 -313
  218. package/dist/loop-expansion.js +0 -60
  219. package/dist/mcp/tool-call.js +0 -470
  220. package/dist/mcp/tool-definitions.js +0 -1066
  221. package/dist/mcp-surface.js +0 -30
  222. package/dist/multi-agent/graph.js +0 -84
  223. package/dist/multi-agent/helpers.js +0 -141
  224. package/dist/multi-agent/ids.js +0 -20
  225. package/dist/multi-agent/paths.js +0 -22
  226. package/dist/multi-agent-eval/normalize.js +0 -51
  227. package/dist/multi-agent-eval.js +0 -678
  228. package/dist/multi-agent-host.js +0 -777
  229. package/dist/multi-agent.js +0 -984
  230. package/dist/node-projection.js +0 -59
  231. package/dist/node-snapshot.js +0 -260
  232. package/dist/operator-ux.js +0 -631
  233. package/dist/orchestrator/app-operations.js +0 -211
  234. package/dist/orchestrator/audit-operations.js +0 -182
  235. package/dist/orchestrator/candidate-operations.js +0 -117
  236. package/dist/orchestrator/cli-options.js +0 -294
  237. package/dist/orchestrator/collaboration-operations.js +0 -86
  238. package/dist/orchestrator/feedback-operations.js +0 -81
  239. package/dist/orchestrator/host-operations.js +0 -78
  240. package/dist/orchestrator/lifecycle-operations.js +0 -650
  241. package/dist/orchestrator/migration-operations.js +0 -44
  242. package/dist/orchestrator/multi-agent-operations.js +0 -362
  243. package/dist/orchestrator/topology-operations.js +0 -84
  244. package/dist/orchestrator.js +0 -925
  245. package/dist/pipeline-runner.js +0 -285
  246. package/dist/reclamation/hash.js +0 -72
  247. package/dist/reclamation.js +0 -812
  248. package/dist/reporter.js +0 -67
  249. package/dist/run-export.js +0 -815
  250. package/dist/run-registry/derive.js +0 -175
  251. package/dist/run-registry/format.js +0 -124
  252. package/dist/run-registry/gc.js +0 -251
  253. package/dist/run-registry/policy.js +0 -16
  254. package/dist/run-registry/queue.js +0 -115
  255. package/dist/run-registry.js +0 -850
  256. package/dist/run-state-schema.js +0 -68
  257. package/dist/scheduling.js +0 -184
  258. package/dist/state-explosion.js +0 -1014
  259. package/dist/state.js +0 -367
  260. package/dist/telemetry-ledger.js +0 -196
  261. package/dist/topology.js +0 -565
  262. package/dist/triggers.js +0 -184
  263. package/dist/trust-audit.js +0 -644
  264. package/dist/types/blackboard.js +0 -2
  265. package/dist/types/candidate.js +0 -2
  266. package/dist/types/collaboration.js +0 -2
  267. package/dist/types/core.js +0 -2
  268. package/dist/types/drive.js +0 -10
  269. package/dist/types/error-feedback.js +0 -2
  270. package/dist/types/evidence-reasoning.js +0 -2
  271. package/dist/types/execution-backend.js +0 -2
  272. package/dist/types/multi-agent.js +0 -2
  273. package/dist/types/observability.js +0 -2
  274. package/dist/types/pipeline.js +0 -2
  275. package/dist/types/reclamation.js +0 -8
  276. package/dist/types/report-bundle.js +0 -6
  277. package/dist/types/result.js +0 -2
  278. package/dist/types/run-registry.js +0 -2
  279. package/dist/types/run.js +0 -2
  280. package/dist/types/sandbox.js +0 -2
  281. package/dist/types/schedule.js +0 -2
  282. package/dist/types/state-node.js +0 -2
  283. package/dist/types/topology.js +0 -2
  284. package/dist/types/trust.js +0 -2
  285. package/dist/types/workbench.js +0 -2
  286. package/dist/types/worker.js +0 -2
  287. package/dist/types/workflow-app.js +0 -2
  288. package/dist/types.js +0 -44
  289. package/dist/util/fingerprint.js +0 -19
  290. package/dist/util/fingerprint.test.js +0 -27
  291. package/dist/verifier.js +0 -78
  292. package/dist/version.js +0 -8
  293. package/dist/workbench-host.js +0 -192
  294. package/dist/workbench.js +0 -192
  295. package/dist/worker-accept/acceptance.js +0 -114
  296. package/dist/worker-accept/blackboard-fanout.js +0 -80
  297. package/dist/worker-accept/blackboard-linkage.js +0 -19
  298. package/dist/worker-accept/context.js +0 -2
  299. package/dist/worker-accept/telemetry-ledger.js +0 -126
  300. package/dist/worker-accept/validation.js +0 -77
  301. package/dist/worker-accept/verifier-completion.js +0 -73
  302. package/dist/worker-isolation/helpers.js +0 -51
  303. package/dist/worker-isolation/paths.js +0 -46
  304. package/dist/worker-isolation.js +0 -656
  305. package/dist/workflow-api.js +0 -131
  306. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -1,59 +0,0 @@
1
- "use strict";
2
- // Canonical StateNode projection (F9 de-triplication) — the SINGLE source of
3
- // truth for which StateNode fields make up the derived, fingerprinted body.
4
- //
5
- // Previously this 13-field set was hand-copied in three places: node-snapshot.ts's
6
- // snapshotBody, reclamation.ts's snapshotProjectionDigest, and reclamation.ts's
7
- // rawNodeBody/nodeBodyDigest (whose comment literally said "Mirror
8
- // node-snapshot.ts"). Any field added in one place but not the others would drift
9
- // the projection — and because reclamation hashes are tombstone-chained, a drift
10
- // would silently break the chain. This module collapses the list to ONE export so
11
- // the field set can only change in one place.
12
- //
13
- // BYTE-IDENTITY [load-bearing]: the projection / digest OUTPUT here is identical to
14
- // the prior in-line implementations. `normalizeValue` sorts object keys, so the
15
- // literal field ORDER never affected the bytes — only the field SET ever mattered.
16
- // `replayStableStringify(value) === JSON.stringify(normalizeValue(value))` and
17
- // `normalizeValue` is idempotent, so the projected-body digest and the raw-body
18
- // digest funnel through the same canonical bytes (node-snapshot-diff-replay +
19
- // reclamation smokes verify this).
20
- //
21
- // Pure: no I/O, no wall-clock, no random — safe in core/state/replay logic.
22
- Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.rawNodeProjection = rawNodeProjection;
24
- exports.projectNodeBody = projectNodeBody;
25
- exports.nodeProjectionDigestInput = nodeProjectionDigestInput;
26
- const multi_agent_eval_1 = require("./multi-agent-eval");
27
- /** The raw (un-normalized) projection input: the canonical field set, copied off
28
- * the StateNode with no scrubbing. `normalizeValue`/`replayStableStringify` apply
29
- * the timestamp/path scrubbing downstream. This is the ONE place the field list
30
- * lives — add or drop a projected field here and every consumer follows. */
31
- function rawNodeProjection(node) {
32
- return {
33
- id: node.id,
34
- kind: node.kind,
35
- status: node.status,
36
- loopStage: node.loopStage,
37
- inputs: node.inputs,
38
- outputs: node.outputs,
39
- artifacts: node.artifacts,
40
- evidence: node.evidence,
41
- errors: node.errors,
42
- parents: node.parents,
43
- children: node.children,
44
- contractId: node.contractId,
45
- metadata: node.metadata
46
- };
47
- }
48
- /** The normalized, derived projected body of one StateNode — timestamps/paths
49
- * stripped via the eval normalizer, so it is byte-stable across captures of the
50
- * same logical state. This is exactly node-snapshot.ts's historical snapshotBody. */
51
- function projectNodeBody(node) {
52
- return (0, multi_agent_eval_1.normalizeValue)(rawNodeProjection(node));
53
- }
54
- /** Stable digest input for the projected body: the canonical bytes that the
55
- * snapshot/reconstruction digests bind. `replayStableStringify` re-normalizes, so
56
- * feeding the raw projection or the already-normalized body yields identical bytes. */
57
- function nodeProjectionDigestInput(node) {
58
- return (0, multi_agent_eval_1.replayStableStringify)(rawNodeProjection(node));
59
- }
@@ -1,260 +0,0 @@
1
- "use strict";
2
- // Node Snapshot / Diff / Replay (v0.1.35) — per-node granularity over the eval
3
- // harness. Snapshot one StateNode into a DERIVED, sha256-fingerprinted projection,
4
- // diff two snapshots structurally, and deterministically replay one node in
5
- // isolation.
6
- //
7
- // BSD discipline:
8
- // - MECHANISM, not policy: the caller names the node id; nothing decides which
9
- // node "matters".
10
- // - FAIL CLOSED ON SOURCE DRIFT [load-bearing]: a snapshot carries a
11
- // sourceFingerprint over the RAW node (id:status:updatedAt + artifact/evidence
12
- // ids+paths). loadNodeSnapshot recomputes it from the current source; on
13
- // divergence (`stale`) or a missing node/artifact (`absent`) diff/replay REFUSE
14
- // with a structured error — never a silent stale replay.
15
- // - REUSE, don't fork: operates on the real StateNode (getRunNode) and reuses the
16
- // eval harness's normalizeValue/replayStableStringify and state-explosion's
17
- // fingerprintStrings. No parallel node type, normalizer, or replay engine.
18
- // - DETERMINISTIC: `now` is injected; the deterministic payload (normalized body
19
- // + outputFingerprint) carries zero wall-clock, so two replays are byte-identical.
20
- //
21
- // Additive: StateNode and STATE_NODE_SCHEMA_VERSION are unchanged. See
22
- // docs/node-snapshot-diff-replay.7.md.
23
- var __importDefault = (this && this.__importDefault) || function (mod) {
24
- return (mod && mod.__esModule) ? mod : { "default": mod };
25
- };
26
- Object.defineProperty(exports, "__esModule", { value: true });
27
- exports.NodeSnapshotError = exports.NODE_SNAPSHOT_SCHEMA_VERSION = void 0;
28
- exports.readNodeSnapshot = readNodeSnapshot;
29
- exports.readNodeReplay = readNodeReplay;
30
- exports.snapshotNode = snapshotNode;
31
- exports.loadNodeSnapshot = loadNodeSnapshot;
32
- exports.diffNodeSnapshots = diffNodeSnapshots;
33
- exports.replayNodeSnapshot = replayNodeSnapshot;
34
- exports.verifyNodeReplay = verifyNodeReplay;
35
- const node_fs_1 = __importDefault(require("node:fs"));
36
- const node_path_1 = __importDefault(require("node:path"));
37
- const pipeline_runner_1 = require("./pipeline-runner");
38
- const state_1 = require("./state");
39
- const multi_agent_eval_1 = require("./multi-agent-eval");
40
- const node_projection_1 = require("./node-projection");
41
- const fingerprint_1 = require("./util/fingerprint");
42
- const validation_1 = require("./validation");
43
- exports.NODE_SNAPSHOT_SCHEMA_VERSION = 1;
44
- /** Structured fail-closed error (mirrors the PipelineContractError shape). */
45
- class NodeSnapshotError extends Error {
46
- code;
47
- freshness;
48
- details;
49
- constructor(code, message, options = {}) {
50
- super(message);
51
- this.name = "NodeSnapshotError";
52
- this.code = code;
53
- this.freshness = options.freshness;
54
- this.details = options.details;
55
- }
56
- }
57
- exports.NodeSnapshotError = NodeSnapshotError;
58
- const SNAPSHOT_SECTIONS = [
59
- "status",
60
- "inputs",
61
- "outputs",
62
- "artifacts",
63
- "evidence",
64
- "errors",
65
- "links",
66
- "metadata"
67
- ];
68
- /** The normalized projection of a node — timestamps/paths stripped by the eval
69
- * normalizer, so it is byte-stable across captures of the same logical state. The
70
- * canonical field set lives in node-projection.ts (shared with reclamation.ts so
71
- * the projection can never drift across the two). */
72
- function snapshotBody(node) {
73
- return (0, node_projection_1.projectNodeBody)(node);
74
- }
75
- /** RAW fingerprint (NOT normalized): any transition (updatedAt/status) or
76
- * artifact/evidence change flips it, which is how drift is detected. */
77
- function sourceFingerprint(node) {
78
- return (0, fingerprint_1.fingerprintStrings)([
79
- `node:${node.id}:${node.status}:${node.updatedAt}`,
80
- ...node.artifacts.map((artifact) => `artifact:${artifact.id}:${artifact.path}`),
81
- ...node.evidence.map((evidence) => `evidence:${evidence.id}:${evidence.path || ""}`)
82
- ]);
83
- }
84
- function tryGetNode(run, nodeId) {
85
- try {
86
- return (0, pipeline_runner_1.getRunNode)(run, nodeId);
87
- }
88
- catch {
89
- return undefined;
90
- }
91
- }
92
- function snapshotsRoot(run) {
93
- const base = run.paths.stateNodesDir || node_path_1.default.join(run.paths.runDir, "nodes");
94
- return node_path_1.default.join(base, "snapshots");
95
- }
96
- function snapshotDir(run, nodeId) {
97
- return node_path_1.default.join(snapshotsRoot(run), (0, state_1.safeFileName)(nodeId));
98
- }
99
- /** Load a persisted snapshot by id (scans the per-node snapshot dirs). */
100
- function readNodeSnapshot(run, snapshotId) {
101
- const root = snapshotsRoot(run);
102
- if (node_fs_1.default.existsSync(root)) {
103
- for (const nodeDir of node_fs_1.default.readdirSync(root)) {
104
- const file = node_path_1.default.join(root, nodeDir, `${snapshotId}.json`);
105
- if (node_fs_1.default.existsSync(file))
106
- return (0, validation_1.validateNodeSnapshot)(JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
107
- }
108
- }
109
- throw new NodeSnapshotError("snapshot-not-found", `Node snapshot ${snapshotId} not found in run ${run.id}`, { freshness: "absent" });
110
- }
111
- /** Load a persisted replay run by id. */
112
- function readNodeReplay(run, replayId) {
113
- const root = snapshotsRoot(run);
114
- if (node_fs_1.default.existsSync(root)) {
115
- for (const nodeDir of node_fs_1.default.readdirSync(root)) {
116
- const file = node_path_1.default.join(root, nodeDir, "replays", `${replayId}.json`);
117
- if (node_fs_1.default.existsSync(file))
118
- return (0, validation_1.validateNodeReplayRun)(JSON.parse(node_fs_1.default.readFileSync(file, "utf8")));
119
- }
120
- }
121
- throw new NodeSnapshotError("replay-not-found", `Node replay ${replayId} not found in run ${run.id}`, { freshness: "absent" });
122
- }
123
- /** Snapshot one StateNode by id. Throws (fail closed) if the node is absent. */
124
- function snapshotNode(run, nodeId, options = {}) {
125
- const node = tryGetNode(run, nodeId);
126
- if (!node) {
127
- throw new NodeSnapshotError("node-absent", `Cannot snapshot: node ${nodeId} not found in run ${run.id}`, { freshness: "absent" });
128
- }
129
- const fingerprint = sourceFingerprint(node);
130
- const snapshot = {
131
- schemaVersion: 1,
132
- snapshotId: `snap-${(0, state_1.safeFileName)(nodeId)}-${fingerprint.replace("sha256:", "").slice(0, 12)}`,
133
- runId: run.id,
134
- nodeId,
135
- capturedAt: options.now || new Date().toISOString(),
136
- sourceFingerprint: fingerprint,
137
- body: snapshotBody(node)
138
- };
139
- if (options.persist !== false) {
140
- (0, state_1.writeJson)(node_path_1.default.join(snapshotDir(run, nodeId), `${snapshot.snapshotId}.json`), snapshot);
141
- }
142
- return snapshot;
143
- }
144
- /** Recompute freshness from current source. valid | stale | absent. */
145
- function loadNodeSnapshot(run, snapshot) {
146
- const node = tryGetNode(run, snapshot.nodeId);
147
- if (!node) {
148
- return { snapshot, freshness: "absent", reason: `source node ${snapshot.nodeId} is gone from run ${run.id}` };
149
- }
150
- const missingArtifact = node.artifacts.find((artifact) => artifact.path && !node_fs_1.default.existsSync(artifact.path));
151
- if (missingArtifact) {
152
- return { snapshot, freshness: "absent", reason: `referenced artifact path is unreadable: ${missingArtifact.id}` };
153
- }
154
- if (sourceFingerprint(node) !== snapshot.sourceFingerprint) {
155
- return { snapshot, freshness: "stale", reason: `source node ${snapshot.nodeId} changed since capture` };
156
- }
157
- return { snapshot, freshness: "valid" };
158
- }
159
- function sectionValue(body, section) {
160
- if (section === "links")
161
- return { parents: body.parents, children: body.children };
162
- return body[section];
163
- }
164
- /** Stable, structural diff of two snapshots (same node id or two explicit ids). */
165
- function diffNodeSnapshots(baseline, candidate) {
166
- const sections = SNAPSHOT_SECTIONS.map((section) => {
167
- const baselineValue = sectionValue(baseline.body, section);
168
- const candidateValue = sectionValue(candidate.body, section);
169
- const sameBytes = (0, multi_agent_eval_1.replayStableStringify)(baselineValue) === (0, multi_agent_eval_1.replayStableStringify)(candidateValue);
170
- let change;
171
- if (sameBytes)
172
- change = "same";
173
- else if (baselineValue === undefined)
174
- change = "added";
175
- else if (candidateValue === undefined)
176
- change = "removed";
177
- else
178
- change = "changed";
179
- const entry = { section, change };
180
- if (change !== "same") {
181
- entry.baseline = baselineValue;
182
- entry.candidate = candidateValue;
183
- }
184
- return entry;
185
- });
186
- return {
187
- schemaVersion: 1,
188
- runId: baseline.runId,
189
- baselineSnapshotId: baseline.snapshotId,
190
- candidateSnapshotId: candidate.snapshotId,
191
- baselineNodeId: baseline.nodeId,
192
- candidateNodeId: candidate.nodeId,
193
- changed: sections.some((entry) => entry.change !== "same"),
194
- sections
195
- };
196
- }
197
- /** Deterministically replay one node from its snapshot, fail-closed on drift.
198
- * `now` is injected; the deterministic payload (body + outputFingerprint) has
199
- * zero wall-clock, so two replays are byte-identical. */
200
- function replayNodeSnapshot(run, snapshot, options = {}) {
201
- const { freshness, reason } = loadNodeSnapshot(run, snapshot);
202
- if (freshness !== "valid") {
203
- throw new NodeSnapshotError(freshness === "stale" ? "snapshot-stale" : "snapshot-absent", reason || `cannot replay a ${freshness} snapshot of node ${snapshot.nodeId}`, { freshness, details: { runId: run.id, nodeId: snapshot.nodeId } });
204
- }
205
- const body = (0, multi_agent_eval_1.normalizeValue)(snapshot.body);
206
- const outputFingerprint = (0, fingerprint_1.fingerprintStrings)([(0, multi_agent_eval_1.replayStableStringify)(body)]);
207
- const replay = {
208
- schemaVersion: 1,
209
- replayId: `replay-${snapshot.snapshotId}-${outputFingerprint.replace("sha256:", "").slice(0, 8)}`,
210
- runId: run.id,
211
- nodeId: snapshot.nodeId,
212
- snapshotId: snapshot.snapshotId,
213
- replayedAt: options.now || new Date().toISOString(),
214
- freshness: "valid",
215
- contractValidated: Boolean(snapshot.body.contractId),
216
- outputFingerprint,
217
- body
218
- };
219
- if (options.persist !== false) {
220
- (0, state_1.writeJson)(node_path_1.default.join(snapshotDir(run, snapshot.nodeId), "replays", `${replay.replayId}.json`), replay);
221
- }
222
- return replay;
223
- }
224
- /** Compare a replay to a fresh snapshot of the source node; pass = byte-identical
225
- * normalized body. Findings reuse the eval harness severity/category shape. */
226
- function verifyNodeReplay(run, replay, options = {}) {
227
- const fresh = tryGetNode(run, replay.nodeId);
228
- if (!fresh) {
229
- return {
230
- schemaVersion: 1,
231
- runId: run.id,
232
- nodeId: replay.nodeId,
233
- replayId: replay.replayId,
234
- pass: false,
235
- freshness: "absent",
236
- findings: [{ id: "source-absent", severity: "error", category: "source", reason: `source node ${replay.nodeId} is gone` }]
237
- };
238
- }
239
- const freshSnapshot = snapshotNode(run, replay.nodeId, { now: options.now, persist: false });
240
- const diff = diffNodeSnapshots(freshSnapshot, { ...freshSnapshot, body: replay.body, snapshotId: replay.snapshotId });
241
- const findings = diff.sections
242
- .filter((section) => section.change !== "same")
243
- .map((section) => ({
244
- id: `drift:${section.section}`,
245
- severity: "error",
246
- category: section.section,
247
- reason: `replay diverged from source in ${section.section}`,
248
- baselineRef: replay.snapshotId,
249
- replayRef: replay.replayId
250
- }));
251
- return {
252
- schemaVersion: 1,
253
- runId: run.id,
254
- nodeId: replay.nodeId,
255
- replayId: replay.replayId,
256
- pass: findings.length === 0,
257
- freshness: "valid",
258
- findings
259
- };
260
- }