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
@@ -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"; v2/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
+ }