cool-workflow 0.1.98 → 0.2.1

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 (315) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +18 -7
  4. package/apps/architecture-review/app.json +2 -2
  5. package/apps/architecture-review/workflow.js +12 -12
  6. package/apps/architecture-review-fast/app.json +1 -1
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/cli/dispatch.js +236 -0
  12. package/dist/cli/entry.js +120 -0
  13. package/dist/cli/io.js +21 -4
  14. package/dist/cli/parseargv.js +157 -0
  15. package/dist/cli.js +6 -33
  16. package/dist/core/capability-table.js +3518 -0
  17. package/dist/core/format/help.js +314 -0
  18. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  19. package/dist/core/hash.js +137 -0
  20. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  21. package/dist/core/multi-agent/collaboration.js +481 -0
  22. package/dist/core/multi-agent/coordinator.js +515 -0
  23. package/dist/core/multi-agent/eval-replay.js +306 -0
  24. package/dist/core/multi-agent/runtime.js +929 -0
  25. package/dist/core/multi-agent/topology.js +298 -0
  26. package/dist/core/multi-agent/trust-policy.js +197 -0
  27. package/dist/core/pipeline/commit-gate.js +320 -0
  28. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  29. package/dist/core/pipeline/dispatch.js +103 -0
  30. package/dist/core/pipeline/drive-decide.js +227 -0
  31. package/dist/core/pipeline/error-feedback.js +161 -0
  32. package/dist/core/pipeline/loop-expansion.js +124 -0
  33. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  34. package/dist/core/pipeline/runner.js +230 -0
  35. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  36. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  37. package/dist/core/state/node-projection.js +95 -0
  38. package/dist/core/state/node-snapshot.js +230 -0
  39. package/dist/core/state/run-paths.js +93 -0
  40. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  41. package/dist/core/state/schema.js +50 -0
  42. package/dist/core/state/state-explosion/digest.js +243 -0
  43. package/dist/core/state/state-explosion/graph.js +527 -0
  44. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  45. package/dist/core/state/state-explosion/report.js +187 -0
  46. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  47. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  48. package/dist/core/state/types.js +19 -0
  49. package/dist/{validation.js → core/state/validation.js} +64 -131
  50. package/dist/core/trust/evidence-grounding.js +134 -0
  51. package/dist/core/trust/ledger.js +199 -0
  52. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  53. package/dist/core/trust/telemetry-ledger.js +127 -0
  54. package/dist/core/types.js +20 -0
  55. package/dist/core/version.js +16 -0
  56. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  57. package/dist/mcp/dispatch.js +104 -0
  58. package/dist/mcp/server.js +144 -0
  59. package/dist/mcp-server.js +6 -84
  60. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  61. package/dist/shell/app-run-cli.js +88 -0
  62. package/dist/shell/audit-cli.js +259 -0
  63. package/dist/shell/audit-provenance.js +83 -0
  64. package/dist/shell/candidate-scoring-io.js +459 -0
  65. package/dist/shell/collaboration-io.js +264 -0
  66. package/dist/shell/commit-summary.js +104 -0
  67. package/dist/shell/commit.js +290 -0
  68. package/dist/shell/coordinator-io.js +476 -0
  69. package/dist/shell/demo-cli.js +19 -0
  70. package/dist/shell/dispatch.js +162 -0
  71. package/dist/shell/doctor.js +292 -0
  72. package/dist/shell/drive.js +885 -0
  73. package/dist/shell/error-feedback-io.js +305 -0
  74. package/dist/shell/eval-io.js +473 -0
  75. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  76. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  77. package/dist/shell/exec-backend-cli.js +88 -0
  78. package/dist/shell/execution-backend/agent.js +507 -0
  79. package/dist/shell/execution-backend/ci.js +15 -0
  80. package/dist/shell/execution-backend/container.js +69 -0
  81. package/dist/shell/execution-backend/envelopes.js +55 -0
  82. package/dist/shell/execution-backend/local.js +113 -0
  83. package/dist/shell/execution-backend/probes.js +175 -0
  84. package/dist/shell/execution-backend/registry.js +402 -0
  85. package/dist/shell/execution-backend/remote.js +128 -0
  86. package/dist/shell/execution-backend/types.js +11 -0
  87. package/dist/shell/feedback-cli.js +81 -0
  88. package/dist/shell/feedback-operations.js +48 -0
  89. package/dist/shell/fs-atomic.js +276 -0
  90. package/dist/shell/harness.js +98 -0
  91. package/dist/shell/ledger-cli.js +212 -0
  92. package/dist/shell/ledger-io.js +169 -0
  93. package/dist/shell/man-cli.js +89 -0
  94. package/dist/shell/metrics-cli.js +100 -0
  95. package/dist/shell/multi-agent-cli.js +1002 -0
  96. package/dist/shell/multi-agent-host.js +563 -0
  97. package/dist/shell/multi-agent-io.js +387 -0
  98. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  99. package/dist/shell/node-store.js +124 -0
  100. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  101. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  102. package/dist/{observability.js → shell/observability.js} +159 -332
  103. package/dist/{onramp.js → shell/onramp.js} +11 -0
  104. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  105. package/dist/shell/operator-ux.js +431 -0
  106. package/dist/shell/orchestrator.js +231 -0
  107. package/dist/shell/pipeline-cli.js +667 -0
  108. package/dist/shell/pipeline.js +217 -0
  109. package/dist/shell/reclamation-io.js +1366 -0
  110. package/dist/shell/registry-cli.js +344 -0
  111. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  112. package/dist/shell/report-cli.js +101 -0
  113. package/dist/shell/report-view-cli.js +117 -0
  114. package/dist/{orchestrator → shell}/report.js +289 -282
  115. package/dist/shell/reporter.js +62 -0
  116. package/dist/shell/run-export-cli.js +106 -0
  117. package/dist/shell/run-export.js +680 -0
  118. package/dist/shell/run-registry-io.js +1014 -0
  119. package/dist/shell/run-store.js +164 -0
  120. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  121. package/dist/{scheduler.js → shell/scheduler-io.js} +257 -48
  122. package/dist/shell/scheduling-io.js +321 -0
  123. package/dist/shell/state-cli.js +181 -0
  124. package/dist/shell/state-explosion-cli.js +197 -0
  125. package/dist/shell/telemetry-cli.js +85 -0
  126. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  127. package/dist/shell/telemetry-ledger-io.js +132 -0
  128. package/dist/{term.js → shell/term.js} +36 -46
  129. package/dist/shell/topology-io.js +361 -0
  130. package/dist/shell/trust-audit.js +472 -0
  131. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  132. package/dist/shell/verifier.js +48 -0
  133. package/dist/shell/workbench-host.js +258 -0
  134. package/dist/shell/workbench-text.js +18 -0
  135. package/dist/shell/workbench.js +170 -0
  136. package/dist/shell/worker-cli.js +124 -0
  137. package/dist/shell/worker-isolation.js +852 -0
  138. package/dist/shell/workflow-app-loader.js +650 -0
  139. package/docs/agent-delegation-drive.7.md +4 -0
  140. package/docs/cli-mcp-parity.7.md +282 -219
  141. package/docs/contract-migration-tooling.7.md +4 -0
  142. package/docs/control-plane-scheduling.7.md +4 -0
  143. package/docs/durable-state-and-locking.7.md +4 -0
  144. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  145. package/docs/execution-backends.7.md +4 -0
  146. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  147. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  148. package/docs/multi-agent-operator-ux.7.md +4 -0
  149. package/docs/node-snapshot-diff-replay.7.md +4 -0
  150. package/docs/observability-cost-accounting.7.md +6 -1
  151. package/docs/project-index.md +137 -71
  152. package/docs/real-execution-backends.7.md +4 -0
  153. package/docs/release-and-migration.7.md +4 -0
  154. package/docs/release-tooling.7.md +4 -0
  155. package/docs/remote-source-review.7.md +4 -4
  156. package/docs/report-verifiable-bundle.7.md +1 -1
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/security-trust-hardening.7.md +3 -1
  160. package/docs/state-explosion-management.7.md +4 -0
  161. package/docs/team-collaboration.7.md +4 -0
  162. package/docs/web-desktop-workbench.7.md +15 -1
  163. package/manifest/plugin.manifest.json +1 -1
  164. package/manifest/source-context-profiles.json +9 -13
  165. package/package.json +1 -1
  166. package/scripts/agents/agent-adapter-core.js +22 -2
  167. package/scripts/agents/claude-p-agent.js +8 -3
  168. package/scripts/agents/cw-attest-wrap.js +2 -2
  169. package/scripts/agents/gemini-agent.js +5 -1
  170. package/scripts/agents/opencode-agent.js +5 -1
  171. package/scripts/bump-version.js +4 -3
  172. package/scripts/canonical-apps.js +4 -4
  173. package/scripts/dogfood-architecture-review.js +2 -3
  174. package/scripts/dogfood-release.js +3 -3
  175. package/scripts/gen-parity-doc.js +15 -2
  176. package/scripts/golden-path.js +4 -4
  177. package/scripts/onramp-check.js +1 -1
  178. package/scripts/parity-check.js +38 -21
  179. package/scripts/release-flow.js +2 -2
  180. package/scripts/sync-project-index.js +51 -27
  181. package/scripts/validate-run-state-schema.js +2 -2
  182. package/scripts/version-sync-check.js +30 -30
  183. package/dist/candidate-scoring.js +0 -729
  184. package/dist/capability-core.js +0 -1189
  185. package/dist/capability-registry.js +0 -885
  186. package/dist/cli/command-surface.js +0 -494
  187. package/dist/cli/format.js +0 -56
  188. package/dist/cli/handlers/audit.js +0 -82
  189. package/dist/cli/handlers/blackboard.js +0 -81
  190. package/dist/cli/handlers/candidate.js +0 -40
  191. package/dist/cli/handlers/clones.js +0 -34
  192. package/dist/cli/handlers/collaboration.js +0 -61
  193. package/dist/cli/handlers/eval.js +0 -40
  194. package/dist/cli/handlers/ledger.js +0 -169
  195. package/dist/cli/handlers/maintenance.js +0 -107
  196. package/dist/cli/handlers/multi-agent.js +0 -165
  197. package/dist/cli/handlers/node.js +0 -41
  198. package/dist/cli/handlers/operational.js +0 -155
  199. package/dist/cli/handlers/operator.js +0 -146
  200. package/dist/cli/handlers/registry.js +0 -68
  201. package/dist/cli/handlers/run.js +0 -153
  202. package/dist/cli/handlers/scheduling.js +0 -132
  203. package/dist/cli/handlers/workbench.js +0 -41
  204. package/dist/cli/handlers/worker.js +0 -45
  205. package/dist/cli/run-summary.js +0 -45
  206. package/dist/clones.js +0 -162
  207. package/dist/collaboration.js +0 -726
  208. package/dist/commit.js +0 -592
  209. package/dist/compare.js +0 -18
  210. package/dist/coordinator/classify.js +0 -45
  211. package/dist/coordinator/paths.js +0 -42
  212. package/dist/coordinator/util.js +0 -126
  213. package/dist/coordinator.js +0 -990
  214. package/dist/daemon.js +0 -44
  215. package/dist/dispatch.js +0 -250
  216. package/dist/doctor.js +0 -212
  217. package/dist/drive.js +0 -864
  218. package/dist/error-feedback.js +0 -419
  219. package/dist/evidence-grounding.js +0 -184
  220. package/dist/execution-backend/agent.js +0 -354
  221. package/dist/execution-backend/probes.js +0 -112
  222. package/dist/execution-backend/util.js +0 -47
  223. package/dist/execution-backend.js +0 -961
  224. package/dist/gates.js +0 -48
  225. package/dist/harness.js +0 -61
  226. package/dist/ledger.js +0 -313
  227. package/dist/loop-expansion.js +0 -60
  228. package/dist/mcp/tool-call.js +0 -470
  229. package/dist/mcp/tool-definitions.js +0 -1066
  230. package/dist/mcp-surface.js +0 -30
  231. package/dist/multi-agent/graph.js +0 -84
  232. package/dist/multi-agent/helpers.js +0 -141
  233. package/dist/multi-agent/ids.js +0 -20
  234. package/dist/multi-agent/paths.js +0 -22
  235. package/dist/multi-agent-eval/normalize.js +0 -51
  236. package/dist/multi-agent-eval.js +0 -678
  237. package/dist/multi-agent-host.js +0 -777
  238. package/dist/multi-agent.js +0 -984
  239. package/dist/node-projection.js +0 -59
  240. package/dist/node-snapshot.js +0 -260
  241. package/dist/operator-ux.js +0 -631
  242. package/dist/orchestrator/app-operations.js +0 -211
  243. package/dist/orchestrator/audit-operations.js +0 -182
  244. package/dist/orchestrator/candidate-operations.js +0 -117
  245. package/dist/orchestrator/cli-options.js +0 -294
  246. package/dist/orchestrator/collaboration-operations.js +0 -86
  247. package/dist/orchestrator/feedback-operations.js +0 -81
  248. package/dist/orchestrator/host-operations.js +0 -78
  249. package/dist/orchestrator/lifecycle-operations.js +0 -650
  250. package/dist/orchestrator/migration-operations.js +0 -44
  251. package/dist/orchestrator/multi-agent-operations.js +0 -362
  252. package/dist/orchestrator/topology-operations.js +0 -84
  253. package/dist/orchestrator.js +0 -925
  254. package/dist/pipeline-runner.js +0 -285
  255. package/dist/reclamation/hash.js +0 -72
  256. package/dist/reclamation.js +0 -812
  257. package/dist/reporter.js +0 -67
  258. package/dist/run-export.js +0 -815
  259. package/dist/run-registry/derive.js +0 -175
  260. package/dist/run-registry/format.js +0 -124
  261. package/dist/run-registry/gc.js +0 -251
  262. package/dist/run-registry/policy.js +0 -16
  263. package/dist/run-registry/queue.js +0 -115
  264. package/dist/run-registry.js +0 -850
  265. package/dist/run-state-schema.js +0 -68
  266. package/dist/scheduling.js +0 -184
  267. package/dist/state-explosion.js +0 -1014
  268. package/dist/state.js +0 -367
  269. package/dist/telemetry-ledger.js +0 -196
  270. package/dist/topology.js +0 -565
  271. package/dist/triggers.js +0 -184
  272. package/dist/trust-audit.js +0 -644
  273. package/dist/types/blackboard.js +0 -2
  274. package/dist/types/candidate.js +0 -2
  275. package/dist/types/collaboration.js +0 -2
  276. package/dist/types/core.js +0 -2
  277. package/dist/types/drive.js +0 -10
  278. package/dist/types/error-feedback.js +0 -2
  279. package/dist/types/evidence-reasoning.js +0 -2
  280. package/dist/types/execution-backend.js +0 -2
  281. package/dist/types/multi-agent.js +0 -2
  282. package/dist/types/observability.js +0 -2
  283. package/dist/types/pipeline.js +0 -2
  284. package/dist/types/reclamation.js +0 -8
  285. package/dist/types/report-bundle.js +0 -6
  286. package/dist/types/result.js +0 -2
  287. package/dist/types/run-registry.js +0 -2
  288. package/dist/types/run.js +0 -2
  289. package/dist/types/sandbox.js +0 -2
  290. package/dist/types/schedule.js +0 -2
  291. package/dist/types/state-node.js +0 -2
  292. package/dist/types/topology.js +0 -2
  293. package/dist/types/trust.js +0 -2
  294. package/dist/types/workbench.js +0 -2
  295. package/dist/types/worker.js +0 -2
  296. package/dist/types/workflow-app.js +0 -2
  297. package/dist/types.js +0 -44
  298. package/dist/util/fingerprint.js +0 -19
  299. package/dist/util/fingerprint.test.js +0 -27
  300. package/dist/verifier.js +0 -78
  301. package/dist/version.js +0 -8
  302. package/dist/workbench-host.js +0 -192
  303. package/dist/workbench.js +0 -192
  304. package/dist/worker-accept/acceptance.js +0 -114
  305. package/dist/worker-accept/blackboard-fanout.js +0 -80
  306. package/dist/worker-accept/blackboard-linkage.js +0 -19
  307. package/dist/worker-accept/context.js +0 -2
  308. package/dist/worker-accept/telemetry-ledger.js +0 -126
  309. package/dist/worker-accept/validation.js +0 -77
  310. package/dist/worker-accept/verifier-completion.js +0 -73
  311. package/dist/worker-isolation/helpers.js +0 -51
  312. package/dist/worker-isolation/paths.js +0 -46
  313. package/dist/worker-isolation.js +0 -656
  314. package/dist/workflow-api.js +0 -131
  315. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ // shell/fs-atomic.ts — atomic/durable JSON file IO and the cross-process
