cool-workflow 0.1.97 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +11 -2
  4. package/apps/architecture-review/app.json +1 -1
  5. package/apps/architecture-review-fast/app.json +1 -1
  6. package/apps/end-to-end-golden-path/app.json +1 -1
  7. package/apps/pr-review-fix-ci/app.json +1 -1
  8. package/apps/release-cut/app.json +1 -1
  9. package/apps/research-synthesis/app.json +1 -1
  10. package/dist/cli/dispatch.js +236 -0
  11. package/dist/cli/entry.js +120 -0
  12. package/dist/cli/io.js +21 -4
  13. package/dist/cli/parseargv.js +157 -0
  14. package/dist/cli.js +6 -33
  15. package/dist/core/capability-table.js +3534 -0
  16. package/dist/core/format/help.js +314 -0
  17. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  18. package/dist/core/hash.js +137 -0
  19. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  20. package/dist/core/multi-agent/collaboration.js +481 -0
  21. package/dist/core/multi-agent/coordinator.js +515 -0
  22. package/dist/core/multi-agent/eval-replay.js +306 -0
  23. package/dist/core/multi-agent/runtime.js +929 -0
  24. package/dist/core/multi-agent/topology.js +298 -0
  25. package/dist/core/multi-agent/trust-policy.js +197 -0
  26. package/dist/core/pipeline/commit-gate.js +320 -0
  27. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  28. package/dist/core/pipeline/dispatch.js +103 -0
  29. package/dist/core/pipeline/drive-decide.js +227 -0
  30. package/dist/core/pipeline/error-feedback.js +161 -0
  31. package/dist/core/pipeline/loop-expansion.js +124 -0
  32. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  33. package/dist/core/pipeline/runner.js +230 -0
  34. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  35. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  36. package/dist/core/state/node-projection.js +95 -0
  37. package/dist/core/state/node-snapshot.js +230 -0
  38. package/dist/core/state/run-paths.js +93 -0
  39. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  40. package/dist/core/state/schema.js +50 -0
  41. package/dist/core/state/state-explosion/digest.js +243 -0
  42. package/dist/core/state/state-explosion/graph.js +527 -0
  43. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  44. package/dist/core/state/state-explosion/report.js +187 -0
  45. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  46. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  47. package/dist/core/state/types.js +19 -0
  48. package/dist/{validation.js → core/state/validation.js} +64 -131
  49. package/dist/core/trust/evidence-grounding.js +134 -0
  50. package/dist/core/trust/ledger.js +199 -0
  51. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  52. package/dist/core/trust/telemetry-ledger.js +127 -0
  53. package/dist/core/types.js +20 -0
  54. package/dist/core/version.js +16 -0
  55. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  56. package/dist/mcp/dispatch.js +104 -0
  57. package/dist/mcp/server.js +144 -0
  58. package/dist/mcp-server.js +6 -84
  59. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  60. package/dist/shell/app-run-cli.js +88 -0
  61. package/dist/shell/audit-cli.js +259 -0
  62. package/dist/shell/audit-provenance.js +83 -0
  63. package/dist/shell/candidate-scoring-io.js +459 -0
  64. package/dist/shell/collaboration-io.js +264 -0
  65. package/dist/shell/commit-summary.js +104 -0
  66. package/dist/shell/commit.js +290 -0
  67. package/dist/shell/coordinator-io.js +476 -0
  68. package/dist/shell/demo-cli.js +19 -0
  69. package/dist/shell/dispatch.js +162 -0
  70. package/dist/{doctor.js → shell/doctor.js} +123 -56
  71. package/dist/shell/drive.js +873 -0
  72. package/dist/shell/error-feedback-io.js +305 -0
  73. package/dist/shell/eval-io.js +473 -0
  74. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  75. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  76. package/dist/shell/exec-backend-cli.js +88 -0
  77. package/dist/shell/execution-backend/agent.js +475 -0
  78. package/dist/shell/execution-backend/ci.js +15 -0
  79. package/dist/shell/execution-backend/container.js +69 -0
  80. package/dist/shell/execution-backend/envelopes.js +55 -0
  81. package/dist/shell/execution-backend/local.js +113 -0
  82. package/dist/shell/execution-backend/probes.js +175 -0
  83. package/dist/shell/execution-backend/registry.js +402 -0
  84. package/dist/shell/execution-backend/remote.js +128 -0
  85. package/dist/shell/execution-backend/types.js +11 -0
  86. package/dist/shell/feedback-cli.js +81 -0
  87. package/dist/shell/feedback-operations.js +48 -0
  88. package/dist/shell/fs-atomic.js +276 -0
  89. package/dist/shell/harness.js +98 -0
  90. package/dist/shell/ledger-cli.js +212 -0
  91. package/dist/shell/ledger-io.js +169 -0
  92. package/dist/shell/man-cli.js +89 -0
  93. package/dist/shell/metrics-cli.js +98 -0
  94. package/dist/shell/multi-agent-cli.js +1002 -0
  95. package/dist/shell/multi-agent-host.js +563 -0
  96. package/dist/shell/multi-agent-io.js +387 -0
  97. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  98. package/dist/shell/node-store.js +124 -0
  99. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  100. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  101. package/dist/{observability.js → shell/observability.js} +159 -332
  102. package/dist/{onramp.js → shell/onramp.js} +11 -0
  103. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  104. package/dist/shell/operator-ux.js +431 -0
  105. package/dist/shell/orchestrator.js +231 -0
  106. package/dist/shell/pipeline-cli.js +667 -0
  107. package/dist/shell/pipeline.js +217 -0
  108. package/dist/shell/reclamation-io.js +1366 -0
  109. package/dist/shell/registry-cli.js +329 -0
  110. package/dist/{remote-source.js → shell/remote-source.js} +12 -5
  111. package/dist/shell/report-cli.js +101 -0
  112. package/dist/shell/report-view-cli.js +117 -0
  113. package/dist/{orchestrator → shell}/report.js +289 -282
  114. package/dist/shell/reporter.js +62 -0
  115. package/dist/shell/run-export-cli.js +106 -0
  116. package/dist/shell/run-export.js +680 -0
  117. package/dist/shell/run-registry-io.js +1014 -0
  118. package/dist/shell/run-store.js +164 -0
  119. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  120. package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
  121. package/dist/shell/scheduling-io.js +311 -0
  122. package/dist/shell/state-cli.js +181 -0
  123. package/dist/shell/state-explosion-cli.js +197 -0
  124. package/dist/shell/telemetry-cli.js +85 -0
  125. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  126. package/dist/shell/telemetry-ledger-io.js +132 -0
  127. package/dist/{term.js → shell/term.js} +36 -46
  128. package/dist/shell/topology-io.js +361 -0
  129. package/dist/shell/trust-audit.js +471 -0
  130. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  131. package/dist/shell/verifier.js +48 -0
  132. package/dist/shell/workbench-host.js +250 -0
  133. package/dist/shell/workbench-text.js +18 -0
  134. package/dist/shell/workbench.js +175 -0
  135. package/dist/shell/worker-cli.js +124 -0
  136. package/dist/shell/worker-isolation.js +852 -0
  137. package/dist/shell/workflow-app-loader.js +650 -0
  138. package/docs/agent-delegation-drive.7.md +4 -0
  139. package/docs/cli-mcp-parity.7.md +284 -211
  140. package/docs/contract-migration-tooling.7.md +4 -0
  141. package/docs/control-plane-scheduling.7.md +4 -0
  142. package/docs/cross-agent-ledger.7.md +217 -0
  143. package/docs/designs/handoff-ledger.md +145 -0
  144. package/docs/durable-state-and-locking.7.md +4 -0
  145. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  146. package/docs/execution-backends.7.md +4 -0
  147. package/docs/handoff-setup.md +120 -0
  148. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  149. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  150. package/docs/multi-agent-operator-ux.7.md +4 -0
  151. package/docs/node-snapshot-diff-replay.7.md +4 -0
  152. package/docs/observability-cost-accounting.7.md +4 -0
  153. package/docs/project-index.md +143 -71
  154. package/docs/real-execution-backends.7.md +4 -0
  155. package/docs/release-and-migration.7.md +4 -0
  156. package/docs/release-tooling.7.md +4 -0
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/state-explosion-management.7.md +4 -0
  160. package/docs/team-collaboration.7.md +4 -0
  161. package/docs/web-desktop-workbench.7.md +4 -0
  162. package/manifest/plugin.manifest.json +1 -1
  163. package/manifest/source-context-profiles.json +9 -13
  164. package/package.json +1 -1
  165. package/scripts/agents/codex-agent.js +34 -4
  166. package/scripts/agents/cw-attest-wrap.js +2 -2
  167. package/scripts/bump-version.js +4 -3
  168. package/scripts/canonical-apps.js +4 -4
  169. package/scripts/children/batch-delegate-child.js +30 -10
  170. package/scripts/dogfood-architecture-review.js +2 -3
  171. package/scripts/dogfood-release.js +3 -3
  172. package/scripts/gen-parity-doc.js +15 -2
  173. package/scripts/golden-path.js +4 -4
  174. package/scripts/onramp-check.js +1 -1
  175. package/scripts/parity-check.js +38 -21
  176. package/scripts/release-flow.js +9 -3
  177. package/scripts/sync-project-index.js +51 -27
  178. package/scripts/validate-run-state-schema.js +2 -2
  179. package/scripts/version-sync-check.js +30 -30
  180. package/dist/candidate-scoring.js +0 -729
  181. package/dist/capability-core.js +0 -1186
  182. package/dist/capability-registry.js +0 -879
  183. package/dist/cli/command-surface.js +0 -490
  184. package/dist/cli/format.js +0 -56
  185. package/dist/cli/handlers/audit.js +0 -82
  186. package/dist/cli/handlers/blackboard.js +0 -81
  187. package/dist/cli/handlers/candidate.js +0 -40
  188. package/dist/cli/handlers/clones.js +0 -34
  189. package/dist/cli/handlers/collaboration.js +0 -61
  190. package/dist/cli/handlers/eval.js +0 -40
  191. package/dist/cli/handlers/maintenance.js +0 -107
  192. package/dist/cli/handlers/multi-agent.js +0 -165
  193. package/dist/cli/handlers/node.js +0 -41
  194. package/dist/cli/handlers/operational.js +0 -155
  195. package/dist/cli/handlers/operator.js +0 -146
  196. package/dist/cli/handlers/registry.js +0 -68
  197. package/dist/cli/handlers/run.js +0 -153
  198. package/dist/cli/handlers/scheduling.js +0 -132
  199. package/dist/cli/handlers/workbench.js +0 -41
  200. package/dist/cli/handlers/worker.js +0 -45
  201. package/dist/cli/run-summary.js +0 -45
  202. package/dist/clones.js +0 -162
  203. package/dist/collaboration.js +0 -726
  204. package/dist/commit.js +0 -592
  205. package/dist/compare.js +0 -18
  206. package/dist/coordinator/classify.js +0 -45
  207. package/dist/coordinator/paths.js +0 -42
  208. package/dist/coordinator/util.js +0 -126
  209. package/dist/coordinator.js +0 -990
  210. package/dist/daemon.js +0 -44
  211. package/dist/dispatch.js +0 -250
  212. package/dist/drive.js +0 -827
  213. package/dist/error-feedback.js +0 -419
  214. package/dist/evidence-grounding.js +0 -184
  215. package/dist/execution-backend/agent.js +0 -294
  216. package/dist/execution-backend/probes.js +0 -112
  217. package/dist/execution-backend/util.js +0 -47
  218. package/dist/execution-backend.js +0 -961
  219. package/dist/gates.js +0 -48
  220. package/dist/harness.js +0 -61
  221. package/dist/loop-expansion.js +0 -60
  222. package/dist/mcp/tool-call.js +0 -434
  223. package/dist/mcp/tool-definitions.js +0 -1040
  224. package/dist/mcp-surface.js +0 -30
  225. package/dist/multi-agent/graph.js +0 -84
  226. package/dist/multi-agent/helpers.js +0 -141
  227. package/dist/multi-agent/ids.js +0 -20
  228. package/dist/multi-agent/paths.js +0 -22
  229. package/dist/multi-agent-eval/normalize.js +0 -51
  230. package/dist/multi-agent-eval.js +0 -678
  231. package/dist/multi-agent-host.js +0 -777
  232. package/dist/multi-agent.js +0 -984
  233. package/dist/node-projection.js +0 -59
  234. package/dist/node-snapshot.js +0 -260
  235. package/dist/operator-ux.js +0 -631
  236. package/dist/orchestrator/app-operations.js +0 -211
  237. package/dist/orchestrator/audit-operations.js +0 -182
  238. package/dist/orchestrator/candidate-operations.js +0 -117
  239. package/dist/orchestrator/cli-options.js +0 -294
  240. package/dist/orchestrator/collaboration-operations.js +0 -86
  241. package/dist/orchestrator/feedback-operations.js +0 -81
  242. package/dist/orchestrator/host-operations.js +0 -78
  243. package/dist/orchestrator/lifecycle-operations.js +0 -626
  244. package/dist/orchestrator/migration-operations.js +0 -44
  245. package/dist/orchestrator/multi-agent-operations.js +0 -362
  246. package/dist/orchestrator/topology-operations.js +0 -84
  247. package/dist/orchestrator.js +0 -917
  248. package/dist/pipeline-runner.js +0 -285
  249. package/dist/reclamation/hash.js +0 -72
  250. package/dist/reclamation.js +0 -812
  251. package/dist/reporter.js +0 -67
  252. package/dist/run-export.js +0 -784
  253. package/dist/run-registry/derive.js +0 -175
  254. package/dist/run-registry/format.js +0 -124
  255. package/dist/run-registry/gc.js +0 -251
  256. package/dist/run-registry/policy.js +0 -16
  257. package/dist/run-registry/queue.js +0 -115
  258. package/dist/run-registry.js +0 -850
  259. package/dist/run-state-schema.js +0 -68
  260. package/dist/scheduling.js +0 -184
  261. package/dist/state-explosion.js +0 -1014
  262. package/dist/state.js +0 -367
  263. package/dist/telemetry-ledger.js +0 -196
  264. package/dist/topology.js +0 -565
  265. package/dist/triggers.js +0 -184
  266. package/dist/trust-audit.js +0 -644
  267. package/dist/types/blackboard.js +0 -2
  268. package/dist/types/candidate.js +0 -2
  269. package/dist/types/collaboration.js +0 -2
  270. package/dist/types/core.js +0 -2
  271. package/dist/types/drive.js +0 -10
  272. package/dist/types/error-feedback.js +0 -2
  273. package/dist/types/evidence-reasoning.js +0 -2
  274. package/dist/types/execution-backend.js +0 -2
  275. package/dist/types/multi-agent.js +0 -2
  276. package/dist/types/observability.js +0 -2
  277. package/dist/types/pipeline.js +0 -2
  278. package/dist/types/reclamation.js +0 -8
  279. package/dist/types/report-bundle.js +0 -6
  280. package/dist/types/result.js +0 -2
  281. package/dist/types/run-registry.js +0 -2
  282. package/dist/types/run.js +0 -2
  283. package/dist/types/sandbox.js +0 -2
  284. package/dist/types/schedule.js +0 -2
  285. package/dist/types/state-node.js +0 -2
  286. package/dist/types/topology.js +0 -2
  287. package/dist/types/trust.js +0 -2
  288. package/dist/types/workbench.js +0 -2
  289. package/dist/types/worker.js +0 -2
  290. package/dist/types/workflow-app.js +0 -2
  291. package/dist/types.js +0 -44
  292. package/dist/util/fingerprint.js +0 -19
  293. package/dist/util/fingerprint.test.js +0 -27
  294. package/dist/verifier.js +0 -78
  295. package/dist/version.js +0 -8
  296. package/dist/workbench-host.js +0 -182
  297. package/dist/workbench.js +0 -192
  298. package/dist/worker-accept/acceptance.js +0 -114
  299. package/dist/worker-accept/blackboard-fanout.js +0 -80
  300. package/dist/worker-accept/blackboard-linkage.js +0 -19
  301. package/dist/worker-accept/context.js +0 -2
  302. package/dist/worker-accept/telemetry-ledger.js +0 -126
  303. package/dist/worker-accept/validation.js +0 -77
  304. package/dist/worker-accept/verifier-completion.js +0 -73
  305. package/dist/worker-isolation/helpers.js +0 -51
  306. package/dist/worker-isolation/paths.js +0 -46
  307. package/dist/worker-isolation.js +0 -656
  308. package/dist/workflow-api.js +0 -131
  309. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,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
+ }