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
@@ -1,122 +1,56 @@
1
1
  "use strict";
2
+ // shell/trust-policy-io.ts — the impure wrapper around core/multi-agent/
3
+ // trust-policy.ts's pure policy evaluation: authorizeMultiAgentAction
4
+ // (records the permission + policy.violation audit events),
5
+ // recordBlackboardWriteAudit, recordMessageProvenanceAudit,
6
+ // recordJudgeRationaleAudit, summarizeMultiAgentTrust,
7
+ // hasAcceptedJudgeRationale.
8
+ //
9
+ // MILESTONE 9. Byte-exact port of the audit-recording half of the old
10
+ // build's src/multi-agent-trust.ts.
11
+ //
12
+ // Evidence: SPEC/multi-agent.md section D ("Trust policies"), "Trust
13
+ // denial reasons"; plugins/cool-workflow/src/multi-agent-trust.ts:129-415
14
+ // (byte-exact source).
2
15
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.policyForRole = policyForRole;
4
- exports.policyForGroup = policyForGroup;
5
- exports.policyForMembership = policyForMembership;
6
- exports.recordRolePolicyAudit = recordRolePolicyAudit;
16
+ exports.policyForRole = exports.policyForMembership = exports.policyForGroup = void 0;
7
17
  exports.authorizeMultiAgentAction = authorizeMultiAgentAction;
8
18
  exports.assertMultiAgentActionAllowed = assertMultiAgentActionAllowed;
9
19
  exports.recordBlackboardWriteAudit = recordBlackboardWriteAudit;
20
+ exports.hashText = hashText;
10
21
  exports.recordMessageProvenanceAudit = recordMessageProvenanceAudit;
11
22
  exports.recordJudgeRationaleAudit = recordJudgeRationaleAudit;
12
23
  exports.summarizeMultiAgentTrust = summarizeMultiAgentTrust;
13
24
  exports.hasAcceptedJudgeRationale = hasAcceptedJudgeRationale;
14
- exports.sourceForActor = sourceForActor;
15
- exports.hashText = hashText;
16
- const execution_backend_1 = require("./execution-backend");
25
+ exports.recordRolePolicyAudit = recordRolePolicyAudit;
26
+ const runtime_1 = require("../core/multi-agent/runtime");
27
+ const trust_policy_1 = require("../core/multi-agent/trust-policy");
17
28
  const trust_audit_1 = require("./trust-audit");