3
+ // advisory file lock. Impure by nature (fs calls); this is the shell layer,
4
+ // not core. Every writer in core/+shell/ that touches disk goes through
5
+ // `writeJson` here, so the write bytes stay exactly one place.
6
+ //
7
+ // Evidence: SPEC/state-core.md "Write ordering and atomic rules", "Lock
8
+ // protocol"; docs/rebuild/PLAN.md byte-compat items 1 and 6.
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.writeJson = writeJson;
44
+ exports.readJson = readJson;
45
+ exports.safeFileName = safeFileName;
46
+ exports.assertSafeRunId = assertSafeRunId;
47
+ exports.realResolve = realResolve;
48
+ exports.isContainedPath = isContainedPath;
49
+ exports.durableAppendFileSync = durableAppendFileSync;
50
+ exports.withFileLock = withFileLock;
51
+ const fs = __importStar(require("node:fs"));
52
+ const path = __importStar(require("node:path"));
53
+ // ---------------------------------------------------------------------------
54
+ // writeJson — the ONE JSON-on-disk byte format: JSON.stringify(value, null, 2)
55
+ // + "\n", written via temp-file + optional fsync + rename (atomic).
56
+ // ---------------------------------------------------------------------------
57
+ let atomicWriteCounter = 0;
58
+ /** Atomic, optionally-durable JSON write. ORDER IS THE SAFETY PROPERTY: write
59
+ * the full bytes to a unique temp file, optionally fsync it, close, then
60
+ * rename over the target. `rename(2)` is atomic on POSIX, so a reader always
61
+ * sees either the old bytes or the new bytes, never a torn file. On a rename
62
+ * failure the temp file is removed (best-effort) and the error is rethrown —
63
+ * the old bytes at `file` are never touched. */
64
+ function writeJson(file, value, options = {}) {
65
+ fs.mkdirSync(path.dirname(file), { recursive: true });
66
+ const tmp = `${file}.tmp.${process.pid}.${atomicWriteCounter++}`;
67
+ const fd = fs.openSync(tmp, "w");
68
+ try {
69
+ fs.writeFileSync(fd, `${JSON.stringify(value, null, 2)}\n`, "utf8");
70
+ if (options.durable)
71
+ fs.fsyncSync(fd);
72
+ }
73
+ finally {
74
+ fs.closeSync(fd);
75
+ }
76
+ try {
77
+ fs.renameSync(tmp, file);
78
+ }
79
+ catch (error) {
80
+ try {
81
+ fs.rmSync(tmp, { force: true });
82
+ }
83
+ catch {
84
+ /* best-effort temp cleanup */
85
+ }
86
+ throw error;
87
+ }
88
+ if (options.durable) {
89
+ try {
90
+ const dirFd = fs.openSync(path.dirname(file), "r");
91
+ try {
92
+ fs.fsyncSync(dirFd);
93
+ }
94
+ finally {
95
+ fs.closeSync(dirFd);
96
+ }
97
+ }
98
+ catch {
99
+ /* directory fsync is best-effort (not supported on every platform) */
100
+ }
101
+ }
102
+ }
103
+ /** Read + JSON.parse a file. Throws `File not found: <file>` when absent,
104
+ * `Invalid JSON in <file>: <message>` on a parse error. */
105
+ function readJson(file) {
106
+ if (!fs.existsSync(file))
107
+ throw new Error(`File not found: ${file}`);
108
+ try {
109
+ return JSON.parse(fs.readFileSync(file, "utf8"));
110
+ }
111
+ catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ throw new Error(`Invalid JSON in ${file}: ${message}`);
114
+ }
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Safe ids / names — src/state.ts:310-334 in the old build.
118
+ // ---------------------------------------------------------------------------
119
+ /** Replaces every run of chars outside `[a-zA-Z0-9_.:-]` with a single `_`. */
120
+ function safeFileName(value) {
121
+ return String(value).replace(/[^a-zA-Z0-9_.:-]+/g, "_");
122
+ }
123
+ /** Refuse a run id that is not a single safe path segment, so an id taken
124
+ * from an untrusted source (an imported run archive/bundle) can never
125
+ * escape the runs directory via a separator or a `..`/`.` component. The
126
+ * charset already forbids any separator, so the whole id is ONE path
127
+ * component; only the exact components `.` and `..` are refused — an
128
+ * EMBEDDED `..` (e.g. `v1..2`) is a safe directory name and is allowed. */
129
+ function assertSafeRunId(value, context = "run id") {
130
+ if (typeof value !== "string" || value.length === 0) {
131
+ throw new Error(`Invalid ${context}: expected a non-empty string`);
132
+ }
133
+ if (!/^[A-Za-z0-9._:-]+$/.test(value) || value === "." || value === "..") {
134
+ throw new Error(`Unsafe ${context}: ${JSON.stringify(value)} must be a single path segment ([A-Za-z0-9._:-], not '.' or '..')`);
135
+ }
136
+ return value;
137
+ }
138
+ // ---------------------------------------------------------------------------
139
+ // Symlink-hardened path containment — src/state.ts:195-227 in the old build.
140
+ // ---------------------------------------------------------------------------
141
+ /** Realpath the deepest EXISTING ancestor (follows symlinks), then re-join
142
+ * the not-yet-created tail. If nothing exists up to the root, returns
143
+ * plain `path.resolve(target)`. */
144
+ function realResolve(target) {
145
+ let current = path.resolve(target);
146
+ const tail = [];
147
+ for (;;) {
148
+ try {
149
+ const real = fs.realpathSync.native ? fs.realpathSync.native(current) : fs.realpathSync(current);
150
+ return tail.length ? path.join(real, ...tail.reverse()) : real;
151
+ }
152
+ catch {
153
+ const parent = path.dirname(current);
154
+ if (parent === current)
155
+ return path.resolve(target);
156
+ tail.push(path.basename(current));
157
+ current = parent;
158
+ }
159
+ }
160
+ }
161
+ /** True when `realResolve(candidate)` equals `realResolve(allowed)` or
162
+ * starts with it plus `path.sep`. Realpaths BOTH sides so a symlinked temp
163
+ * root (macOS `/tmp` -> `/private/tmp`) compares right. */
164
+ function isContainedPath(candidate, allowed) {
165
+ const realCandidate = realResolve(candidate);
166
+ const realAllowed = realResolve(allowed);
167
+ return realCandidate === realAllowed || realCandidate.startsWith(realAllowed + path.sep);
168
+ }
169
+ // ---------------------------------------------------------------------------
170
+ // durableAppendFileSync — append a line and fsync it before returning. Used
171
+ // by the trust-audit event log, whose loss breaks audit-completeness.
172
+ // ---------------------------------------------------------------------------
173
+ function durableAppendFileSync(file, data) {
174
+ fs.mkdirSync(path.dirname(file), { recursive: true });
175
+ const fd = fs.openSync(file, "a");
176
+ try {
177
+ fs.writeFileSync(fd, data, "utf8");
178
+ fs.fsyncSync(fd);
179
+ }
180
+ finally {
181
+ fs.closeSync(fd);
182
+ }
183
+ }
184
+ // ---------------------------------------------------------------------------
185
+ // withFileLock — portable advisory cross-process lock.
186
+ //
187
+ // Lock file: `<targetPath>.lock`, created with O_EXCL (`wx`), body
188
+ // `"<pid>@<ISO>\n"`. Up to 240 tries; on EEXIST, a lock whose mtime is older
189
+ // than FILE_LOCK_STALE_MS (30_000) is stolen (deleted) and retried AT ONCE;
190
+ // else sleep 25ms (Atomics.wait busy-safe sleep) and retry. Any non-EEXIST
191
+ // open error is rethrown. No lock after 240 tries throws
192
+ // `could not acquire file lock for <targetPath>`.
193
+ //
194
+ // Right before fn() the lock mtime is refreshed (utimesSync, best-effort).
195
+ // After fn() returns, the lock body is re-read: if it no longer starts with
196
+ // "<pid>@", the lock was stolen mid-operation -> throw the "stolen" error and
197
+ // do NOT delete the thief's lock. On the release path the lock is removed
198
+ // only when still owned by this pid.
199
+ // ---------------------------------------------------------------------------
200
+ const FILE_LOCK_STALE_MS = 30_000;
201
+ function sleepSync(ms) {
202
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
203
+ }
204
+ /** Run `fn` while holding an advisory lock for `targetPath`; always released
205
+ * (unless the lock was stolen mid-operation, in which case releasing would
206
+ * corrupt the thief's critical section, so it is deliberately NOT released). */
207
+ function withFileLock(targetPath, fn) {
208
+ const lock = `${targetPath}.lock`;
209
+ fs.mkdirSync(path.dirname(lock), { recursive: true });
210
+ const pid = String(process.pid);
211
+ let acquired = false;
212
+ for (let attempt = 0; attempt < 240 && !acquired; attempt++) {
213
+ try {
214
+ const fd = fs.openSync(lock, "wx");
215
+ fs.writeFileSync(fd, `${pid}@${new Date().toISOString()}\n`, "utf8");
216
+ fs.closeSync(fd);
217
+ acquired = true;
218
+ }
219
+ catch (error) {
220
+ if (!(error && typeof error === "object" && error.code === "EEXIST")) {
221
+ throw error;
222
+ }
223
+ try {
224
+ if (Date.now() - fs.statSync(lock).mtimeMs > FILE_LOCK_STALE_MS) {
225
+ fs.rmSync(lock, { force: true });
226
+ continue;
227
+ }
228
+ }
229
+ catch {
230
+ continue;
231
+ }
232
+ sleepSync(25);
233
+ }
234
+ }
235
+ if (!acquired)
236
+ throw new Error(`could not acquire file lock for ${targetPath}`);
237
+ // Refresh mtime right before the critical section.
238
+ try {
239
+ fs.utimesSync(lock, new Date(), new Date());
240
+ }
241
+ catch {
242
+ /* best-effort */
243
+ }
244
+ try {
245
+ const result = fn();
246
+ // Verify the lock was not stolen during fn(). If our pid is no longer at
247
+ // the front of the lock body, another process's stale-steal fired mid-
248
+ // operation and now owns the lock — do NOT release it (that would corrupt
249
+ // the thief's critical section).
250
+ try {
251
+ const current = fs.readFileSync(lock, "utf8");
252
+ if (!current.startsWith(pid + "@")) {
253
+ throw new Error(`File lock for ${targetPath} was stolen during the critical section ` +
254
+ `(lock now owned by another process). The operation may have lost ` +
255
+ `cross-process isolation — increase FILE_LOCK_STALE_MS or split the work.`);
256
+ }
257
+ }
258
+ catch (checkError) {
259
+ if (checkError instanceof Error && checkError.message.includes("stolen"))
260
+ throw checkError;
261
+ /* lock vanished — another process already released it, nothing to clean up */
262
+ }
263
+ return result;
264
+ }
265
+ finally {
266
+ try {
267
+ // Only release if we still own the lock.
268
+ const current = fs.readFileSync(lock, "utf8");
269
+ if (current.startsWith(pid + "@"))
270
+ fs.rmSync(lock, { force: true });
271
+ }
272
+ catch {
273
+ /* releasing a missing lock is fine */
274
+ }
275
+ }
276
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ // shell/harness.ts — writeTaskFiles, renderTask (task file template).
3
+ //
4
+ // MILESTONE 6+7 (combined). Byte-exact port of the old build's
5
+ // src/harness.ts.
6
+ //
7
+ // Evidence: SPEC/pipeline-run.md "Task files — src/harness.ts", "Task
8
+ // file template (verbatim skeleton from renderTask)".
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.renderTask = renderTask;
44
+ exports.writeTaskFiles = writeTaskFiles;
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const fs_atomic_1 = require("./fs-atomic");
48
+ function formatInputList(value) {
49
+ if (Array.isArray(value))
50
+ return value.join("; ");
51
+ return value ? String(value) : "";
52
+ }
53
+ function renderTask(run, task) {
54
+ return [
55
+ `# ${task.id}`,
56
+ "",
57
+ `- Workflow: ${run.workflow.title}`,
58
+ `- Run: ${run.id}`,
59
+ `- Phase: ${task.phase}`,
60
+ `- Kind: ${task.kind}`,
61
+ "",
62
+ "## Inputs",
63
+ "",
64
+ `- Repository: ${String(run.inputs.repo || run.cwd)}`,
65
+ `- Question: ${String(run.inputs.question || "")}`,
66
+ `- Invariants: ${formatInputList(run.inputs.invariant)}`,
67
+ "",
68
+ "## Task",
69
+ "",
70
+ task.prompt,
71
+ "",
72
+ "## Output Contract",
73
+ "",
74
+ "- Return a concise Markdown summary.",
75
+ "- Include concrete evidence paths and line numbers when applicable.",
76
+ "- Separate real, conditional, non-issue, and unknown findings when reviewing risk.",
77
+ "- For verification or verdict tasks, include a `cw:result` JSON fence with `findings` and `evidence`.",
78
+ "- Do not edit files unless the parent agent session explicitly assigned implementation work.",
79
+ "",
80
+ "```cw:result",
81
+ "{",
82
+ ' "summary": "one sentence",',
83
+ ' "findings": [',
84
+ ' { "id": "finding-id", "classification": "real|conditional|non-issue|unknown", "severity": "P0|P1|P2|P3|none", "evidence": ["file-or-url:line"] }',
85
+ " ],",
86
+ ' "evidence": ["file-or-url:line"]',
87
+ "}",
88
+ "```",
89
+ "",
90
+ ].join("\n");
91
+ }
92
+ function writeTaskFiles(run) {
93
+ for (const task of run.tasks) {
94
+ const taskPath = path.join(run.paths.tasksDir, `${(0, fs_atomic_1.safeFileName)(task.id)}.md`);
95
+ task.taskPath = taskPath;
96
+ fs.writeFileSync(taskPath, renderTask(run, task), "utf8");
97
+ }
98
+ }
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ // shell/ledger-cli.ts — CLI/MCP-reachable bodies for `cw ledger
3
+ // propose|review|verify|apply|list`.
4
+ //
5
+ // MILESTONE 8. Byte-exact port of the old build's src/cli/handlers/
6
+ // ledger.ts argv shape, now calling core/trust/ledger.ts's pure
7
+ // functions + shell/ledger-io.ts's directory reads. Impure (fs: stdin/
8
+ // file reads for verify/apply, directory scans for list) — this is the
9
+ // shell layer the capability-table's CLI/MCP handlers delegate to.
10
+ //
11
+ // Evidence: SPEC/ledger-trust.md "CLI: `cw ledger`", "Edge cases";
12
+ // plugins/cool-workflow/src/cli/handlers/ledger.ts:1-133.
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ exports.ledgerProposeCli = ledgerProposeCli;
48
+ exports.ledgerProposeMcp = ledgerProposeMcp;
49
+ exports.ledgerReviewCli = ledgerReviewCli;
50
+ exports.ledgerReviewMcp = ledgerReviewMcp;
51
+ exports.ledgerVerifyCli = ledgerVerifyCli;
52
+ exports.ledgerApplyCli = ledgerApplyCli;
53
+ exports.ledgerListCli = ledgerListCli;
54
+ exports.ledgerVerifyEntry = ledgerVerifyEntry;
55
+ exports.ledgerApplyEntry = ledgerApplyEntry;
56
+ exports.ledgerListMcp = ledgerListMcp;
57
+ const fs = __importStar(require("node:fs"));
58
+ const ledger_1 = require("../core/trust/ledger");
59
+ const ledger_io_1 = require("./ledger-io");
60
+ /** Coerce a repeatable/comma-joined list option to a clean string[]. */
61
+ function listOption(value) {
62
+ const parts = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : [];
63
+ return parts.map((p) => String(p).trim()).filter(Boolean);
64
+ }
65
+ function stringOption(value) {
66
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
67
+ }
68
+ function required(value, label) {
69
+ if (!value)
70
+ throw new Error(`${label} is required`);
71
+ return value;
72
+ }
73
+ function nowIso() {
74
+ return new Date().toISOString();
75
+ }
76
+ function ledgerProposeCli(options) {
77
+ return (0, ledger_1.buildLedgerProposal)({
78
+ from: required(stringOption(options.from), "--from <agent/repo>"),
79
+ to: required(stringOption(options.to), "--to <agent/repo>"),
80
+ title: required(stringOption(options.title), "--title <text>"),
81
+ rationale: required(stringOption(options.rationale), "--rationale <text>"),
82
+ targetFiles: listOption(options.files),
83
+ // Do NOT trim the diff: it is a unified patch (payload, not a
84
+ // label), and trimming strips the trailing newline `git apply`
85
+ // requires. Presence is detected with a trimmed test, but the bytes
86
+ // are passed through verbatim.
87
+ suggestedDiff: typeof options.diff === "string" && options.diff.trim() ? options.diff : undefined,
88
+ createdAt: nowIso(),
89
+ });
90
+ }
91
+ /** MCP-facing `cw_ledger_propose`: `files` is a comma string; `diff` is
92
+ * passed through as-is (undefined only when the arg itself is absent),
93
+ * a slightly looser shape than the CLI handler's flag-labeled
94
+ * requireds (byte-exact to the old build's src/mcp/tool-call.ts). */
95
+ function ledgerProposeMcp(args) {
96
+ return (0, ledger_1.buildLedgerProposal)({
97
+ from: String(args.from || ""),
98
+ to: String(args.to || ""),
99
+ title: String(args.title || ""),
100
+ rationale: String(args.rationale || ""),
101
+ targetFiles: String(args.files || "").split(",").map((f) => f.trim()).filter(Boolean),
102
+ suggestedDiff: args.diff === undefined ? undefined : String(args.diff),
103
+ createdAt: nowIso(),
104
+ });
105
+ }
106
+ function ledgerReviewCli(options) {
107
+ const verdictRaw = required(stringOption(options.verdict), "--verdict <approved|rejected>").toUpperCase();
108
+ if (verdictRaw !== "APPROVED" && verdictRaw !== "REJECTED") {
109
+ throw new Error('--verdict must be "approved" or "rejected".');
110
+ }
111
+ return (0, ledger_1.buildLedgerReview)({
112
+ from: required(stringOption(options.from), "--from <agent/repo>"),
113
+ to: required(stringOption(options.to), "--to <agent/repo>"),
114
+ target: required(stringOption(options.target), "--target <proposal-id|pr-ref>"),
115
+ verdict: verdictRaw,
116
+ findings: listOption(options.findings),
117
+ createdAt: nowIso(),
118
+ });
119
+ }
120
+ /** MCP-facing `cw_ledger_review`: same verdict check, but WITHOUT the
121
+ * `--verdict` CLI-flag framing in the error message (byte-exact to the
122
+ * old build's src/mcp/tool-call.ts, a separate call site from the CLI
123
+ * handler's own message). */
124
+ function ledgerReviewMcp(args) {
125
+ const verdict = String(args.verdict || "").toUpperCase();
126
+ if (verdict !== "APPROVED" && verdict !== "REJECTED") {
127
+ throw new Error('verdict must be "approved" or "rejected".');
128
+ }
129
+ return (0, ledger_1.buildLedgerReview)({
130
+ from: String(args.from || ""),
131
+ to: String(args.to || ""),
132
+ target: String(args.target || ""),
133
+ verdict: verdict,
134
+ findings: String(args.findings || "").split(",").map((f) => f.trim()).filter(Boolean),
135
+ createdAt: nowIso(),
136
+ });
137
+ }
138
+ const BAD_JSON_VERIFY = {
139
+ ok: false,
140
+ id: null,
141
+ kind: null,
142
+ checks: [{ name: "parse", pass: false, code: "ledger-bad-json" }],
143
+ failedChecks: [{ name: "parse", code: "ledger-bad-json" }],
144
+ };
145
+ const BAD_JSON_APPLY = {
146
+ ok: false,
147
+ id: null,
148
+ kind: null,
149
+ diff: null,
150
+ failedChecks: [{ name: "parse", code: "ledger-bad-json" }],
151
+ };
152
+ function readLedgerEntryInput(options) {
153
+ const file = stringOption(options.file);
154
+ try {
155
+ // --file <path>, else read the entry from stdin (fd 0).
156
+ return fs.readFileSync(file || 0, "utf8");
157
+ }
158
+ catch (error) {
159
+ throw new Error(`Cannot read ledger entry${file ? ` from ${file}` : " from stdin"}: ${error.message}`);
160
+ }
161
+ }
162
+ function ledgerVerifyCli(options) {
163
+ let text;
164
+ try {
165
+ text = readLedgerEntryInput(options);
166
+ }
167
+ catch (error) {
168
+ throw error;
169
+ }
170
+ let parsed;
171
+ try {
172
+ parsed = JSON.parse(text);
173
+ }
174
+ catch {
175
+ return BAD_JSON_VERIFY;
176
+ }
177
+ return (0, ledger_1.verifyLedgerEntry)(parsed);
178
+ }
179
+ function ledgerApplyCli(options) {
180
+ const text = readLedgerEntryInput(options);
181
+ let parsed;
182
+ try {
183
+ parsed = JSON.parse(text);
184
+ }
185
+ catch {
186
+ return BAD_JSON_APPLY;
187
+ }
188
+ return (0, ledger_1.applyLedgerProposal)(parsed);
189
+ }
190
+ function ledgerListCli(options) {
191
+ const dirs = Array.isArray(options.dir) ? options.dir.map(String).filter(Boolean) : [];
192
+ if (dirs.length > 1)
193
+ return (0, ledger_io_1.unionLedgerEntries)(dirs);
194
+ const dir = required(dirs[0] || stringOption(options.dir), "--dir <ledger-directory>");
195
+ return (0, ledger_io_1.listLedgerEntries)(dir);
196
+ }
197
+ /** MCP-facing verify/apply take the entry OBJECT directly (not a file/
198
+ * stdin path). */
199
+ function ledgerVerifyEntry(entry) {
200
+ return (0, ledger_1.verifyLedgerEntry)(entry);
201
+ }
202
+ function ledgerApplyEntry(entry) {
203
+ return (0, ledger_1.applyLedgerProposal)(entry);
204
+ }
205
+ function ledgerListMcp(args) {
206
+ const dirsArg = args.dirs;
207
+ const dirs = Array.isArray(dirsArg) ? dirsArg.map(String).filter(Boolean) : [];
208
+ if (dirs.length > 1)
209
+ return (0, ledger_io_1.unionLedgerEntries)(dirs);
210
+ const dir = required(dirs[0] || stringOption(args.dir), "dir");
211
+ return (0, ledger_io_1.listLedgerEntries)(dir);
212
+ }