18
- function policyForRole(role) {
19
- const topologyRole = String(role.metadata?.topologyRoleId || role.title || "").toLowerCase();
20
- const isChair = topologyRole.includes("chair") || topologyRole.includes("reducer") || topologyRole.includes("synthesizer");
21
- const isJudge = topologyRole.includes("judge");
22
- return {
23
- schemaVersion: 1,
24
- id: `${role.id}-policy`,
25
- policyRef: `multiAgent.roles.${role.id}.policy`,
26
- subjectKind: "role",
27
- subjectId: role.id,
28
- allowedBlackboardTopicIds: unique(role.topicIds || ["*"]),
29
- allowedWriteOperations: unique([
30
- "message",
31
- "context",
32
- "artifact",
33
- ...(isChair ? ["snapshot", "coordinator-decision"] : [])
34
- ]),
35
- allowedCandidateOperations: isChair ? ["score", "select"] : ["score"],
36
- allowedJudgeOperations: unique([
37
- ...(isJudge ? ["verdict", "rationale"] : []),
38
- ...(isChair ? ["rationale", "panel-decision"] : [])
39
- ]),
40
- sandboxProfileHints: unique(role.sandboxProfileHints || []),
41
- requiredEvidenceRefs: unique(role.requiredEvidence || []),
42
- requiredEvidenceFor: {
43
- "judge.rationale": ["judge rationale evidence"],
44
- "judge.verdict": ["judge verdict evidence"],
45
- "judge.panel-decision": ["judge messages", "score evidence", "coordinator decision"],
46
- "candidate.select": ["score evidence", "judge rationale"]
47
- },
48
- deniedOperations: [],
49
- metadata: { title: role.title, topologyRoleId: role.metadata?.topologyRoleId }
50
- };
51
- }
52
- function policyForGroup(group) {
53
- return {
54
- schemaVersion: 1,
55
- id: `${group.id}-policy`,
56
- policyRef: `multiAgent.groups.${group.id}.policy`,
57
- subjectKind: "group",
58
- subjectId: group.id,
59
- allowedBlackboardTopicIds: unique(group.topicIds || ["*"]),
60
- allowedWriteOperations: ["message", "context", "artifact", "snapshot", "coordinator-decision"],
61
- allowedCandidateOperations: ["register", "score", "select"],
62
- allowedJudgeOperations: ["verdict", "rationale", "panel-decision"],
63
- sandboxProfileHints: [],
64
- requiredEvidenceRefs: [],
65
- deniedOperations: [],
66
- metadata: { phase: group.phase }
67
- };
68
- }
69
- function policyForMembership(membership, role) {
70
- const source = role?.policy || (role ? policyForRole(role) : undefined);
71
- return {
72
- ...(source || {
73
- schemaVersion: 1,
74
- id: `${membership.id}-policy`,
75
- policyRef: `multiAgent.memberships.${membership.id}.policy`,
76
- subjectKind: "membership",
77
- subjectId: membership.id,
78
- allowedBlackboardTopicIds: unique(membership.topicIds || ["*"]),
79
- allowedWriteOperations: ["message", "context", "artifact"],
80
- allowedCandidateOperations: [],
81
- allowedJudgeOperations: [],
82
- sandboxProfileHints: [],
83
- requiredEvidenceRefs: [],
84
- deniedOperations: []
85
- }),
86
- id: `${membership.id}-policy`,
87
- policyRef: `multiAgent.memberships.${membership.id}.policy`,
88
- subjectKind: "membership",
89
- subjectId: membership.id
90
- };
91
- }
92
- function recordRolePolicyAudit(run, role) {
93
- return (0, trust_audit_1.recordTrustAuditEvent)(run, {
94
- kind: "multi-agent.role-policy",
95
- decision: "recorded",
96
- source: "runtime-derived",
97
- multiAgentRunId: role.multiAgentRunId,
98
- agentRoleId: role.id,
99
- blackboardId: role.blackboardId,
100
- policyRef: role.policy?.policyRef,
101
- metadata: role.policy
102
- });
29
+ const hash_1 = require("../core/hash");
30
+ function policyRunId(run, roleId, groupId, membershipId) {
31
+ const membership = membershipId ? (0, runtime_1.getAgentMembership)(run, membershipId) : undefined;
32
+ if (membership)
33
+ return membership.multiAgentRunId;
34
+ const role = roleId ? (0, runtime_1.getAgentRole)(run, roleId) : undefined;
35
+ if (role)
36
+ return role.multiAgentRunId;
37
+ const group = groupId ? (0, runtime_1.getAgentGroup)(run, groupId) : undefined;
38
+ return group?.multiAgentRunId;
103
39
  }
104
40
  function authorizeMultiAgentAction(run, input) {
105
41
  const roleId = input.agentRoleId || (input.actor?.kind === "role" ? input.actor.id : undefined);
106
42
  const membershipId = input.agentMembershipId || (input.actor?.kind === "membership" ? input.actor.id : undefined);
107
43
  const groupId = input.agentGroupId || (input.actor?.kind === "group" ? input.actor.id : undefined);
108
- const policy = resolvePolicy(run, { roleId, membershipId, groupId });
109
- const reason = evaluatePolicy(policy, input.operation, input.blackboardTopicId, input.evidenceRefs || []);
110
- const allowed = !reason;
111
- const metadata = {
112
- operation: input.operation,
113
- reason: reason || "allowed by explicit multi-agent policy",
114
- policyRef: policy?.policyRef,
115
- ...(input.metadata || {})
116
- };
44
+ const policy = (0, trust_policy_1.resolvePolicy)({ roleId, membershipId, groupId }, {
45
+ membership: (id) => (0, runtime_1.getAgentMembership)(run, id),
46
+ role: (id) => (0, runtime_1.getAgentRole)(run, id),
47
+ group: (id) => (0, runtime_1.getAgentGroup)(run, id),
48
+ });
49
+ const core = (0, trust_policy_1.evaluateMultiAgentAction)(policy, input.operation, input.blackboardTopicId, input.evidenceRefs || []);
50
+ const metadata = { operation: input.operation, reason: core.reason, policyRef: policy?.policyRef, ...(input.metadata || {}) };
117
51
  const event = (0, trust_audit_1.recordTrustAuditEvent)(run, {
118
52
  kind: "multi-agent.permission",
119
- decision: allowed ? "allowed" : "denied",
53
+ decision: core.allowed ? "allowed" : "denied",
120
54
  source: "cw-validated",
121
55
  actor: input.actor?.id,
122
56
  multiAgentRunId: input.multiAgentRunId || policyRunId(run, roleId, groupId, membershipId),
@@ -140,9 +74,9 @@ function authorizeMultiAgentAction(run, input) {
140
74
  evidence: input.evidence,
141
75
  evidenceRefs: input.evidenceRefs,
142
76
  policyRef: policy?.policyRef,
143
- metadata
77
+ metadata,
144
78
  });
145
- if (!allowed) {
79
+ if (!core.allowed) {
146
80
  (0, trust_audit_1.recordTrustAuditEvent)(run, {
147
81
  kind: "policy.violation",
148
82
  decision: "denied",
@@ -159,18 +93,10 @@ function authorizeMultiAgentAction(run, input) {
159
93
  evidenceRefs: input.evidenceRefs,
160
94
  parentEventIds: [event.id],
161
95
  policyRef: policy?.policyRef,
162
- metadata
96
+ metadata,
163
97
  });
164
98
  }
165
- return {
166
- allowed,
167
- decision: allowed ? "allowed" : "denied",
168
- reason: reason || "allowed by explicit multi-agent policy",
169
- policyRef: policy?.policyRef,
170
- policy,
171
- missingEvidenceRefs: missingEvidence(policy, input.operation, input.evidenceRefs || []),
172
- event
173
- };
99
+ return { allowed: core.allowed, decision: core.decision, reason: core.reason, policyRef: core.policyRef, policy: core.policy, missingEvidenceRefs: core.missingEvidenceRefs, event };
174
100
  }
175
101
  function assertMultiAgentActionAllowed(run, input) {
176
102
  const decision = authorizeMultiAgentAction(run, input);
@@ -178,6 +104,17 @@ function assertMultiAgentActionAllowed(run, input) {
178
104
  throw new Error(decision.reason);
179
105
  return decision;
180
106
  }
107
+ function sourceForActor(actor) {
108
+ if (!actor)
109
+ return "operator-recorded";
110
+ if (actor.kind === "worker")
111
+ return "host-attested";
112
+ if (actor.kind === "operator")
113
+ return "operator-recorded";
114
+ if (actor.kind === "runtime" || actor.kind === "coordinator" || actor.kind === "verifier")
115
+ return "runtime-derived";
116
+ return "cw-validated";
117
+ }
181
118
  function recordBlackboardWriteAudit(run, input) {
182
119
  return (0, trust_audit_1.recordTrustAuditEvent)(run, {
183
120
  kind: "blackboard.write",
@@ -200,9 +137,12 @@ function recordBlackboardWriteAudit(run, input) {
200
137
  evidenceRefs: input.evidenceRefs,
201
138
  parentEventIds: input.parentEventIds,
202
139
  policyRef: input.policyRef,
203
- metadata: { operation: input.operation, status: input.status, ...(input.metadata || {}) }
140
+ metadata: { operation: input.operation, status: input.status, ...(input.metadata || {}) },
204
141
  });
205
142
  }
143
+ function hashText(value) {
144
+ return (0, hash_1.sha256)(value);
145
+ }
206
146
  function recordMessageProvenanceAudit(run, input) {
207
147
  return (0, trust_audit_1.recordTrustAuditEvent)(run, {
208
148
  kind: "blackboard.message-provenance",
@@ -226,8 +166,8 @@ function recordMessageProvenanceAudit(run, input) {
226
166
  summary: input.body.trim().slice(0, 120),
227
167
  parentMessageIds: input.parentMessageIds || [],
228
168
  topicScope: input.topicId,
229
- locator: `${input.blackboardId}/messages/${input.messageId}`
230
- }
169
+ locator: `${input.blackboardId}/messages/${input.messageId}`,
170
+ },
231
171
  });
232
172
  }
233
173
  function recordJudgeRationaleAudit(run, input) {
@@ -250,12 +190,13 @@ function recordJudgeRationaleAudit(run, input) {
250
190
  evidenceRefs: input.evidenceRefs,
251
191
  parentEventIds: input.parentEventIds,
252
192
  policyRef: input.policyRef,
253
- metadata: { rationale: input.rationale?.slice(0, 240) }
193
+ metadata: { rationale: input.rationale?.slice(0, 240) },
254
194
  });
255
195
  }
256
196
  function summarizeMultiAgentTrust(run) {
257
197
  const events = (0, trust_audit_1.listTrustAuditEvents)(run);
258
- const rolePolicies = (run.multiAgent?.roles || []).map((role) => role.policy || policyForRole(role));
198
+ const roles = (run.multiAgent?.roles || []);
199
+ const rolePolicies = roles.map((role) => role.policy || (0, trust_policy_1.policyForRole)(role));
259
200
  const byKind = (kind) => events.filter((event) => event.kind === kind);
260
201
  const policyViolations = byKind("policy.violation");
261
202
  return {
@@ -268,9 +209,7 @@ function summarizeMultiAgentTrust(run) {
268
209
  judgeRationales: byKind("judge.rationale"),
269
210
  panelDecisions: byKind("judge.panel-decision"),
270
211
  policyViolations,
271
- nextAction: policyViolations.length
272
- ? `node scripts/cw.js audit policy ${run.id}`
273
- : `node scripts/cw.js audit multi-agent ${run.id} --json`
212
+ nextAction: policyViolations.length ? `node scripts/cw.js audit policy ${run.id}` : `node scripts/cw.js audit multi-agent ${run.id} --json`,
274
213
  };
275
214
  }
276
215
  function hasAcceptedJudgeRationale(run, input = {}) {
@@ -280,87 +219,10 @@ function hasAcceptedJudgeRationale(run, input = {}) {
280
219
  (!input.candidateId || event.candidateId === input.candidateId) &&
281
220
  (!input.scoreId || !event.scoreId || event.scoreId === input.scoreId));
282
221
  }
283
- function sourceForActor(actor) {
284
- if (!actor)
285
- return "operator-recorded";
286
- if (actor.kind === "worker")
287
- return "host-attested";
288
- if (actor.kind === "operator")
289
- return "operator-recorded";
290
- if (actor.kind === "runtime" || actor.kind === "coordinator" || actor.kind === "verifier")
291
- return "runtime-derived";
292
- return "cw-validated";
293
- }
294
- // Delegates to the shared execution-backend sha256 (F10 dedup). Byte-identical:
295
- // both emit `sha256:<hex>` and Node's Hash.update(string) defaults to utf8, the
296
- // same encoding the shared helper passes explicitly.
297
- function hashText(value) {
298
- return (0, execution_backend_1.sha256)(value);
299
- }
300
- function resolvePolicy(run, input) {
301
- const membership = input.membershipId ? run.multiAgent?.memberships.find((entry) => entry.id === input.membershipId) : undefined;
302
- if (membership?.policy)
303
- return membership.policy;
304
- const roleId = input.roleId || membership?.roleId;
305
- const role = roleId ? run.multiAgent?.roles.find((entry) => entry.id === roleId) : undefined;
306
- if (role?.policy)
307
- return role.policy;
308
- if (role)
309
- return policyForRole(role);
310
- const group = input.groupId ? run.multiAgent?.groups.find((entry) => entry.id === input.groupId) : undefined;
311
- if (group?.policy)
312
- return group.policy;
313
- if (group)
314
- return policyForGroup(group);
315
- return undefined;
316
- }
317
- function evaluatePolicy(policy, operation, topicId, evidenceRefs) {
318
- if (!policy)
319
- return "missing role authority or policy";
320
- const denied = policy.deniedOperations.find((entry) => entry.operation === operation);
321
- if (denied)
322
- return denied.reason;
323
- if (topicId && policy.allowedBlackboardTopicIds.length && !policy.allowedBlackboardTopicIds.includes("*") && !policy.allowedBlackboardTopicIds.includes(topicId)) {
324
- return `topic ${topicId} is outside policy ${policy.policyRef}`;
325
- }
326
- if (operation.startsWith("candidate.")) {
327
- const op = operation.slice("candidate.".length);
328
- if (!policy.allowedCandidateOperations.includes(op))
329
- return `candidate operation ${op} is outside policy ${policy.policyRef}`;
330
- }
331
- else if (operation.startsWith("judge.")) {
332
- const op = operation.slice("judge.".length);
333
- if (!policy.allowedJudgeOperations.includes(op))
334
- return `judge operation ${op} is outside policy ${policy.policyRef}`;
335
- }
336
- else if (!policy.allowedWriteOperations.includes(operation)) {
337
- return `blackboard write operation ${operation} is outside policy ${policy.policyRef}`;
338
- }
339
- const missing = missingEvidence(policy, operation, evidenceRefs);
340
- if (missing.length)
341
- return `operation ${operation} requires evidence refs: ${missing.join(", ")}`;
342
- return undefined;
343
- }
344
- function missingEvidence(policy, operation, evidenceRefs) {
345
- if (!policy)
346
- return [];
347
- const required = unique([...(policy.requiredEvidenceFor?.[operation] || [])]);
348
- if (!required.length)
349
- return [];
350
- if (evidenceRefs.length)
351
- return [];
352
- return required;
353
- }
354
- function policyRunId(run, roleId, groupId, membershipId) {
355
- const membership = membershipId ? run.multiAgent?.memberships.find((entry) => entry.id === membershipId) : undefined;
356
- if (membership)
357
- return membership.multiAgentRunId;
358
- const role = roleId ? run.multiAgent?.roles.find((entry) => entry.id === roleId) : undefined;
359
- if (role)
360
- return role.multiAgentRunId;
361
- const group = groupId ? run.multiAgent?.groups.find((entry) => entry.id === groupId) : undefined;
362
- return group?.multiAgentRunId;
363
- }
364
- function unique(values) {
365
- return Array.from(new Set(values.filter(Boolean)));
222
+ function recordRolePolicyAudit(run, role) {
223
+ return (0, trust_audit_1.recordTrustAuditEvent)(run, { kind: "multi-agent.role-policy", decision: "recorded", source: "runtime-derived", multiAgentRunId: role.multiAgentRunId, agentRoleId: role.id, blackboardId: role.blackboardId, policyRef: role.policy?.policyRef, metadata: role.policy });
366
224
  }
225
+ var trust_policy_2 = require("../core/multi-agent/trust-policy");
226
+ Object.defineProperty(exports, "policyForGroup", { enumerable: true, get: function () { return trust_policy_2.policyForGroup; } });
227
+ Object.defineProperty(exports, "policyForMembership", { enumerable: true, get: function () { return trust_policy_2.policyForMembership; } });
228
+ Object.defineProperty(exports, "policyForRole", { enumerable: true, get: function () { return trust_policy_2.policyForRole; } });
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ // shell/verifier.ts — taskRequiresEvidence, the small task-level gate the
3
+ // worker-accept path and the commit gate both consult.
4
+ //
5
+ // MILESTONE 6+7 (combined). Byte-exact port of the old build's
6
+ // src/verifier.ts's `taskRequiresEvidence`. Pure logic; kept under
7
+ // shell/ only because its caller (worker-isolation.ts) is shell-side —
8
+ // the function itself takes no fs/clock/random input.
9
+ //
10
+ // Evidence: SPEC/pipeline-run.md "Commit gate" (verifierNodeRequiresEvidence
11
+ // reuses this exact predicate).
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.taskRequiresEvidence = taskRequiresEvidence;
14
+ exports.validateResultEnvelope = validateResultEnvelope;
15
+ const evidence_grounding_1 = require("../core/trust/evidence-grounding");
16
+ const schema_validate_1 = require("../core/state/schema-validate");
17
+ function taskRequiresEvidence(task) {
18
+ return Boolean(task.requiresEvidence || /^verify[:/]/i.test(task.id) || /^verdict[:/]/i.test(task.id) || /^synthesis[:/]/i.test(task.id));
19
+ }
20
+ function validateFinding(task, finding) {
21
+ if (!finding.id)
22
+ throw new Error(`Task ${task.id} has a finding without id`);
23
+ if (finding.classification && !["real", "conditional", "non-issue", "unknown"].includes(finding.classification)) {
24
+ throw new Error(`Task ${task.id} finding ${finding.id} has invalid classification`);
25
+ }
26
+ if (["P0", "P1", "P2"].includes(finding.severity || "") && !(0, evidence_grounding_1.hasGroundedEvidence)(finding.evidence)) {
27
+ throw new Error(`Task ${task.id} finding ${finding.id} severity ${finding.severity} requires grounded evidence (a path-like locator, URL, or namespace:value token)`);
28
+ }
29
+ }
30
+ /** Validate an accepted result envelope against the task's declared contract:
31
+ * required grounded evidence, per-finding shape, and (opt-in) the task's
32
+ * declared output schema. Throws on violation (fail-closed — the drive parks
33
+ * the hop). Byte-behavior port of the old build's src/verifier.ts. */
34
+ function validateResultEnvelope(task, result) {
35
+ if (taskRequiresEvidence(task) && !(0, evidence_grounding_1.hasGroundedEvidence)(result.evidence)) {
36
+ throw new Error(`Task ${task.id} requires grounded cw:result evidence (a path-like locator, URL, or namespace:value token — not free text)`);
37
+ }
38
+ for (const finding of result.findings || []) {
39
+ validateFinding(task, finding);
40
+ }
41
+ const schema = task.schema;
42
+ if (schema) {
43
+ const violations = (0, schema_validate_1.validateAgainstSchema)(result, schema);
44
+ if (violations.length) {
45
+ throw new Error(`Task ${task.id} result violates declared schema: ${violations.slice(0, 5).join("; ")}${violations.length > 5 ? ` (+${violations.length - 5} more)` : ""}`);
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ // shell/workbench-host.ts — the localhost-only, read-only Workbench HTTP
3
+ // server: `cw workbench serve`.
4
+ //
5
+ // MILESTONE 11 (reporting/observability). Byte-exact port of the old
6
+ // build's src/workbench-host.ts's HTTP behavior: loopback bind only,
7
+ // GET-only, non-localhost Host header refused, optional bearer-token
8
+ // auth (timing-safe compare), pretty (2-space) JSON responses with
9
+ // `Cache-Control: no-store` + `X-Content-Type-Options: nosniff`, a fixed
10
+ // fallback index page when the UI is not installed, and `--once`/`--json`
11
+ // printing only the descriptor and starting nothing.
12
+ //
13
+ // CRITICAL: the MCP path (mcp/dispatch.ts) must NEVER start a persistent
14
+ // listener — it always forces `once: true` so `cw_workbench_serve` only
15
+ // ever returns the descriptor. Only the CLI's default (no `--once`, no
16
+ // `--json`) actually binds and blocks.
17
+ //
18
+ // Evidence: SPEC/reporting-ux.md "Workbench" (HTTP behavior, allowed
19
+ // hostnames, serve descriptor stdout line), invariant 11.
20
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ var desc = Object.getOwnPropertyDescriptor(m, k);
23
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
24
+ desc = { enumerable: true, get: function() { return m[k]; } };
25
+ }
26
+ Object.defineProperty(o, k2, desc);
27
+ }) : (function(o, m, k, k2) {
28
+ if (k2 === undefined) k2 = k;
29
+ o[k2] = m[k];
30
+ }));
31
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
32
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
33
+ }) : function(o, v) {
34
+ o["default"] = v;
35
+ });
36
+ var __importStar = (this && this.__importStar) || (function () {
37
+ var ownKeys = function(o) {
38
+ ownKeys = Object.getOwnPropertyNames || function (o) {
39
+ var ar = [];
40
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
41
+ return ar;
42
+ };
43
+ return ownKeys(o);
44
+ };
45
+ return function (mod) {
46
+ if (mod && mod.__esModule) return mod;
47
+ var result = {};
48
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
49
+ __setModuleDefault(result, mod);
50
+ return result;
51
+ };
52
+ })();
53
+ Object.defineProperty(exports, "__esModule", { value: true });
54
+ exports.WorkbenchHost = void 0;
55
+ const crypto = __importStar(require("node:crypto"));
56
+ const fs = __importStar(require("node:fs"));
57
+ const http = __importStar(require("node:http"));
58
+ const path = __importStar(require("node:path"));
59
+ const node_url_1 = require("node:url");
60
+ const workbench_1 = require("./workbench");
61
+ const ALLOWED_HOSTNAMES = new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
62
+ function timingSafeEqual(a, b) {
63
+ const bufA = Buffer.from(a, "utf8");
64
+ const bufB = Buffer.from(b, "utf8");
65
+ if (bufA.length !== bufB.length) {
66
+ // Compare against a same-length dummy so the check still takes
67
+ // constant time relative to the (wrong) input length, matching the
68
+ // old build's mismatched-length handling.
69
+ crypto.timingSafeEqual(bufA, bufA);
70
+ return false;
71
+ }
72
+ return crypto.timingSafeEqual(bufA, bufB);
73
+ }
74
+ function sendJson(res, status, body) {
75
+ const text = `${JSON.stringify(body, null, 2)}\n`;
76
+ res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
77
+ res.end(text);
78
+ }
79
+ function contentTypeFor(file) {
80
+ const ext = path.extname(file).toLowerCase();
81
+ const map = { ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".json": "application/json; charset=utf-8", ".svg": "image/svg+xml" };
82
+ return map[ext] || "application/octet-stream";
83
+ }
84
+ function fallbackIndexHtml(descriptor) {
85
+ const routeLines = descriptor.routes.map((r) => `<li><code>${r.path}</code> — ${r.description}</li>`).join("\n ");
86
+ return `<!doctype html>
87
+ <html>
88
+ <head><meta charset="utf-8"><title>Cool Workflow Workbench</title></head>
89
+ <body>
90
+ <h1>Cool Workflow Workbench</h1>
91
+ <p>The Workbench UI is not installed. Read-only JSON routes:</p>
92
+ <ul>
93
+ ${routeLines}
94
+ </ul>
95
+ </body>
96
+ </html>
97
+ `;
98
+ }
99
+ class WorkbenchHost {
100
+ args;
101
+ token;
102
+ server;
103
+ constructor(args = {}) {
104
+ this.args = args;
105
+ this.token = process.env.CW_WORKBENCH_TOKEN && process.env.CW_WORKBENCH_TOKEN.trim() ? process.env.CW_WORKBENCH_TOKEN.trim() : undefined;
106
+ }
107
+ /** The serve descriptor; `boundPort` is filled in only after `listen()`
108
+ * actually binds (an ephemeral `--port 0` resolves to its real port). */
109
+ descriptor(once, boundPort) {
110
+ return (0, workbench_1.buildWorkbenchServeDescriptor)({ ...this.args, once }, boundPort);
111
+ }
112
+ checkAuth(req, url) {
113
+ if (!this.token)
114
+ return true;
115
+ const header = req.headers.authorization;
116
+ const bearer = header && header.startsWith("Bearer ") ? header.slice("Bearer ".length) : undefined;
117
+ const queryToken = url.searchParams.get("token") || undefined;
118
+ const provided = bearer || queryToken;
119
+ if (!provided)
120
+ return false;
121
+ return timingSafeEqual(provided, this.token);
122
+ }
123
+ handleRequest(req, res) {
124
+ try {
125
+ const hostHeader = req.headers.host;
126
+ if (hostHeader) {
127
+ const hostname = hostHeader.split(":")[0];
128
+ if (!ALLOWED_HOSTNAMES.has(hostname)) {
129
+ sendJson(res, 403, { error: "forbidden: non-localhost Host header" });
130
+ return;
131
+ }
132
+ }
133
+ if (req.method !== "GET") {
134
+ res.setHeader("Allow", "GET");
135
+ sendJson(res, 405, { error: "read-only: only GET is permitted" });
136
+ return;
137
+ }
138
+ let url;
139
+ try {
140
+ url = new node_url_1.URL(req.url || "/", "http://127.0.0.1");
141
+ }
142
+ catch {
143
+ sendJson(res, 400, { error: "bad request: invalid URL" });
144
+ return;
145
+ }
146
+ if (!this.checkAuth(req, url)) {
147
+ sendJson(res, 401, { error: "unauthorized: token mismatch" });
148
+ return;
149
+ }
150
+ // Decode the route path ONCE (old build's decodeRoutePath). Node's
151
+ // `new URL()` does NOT throw on a malformed percent-escape like
152
+ // `/%E0%A4%A`; only decodeURIComponent does — so this is where a
153
+ // malformed URL is caught as a 400 client error (not a 404, not a
154
+ // 500 crash). The decoded route is also what the traversal guard
155
+ // inspects, so an encoded `..%2f..%2f` unescapes to `../../` BEFORE
156
+ // path.resolve, letting it escape uiRoot and fail 403.
157
+ let route;
158
+ try {
159
+ route = decodeURIComponent(url.pathname);
160
+ }
161
+ catch {
162
+ sendJson(res, 400, { error: "bad request: malformed URL path" });
163
+ return;
164
+ }
165
+ if (route === "/api/serve") {
166
+ sendJson(res, 200, this.descriptor(false));
167
+ return;
168
+ }
169
+ if (route === "/api/index") {
170
+ const scope = this.args.scope === "home" ? "home" : "repo";
171
+ sendJson(res, 200, (0, workbench_1.buildWorkbenchIndex)({ ...this.args, scope, ...Object.fromEntries(url.searchParams.entries()) }));
172
+ return;
173
+ }
174
+ const runMatch = route.match(/^\/api\/run\/([^/]+)$/);
175
+ if (runMatch) {
176
+ const runId = runMatch[1];
177
+ sendJson(res, 200, (0, workbench_1.buildWorkbenchRunView)(runId, this.args));
178
+ return;
179
+ }
180
+ if (route === "/" || route.startsWith("/ui/")) {
181
+ this.serveUiAsset(route, res);
182
+ return;
183
+ }
184
+ sendJson(res, 404, { error: `no such read-only view: ${route}` });
185
+ }
186
+ catch (error) {
187
+ sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
188
+ }
189
+ }
190
+ serveUiAsset(pathname, res) {
191
+ const uiRoot = (0, workbench_1.workbenchUiRoot)();
192
+ const relative = pathname === "/" ? "index.html" : pathname.replace(/^\/ui\//, "");
193
+ const resolved = path.resolve(uiRoot, relative);
194
+ if (!resolved.startsWith(path.resolve(uiRoot) + path.sep) && resolved !== path.resolve(uiRoot)) {
195
+ sendJson(res, 403, { error: "forbidden: path traversal" });
196
+ return;
197
+ }
198
+ if (pathname === "/" && !fs.existsSync(resolved)) {
199
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
200
+ res.end(fallbackIndexHtml(this.descriptor(false)));
201
+ return;
202
+ }
203
+ if (!fs.existsSync(resolved) || fs.statSync(resolved).isDirectory()) {
204
+ sendJson(res, 404, { error: `UI asset not installed: ${relative}` });
205
+ return;
206
+ }
207
+ res.writeHead(200, { "Content-Type": contentTypeFor(resolved), "Cache-Control": "no-store", "X-Content-Type-Options": "nosniff" });
208
+ res.end(fs.readFileSync(resolved));
209
+ }
210
+ /** Binds the loopback server and resolves once listening. Returns the
211
+ * actually-bound port (useful for `--port 0`). */
212
+ listen() {
213
+ return new Promise((resolve, reject) => {
214
+ const server = http.createServer((req, res) => this.handleRequest(req, res));
215
+ const requestedPort = this.args.port !== undefined ? Number(this.args.port) : undefined;
216
+ server.on("error", reject);
217
+ server.listen(requestedPort ?? 7717, "127.0.0.1", () => {
218
+ this.server = server;
219
+ const address = server.address();
220
+ resolve(typeof address === "object" && address ? address.port : requestedPort || 7717);
221
+ });
222
+ });
223
+ }
224
+ close() {
225
+ return new Promise((resolve) => {
226
+ if (!this.server) {
227
+ resolve();
228
+ return;
229
+ }
230
+ this.server.close(() => resolve());
231
+ });
232
+ }
233
+ /** `cw workbench serve` entry point: `--once`/`--json` prints ONLY the
234
+ * descriptor (compact JSON) and starts nothing; the default binds and
235
+ * blocks, printing one compact line (`{...descriptor, boundPort}`)
236
+ * once listening. `--require-token` is a strict opt-in: default behavior
237
+ * (unauthenticated local reads when no token is configured) is
238
+ * unchanged unless the caller explicitly asks to fail closed. */
239
+ async run() {
240
+ const requireToken = Boolean(this.args.requireToken || this.args["require-token"]);
241
+ if (requireToken && !this.token) {
242
+ process.stderr.write("workbench serve --require-token: CW_WORKBENCH_TOKEN is not set; refusing to start.\n");
243
+ process.exitCode = 1;
244
+ return;
245
+ }
246
+ const once = Boolean(this.args.once || this.args.json);
247
+ if (once) {
248
+ process.stdout.write(`${JSON.stringify(this.descriptor(true))}\n`);
249
+ return;
250
+ }
251
+ const boundPort = await this.listen();
252
+ const descriptor = this.descriptor(false, boundPort);
253
+ process.stdout.write(`${JSON.stringify({ ...descriptor, boundPort })}\n`);
254
+ // Block forever (until the process is killed) — a real serve.
255
+ await new Promise(() => { });
256
+ }
257
+ }
258
+ exports.WorkbenchHost = WorkbenchHost;