cool-workflow 0.1.98 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/README.md +18 -7
  4. package/apps/architecture-review/app.json +2 -2
  5. package/apps/architecture-review/workflow.js +12 -12
  6. package/apps/architecture-review-fast/app.json +1 -1
  7. package/apps/end-to-end-golden-path/app.json +1 -1
  8. package/apps/pr-review-fix-ci/app.json +1 -1
  9. package/apps/release-cut/app.json +1 -1
  10. package/apps/research-synthesis/app.json +1 -1
  11. package/dist/cli/dispatch.js +236 -0
  12. package/dist/cli/entry.js +120 -0
  13. package/dist/cli/io.js +21 -4
  14. package/dist/cli/parseargv.js +157 -0
  15. package/dist/cli.js +6 -33
  16. package/dist/core/capability-table.js +3518 -0
  17. package/dist/core/format/help.js +314 -0
  18. package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
  19. package/dist/core/hash.js +137 -0
  20. package/dist/core/multi-agent/candidate-scoring.js +219 -0
  21. package/dist/core/multi-agent/collaboration.js +481 -0
  22. package/dist/core/multi-agent/coordinator.js +515 -0
  23. package/dist/core/multi-agent/eval-replay.js +306 -0
  24. package/dist/core/multi-agent/runtime.js +929 -0
  25. package/dist/core/multi-agent/topology.js +298 -0
  26. package/dist/core/multi-agent/trust-policy.js +197 -0
  27. package/dist/core/pipeline/commit-gate.js +320 -0
  28. package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
  29. package/dist/core/pipeline/dispatch.js +103 -0
  30. package/dist/core/pipeline/drive-decide.js +227 -0
  31. package/dist/core/pipeline/error-feedback.js +161 -0
  32. package/dist/core/pipeline/loop-expansion.js +124 -0
  33. package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
  34. package/dist/core/pipeline/runner.js +230 -0
  35. package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
  36. package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
  37. package/dist/core/state/node-projection.js +95 -0
  38. package/dist/core/state/node-snapshot.js +230 -0
  39. package/dist/core/state/run-paths.js +93 -0
  40. package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
  41. package/dist/core/state/schema.js +50 -0
  42. package/dist/core/state/state-explosion/digest.js +243 -0
  43. package/dist/core/state/state-explosion/graph.js +527 -0
  44. package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
  45. package/dist/core/state/state-explosion/report.js +187 -0
  46. package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
  47. package/dist/{state-node.js → core/state/state-node.js} +90 -113
  48. package/dist/core/state/types.js +19 -0
  49. package/dist/{validation.js → core/state/validation.js} +64 -131
  50. package/dist/core/trust/evidence-grounding.js +134 -0
  51. package/dist/core/trust/ledger.js +199 -0
  52. package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
  53. package/dist/core/trust/telemetry-ledger.js +127 -0
  54. package/dist/core/types.js +20 -0
  55. package/dist/core/version.js +16 -0
  56. package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
  57. package/dist/mcp/dispatch.js +104 -0
  58. package/dist/mcp/server.js +144 -0
  59. package/dist/mcp-server.js +6 -84
  60. package/dist/{agent-config.js → shell/agent-config.js} +118 -71
  61. package/dist/shell/app-run-cli.js +88 -0
  62. package/dist/shell/audit-cli.js +259 -0
  63. package/dist/shell/audit-provenance.js +83 -0
  64. package/dist/shell/candidate-scoring-io.js +459 -0
  65. package/dist/shell/collaboration-io.js +264 -0
  66. package/dist/shell/commit-summary.js +104 -0
  67. package/dist/shell/commit.js +290 -0
  68. package/dist/shell/coordinator-io.js +476 -0
  69. package/dist/shell/demo-cli.js +19 -0
  70. package/dist/shell/dispatch.js +162 -0
  71. package/dist/shell/doctor.js +292 -0
  72. package/dist/shell/drive.js +885 -0
  73. package/dist/shell/error-feedback-io.js +305 -0
  74. package/dist/shell/eval-io.js +473 -0
  75. package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
  76. package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
  77. package/dist/shell/exec-backend-cli.js +88 -0
  78. package/dist/shell/execution-backend/agent.js +507 -0
  79. package/dist/shell/execution-backend/ci.js +15 -0
  80. package/dist/shell/execution-backend/container.js +69 -0
  81. package/dist/shell/execution-backend/envelopes.js +55 -0
  82. package/dist/shell/execution-backend/local.js +113 -0
  83. package/dist/shell/execution-backend/probes.js +175 -0
  84. package/dist/shell/execution-backend/registry.js +402 -0
  85. package/dist/shell/execution-backend/remote.js +128 -0
  86. package/dist/shell/execution-backend/types.js +11 -0
  87. package/dist/shell/feedback-cli.js +81 -0
  88. package/dist/shell/feedback-operations.js +48 -0
  89. package/dist/shell/fs-atomic.js +276 -0
  90. package/dist/shell/harness.js +98 -0
  91. package/dist/shell/ledger-cli.js +212 -0
  92. package/dist/shell/ledger-io.js +169 -0
  93. package/dist/shell/man-cli.js +89 -0
  94. package/dist/shell/metrics-cli.js +100 -0
  95. package/dist/shell/multi-agent-cli.js +1002 -0
  96. package/dist/shell/multi-agent-host.js +563 -0
  97. package/dist/shell/multi-agent-io.js +387 -0
  98. package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
  99. package/dist/shell/node-store.js +124 -0
  100. package/dist/{observability/format.js → shell/observability-format.js} +7 -1
  101. package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
  102. package/dist/{observability.js → shell/observability.js} +159 -332
  103. package/dist/{onramp.js → shell/onramp.js} +11 -0
  104. package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
  105. package/dist/shell/operator-ux.js +431 -0
  106. package/dist/shell/orchestrator.js +231 -0
  107. package/dist/shell/pipeline-cli.js +667 -0
  108. package/dist/shell/pipeline.js +217 -0
  109. package/dist/shell/reclamation-io.js +1366 -0
  110. package/dist/shell/registry-cli.js +344 -0
  111. package/dist/{remote-source.js → shell/remote-source.js} +2 -2
  112. package/dist/shell/report-cli.js +101 -0
  113. package/dist/shell/report-view-cli.js +117 -0
  114. package/dist/{orchestrator → shell}/report.js +289 -282
  115. package/dist/shell/reporter.js +62 -0
  116. package/dist/shell/run-export-cli.js +106 -0
  117. package/dist/shell/run-export.js +680 -0
  118. package/dist/shell/run-registry-io.js +1014 -0
  119. package/dist/shell/run-store.js +164 -0
  120. package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
  121. package/dist/{scheduler.js → shell/scheduler-io.js} +257 -48
  122. package/dist/shell/scheduling-io.js +321 -0
  123. package/dist/shell/state-cli.js +181 -0
  124. package/dist/shell/state-explosion-cli.js +197 -0
  125. package/dist/shell/telemetry-cli.js +85 -0
  126. package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
  127. package/dist/shell/telemetry-ledger-io.js +132 -0
  128. package/dist/{term.js → shell/term.js} +36 -46
  129. package/dist/shell/topology-io.js +361 -0
  130. package/dist/shell/trust-audit.js +472 -0
  131. package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
  132. package/dist/shell/verifier.js +48 -0
  133. package/dist/shell/workbench-host.js +258 -0
  134. package/dist/shell/workbench-text.js +18 -0
  135. package/dist/shell/workbench.js +170 -0
  136. package/dist/shell/worker-cli.js +124 -0
  137. package/dist/shell/worker-isolation.js +852 -0
  138. package/dist/shell/workflow-app-loader.js +650 -0
  139. package/docs/agent-delegation-drive.7.md +4 -0
  140. package/docs/cli-mcp-parity.7.md +282 -219
  141. package/docs/contract-migration-tooling.7.md +4 -0
  142. package/docs/control-plane-scheduling.7.md +4 -0
  143. package/docs/durable-state-and-locking.7.md +4 -0
  144. package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
  145. package/docs/execution-backends.7.md +4 -0
  146. package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
  147. package/docs/multi-agent-eval-replay-harness.7.md +4 -0
  148. package/docs/multi-agent-operator-ux.7.md +4 -0
  149. package/docs/node-snapshot-diff-replay.7.md +4 -0
  150. package/docs/observability-cost-accounting.7.md +6 -1
  151. package/docs/project-index.md +137 -71
  152. package/docs/real-execution-backends.7.md +4 -0
  153. package/docs/release-and-migration.7.md +4 -0
  154. package/docs/release-tooling.7.md +4 -0
  155. package/docs/remote-source-review.7.md +4 -4
  156. package/docs/report-verifiable-bundle.7.md +1 -1
  157. package/docs/run-registry-control-plane.7.md +4 -0
  158. package/docs/run-retention-reclamation.7.md +25 -0
  159. package/docs/security-trust-hardening.7.md +3 -1
  160. package/docs/state-explosion-management.7.md +4 -0
  161. package/docs/team-collaboration.7.md +4 -0
  162. package/docs/web-desktop-workbench.7.md +15 -1
  163. package/manifest/plugin.manifest.json +1 -1
  164. package/manifest/source-context-profiles.json +9 -13
  165. package/package.json +1 -1
  166. package/scripts/agents/agent-adapter-core.js +22 -2
  167. package/scripts/agents/claude-p-agent.js +8 -3
  168. package/scripts/agents/cw-attest-wrap.js +2 -2
  169. package/scripts/agents/gemini-agent.js +5 -1
  170. package/scripts/agents/opencode-agent.js +5 -1
  171. package/scripts/bump-version.js +4 -3
  172. package/scripts/canonical-apps.js +4 -4
  173. package/scripts/dogfood-architecture-review.js +2 -3
  174. package/scripts/dogfood-release.js +3 -3
  175. package/scripts/gen-parity-doc.js +15 -2
  176. package/scripts/golden-path.js +4 -4
  177. package/scripts/onramp-check.js +1 -1
  178. package/scripts/parity-check.js +38 -21
  179. package/scripts/release-flow.js +2 -2
  180. package/scripts/sync-project-index.js +51 -27
  181. package/scripts/validate-run-state-schema.js +2 -2
  182. package/scripts/version-sync-check.js +30 -30
  183. package/dist/candidate-scoring.js +0 -729
  184. package/dist/capability-core.js +0 -1189
  185. package/dist/capability-registry.js +0 -885
  186. package/dist/cli/command-surface.js +0 -494
  187. package/dist/cli/format.js +0 -56
  188. package/dist/cli/handlers/audit.js +0 -82
  189. package/dist/cli/handlers/blackboard.js +0 -81
  190. package/dist/cli/handlers/candidate.js +0 -40
  191. package/dist/cli/handlers/clones.js +0 -34
  192. package/dist/cli/handlers/collaboration.js +0 -61
  193. package/dist/cli/handlers/eval.js +0 -40
  194. package/dist/cli/handlers/ledger.js +0 -169
  195. package/dist/cli/handlers/maintenance.js +0 -107
  196. package/dist/cli/handlers/multi-agent.js +0 -165
  197. package/dist/cli/handlers/node.js +0 -41
  198. package/dist/cli/handlers/operational.js +0 -155
  199. package/dist/cli/handlers/operator.js +0 -146
  200. package/dist/cli/handlers/registry.js +0 -68
  201. package/dist/cli/handlers/run.js +0 -153
  202. package/dist/cli/handlers/scheduling.js +0 -132
  203. package/dist/cli/handlers/workbench.js +0 -41
  204. package/dist/cli/handlers/worker.js +0 -45
  205. package/dist/cli/run-summary.js +0 -45
  206. package/dist/clones.js +0 -162
  207. package/dist/collaboration.js +0 -726
  208. package/dist/commit.js +0 -592
  209. package/dist/compare.js +0 -18
  210. package/dist/coordinator/classify.js +0 -45
  211. package/dist/coordinator/paths.js +0 -42
  212. package/dist/coordinator/util.js +0 -126
  213. package/dist/coordinator.js +0 -990
  214. package/dist/daemon.js +0 -44
  215. package/dist/dispatch.js +0 -250
  216. package/dist/doctor.js +0 -212
  217. package/dist/drive.js +0 -864
  218. package/dist/error-feedback.js +0 -419
  219. package/dist/evidence-grounding.js +0 -184
  220. package/dist/execution-backend/agent.js +0 -354
  221. package/dist/execution-backend/probes.js +0 -112
  222. package/dist/execution-backend/util.js +0 -47
  223. package/dist/execution-backend.js +0 -961
  224. package/dist/gates.js +0 -48
  225. package/dist/harness.js +0 -61
  226. package/dist/ledger.js +0 -313
  227. package/dist/loop-expansion.js +0 -60
  228. package/dist/mcp/tool-call.js +0 -470
  229. package/dist/mcp/tool-definitions.js +0 -1066
  230. package/dist/mcp-surface.js +0 -30
  231. package/dist/multi-agent/graph.js +0 -84
  232. package/dist/multi-agent/helpers.js +0 -141
  233. package/dist/multi-agent/ids.js +0 -20
  234. package/dist/multi-agent/paths.js +0 -22
  235. package/dist/multi-agent-eval/normalize.js +0 -51
  236. package/dist/multi-agent-eval.js +0 -678
  237. package/dist/multi-agent-host.js +0 -777
  238. package/dist/multi-agent.js +0 -984
  239. package/dist/node-projection.js +0 -59
  240. package/dist/node-snapshot.js +0 -260
  241. package/dist/operator-ux.js +0 -631
  242. package/dist/orchestrator/app-operations.js +0 -211
  243. package/dist/orchestrator/audit-operations.js +0 -182
  244. package/dist/orchestrator/candidate-operations.js +0 -117
  245. package/dist/orchestrator/cli-options.js +0 -294
  246. package/dist/orchestrator/collaboration-operations.js +0 -86
  247. package/dist/orchestrator/feedback-operations.js +0 -81
  248. package/dist/orchestrator/host-operations.js +0 -78
  249. package/dist/orchestrator/lifecycle-operations.js +0 -650
  250. package/dist/orchestrator/migration-operations.js +0 -44
  251. package/dist/orchestrator/multi-agent-operations.js +0 -362
  252. package/dist/orchestrator/topology-operations.js +0 -84
  253. package/dist/orchestrator.js +0 -925
  254. package/dist/pipeline-runner.js +0 -285
  255. package/dist/reclamation/hash.js +0 -72
  256. package/dist/reclamation.js +0 -812
  257. package/dist/reporter.js +0 -67
  258. package/dist/run-export.js +0 -815
  259. package/dist/run-registry/derive.js +0 -175
  260. package/dist/run-registry/format.js +0 -124
  261. package/dist/run-registry/gc.js +0 -251
  262. package/dist/run-registry/policy.js +0 -16
  263. package/dist/run-registry/queue.js +0 -115
  264. package/dist/run-registry.js +0 -850
  265. package/dist/run-state-schema.js +0 -68
  266. package/dist/scheduling.js +0 -184
  267. package/dist/state-explosion.js +0 -1014
  268. package/dist/state.js +0 -367
  269. package/dist/telemetry-ledger.js +0 -196
  270. package/dist/topology.js +0 -565
  271. package/dist/triggers.js +0 -184
  272. package/dist/trust-audit.js +0 -644
  273. package/dist/types/blackboard.js +0 -2
  274. package/dist/types/candidate.js +0 -2
  275. package/dist/types/collaboration.js +0 -2
  276. package/dist/types/core.js +0 -2
  277. package/dist/types/drive.js +0 -10
  278. package/dist/types/error-feedback.js +0 -2
  279. package/dist/types/evidence-reasoning.js +0 -2
  280. package/dist/types/execution-backend.js +0 -2
  281. package/dist/types/multi-agent.js +0 -2
  282. package/dist/types/observability.js +0 -2
  283. package/dist/types/pipeline.js +0 -2
  284. package/dist/types/reclamation.js +0 -8
  285. package/dist/types/report-bundle.js +0 -6
  286. package/dist/types/result.js +0 -2
  287. package/dist/types/run-registry.js +0 -2
  288. package/dist/types/run.js +0 -2
  289. package/dist/types/sandbox.js +0 -2
  290. package/dist/types/schedule.js +0 -2
  291. package/dist/types/state-node.js +0 -2
  292. package/dist/types/topology.js +0 -2
  293. package/dist/types/trust.js +0 -2
  294. package/dist/types/workbench.js +0 -2
  295. package/dist/types/worker.js +0 -2
  296. package/dist/types/workflow-app.js +0 -2
  297. package/dist/types.js +0 -44
  298. package/dist/util/fingerprint.js +0 -19
  299. package/dist/util/fingerprint.test.js +0 -27
  300. package/dist/verifier.js +0 -78
  301. package/dist/version.js +0 -8
  302. package/dist/workbench-host.js +0 -192
  303. package/dist/workbench.js +0 -192
  304. package/dist/worker-accept/acceptance.js +0 -114
  305. package/dist/worker-accept/blackboard-fanout.js +0 -80
  306. package/dist/worker-accept/blackboard-linkage.js +0 -19
  307. package/dist/worker-accept/context.js +0 -2
  308. package/dist/worker-accept/telemetry-ledger.js +0 -126
  309. package/dist/worker-accept/validation.js +0 -77
  310. package/dist/worker-accept/verifier-completion.js +0 -73
  311. package/dist/worker-isolation/helpers.js +0 -51
  312. package/dist/worker-isolation/paths.js +0 -46
  313. package/dist/worker-isolation.js +0 -656
  314. package/dist/workflow-api.js +0 -131
  315. /package/dist/{types → core/types}/boundary.js +0 -0
@@ -0,0 +1,298 @@
1
+ "use strict";
2
+ // core/multi-agent/topology.ts — OFFICIAL_TOPOLOGIES, applyTopology's
3
+ // decision half, role-width math, collector-exclusion, trust-authority.
4
+ //
5
+ // MILESTONE 9. Byte-exact port of the old build's src/topology.ts, minus
6
+ // the actual multi-agent/blackboard/coordinator record creation and audit
7
+ // writes (those are impure — see shell/topology-io.ts, which calls this
8
+ // file's pure helpers then wires createMultiAgentRun/createAgentRole/
9
+ // createBlackboardTopic/etc via shell/multi-agent-io.ts +
10
+ // shell/coordinator-io.ts, byte-identical to the old build's applyTopology
11
+ // call sequence).
12
+ //
13
+ // BYTE-COMPAT ITEM 3 [load-bearing, HIGH priority]: `unique` in THIS file
14
+ // does NOT sort (dedup only, insertion order preserved) — the opposite of
15
+ // core/multi-agent/runtime.ts's sorting `unique`. Never merge the two. See
16
+ // uniquedual-role-vs-candidate-order.case.js.
17
+ //
18
+ // Evidence: SPEC/multi-agent.md section B ("Topologies"), "Topology error
19
+ // strings and outputs"; plugins/cool-workflow/src/topology.ts (byte-exact
20
+ // source).
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.OFFICIAL_TOPOLOGIES = exports.TOPOLOGY_SCHEMA_VERSION = void 0;
23
+ exports.unique = unique;
24
+ exports.registerTopology = registerTopology;
25
+ exports.listTopologyDefinitions = listTopologyDefinitions;
26
+ exports.getTopologyDefinition = getTopologyDefinition;
27
+ exports.validateTopologyDefinition = validateTopologyDefinition;
28
+ exports.withLegacyRoleCounts = withLegacyRoleCounts;
29
+ exports.materializedRoles = materializedRoles;
30
+ exports.fanoutRoleIds = fanoutRoleIds;
31
+ exports.selectedTaskIds = selectedTaskIds;
32
+ exports.topologyRunId = topologyRunId;
33
+ exports.nextActionsFor = nextActionsFor;
34
+ exports.statusToNodeStatus = statusToNodeStatus;
35
+ exports.buildTopologyGraphFromRuns = buildTopologyGraphFromRuns;
36
+ exports.TOPOLOGY_SCHEMA_VERSION = 1;
37
+ /** Dedup, insertion-order preserved (does NOT sort) — the topology-side
38
+ * `unique` twin. See file header byte-compat note. */
39
+ function unique(items) {
40
+ return [...new Set(items.filter((item) => item !== undefined && item !== null))];
41
+ }
42
+ function roleSpec(id, title, responsibilities, expectedArtifacts, faninObligations, count) {
43
+ return { id, title, responsibilities, requiredEvidence: expectedArtifacts, expectedArtifacts, faninObligations, ...(count !== undefined ? { count } : {}) };
44
+ }
45
+ function topicSpec(id, title, description) {
46
+ return { id, title, description };
47
+ }
48
+ function phaseSpec(id, title, roleIds, fanout, fanin, requiredEvidence, coordinatorDecisionKinds) {
49
+ return { id, title, roleIds, fanout, fanin, requiredEvidence, coordinatorDecisionKinds };
50
+ }
51
+ /** The three built-in topologies, in this exact order. Byte-exact
52
+ * transcription of src/topology.ts:58-137. */
53
+ exports.OFFICIAL_TOPOLOGIES = [
54
+ {
55
+ schemaVersion: 1,
56
+ id: "map-reduce",
57
+ title: "Map-Reduce",
58
+ summary: "Fan out mapper roles, index mapper evidence on the blackboard, then reduce only after required evidence is present.",
59
+ roles: [
60
+ roleSpec("mapper", "Mapper", ["Produce an independent shard result and cite evidence."], ["mapper output artifact"], ["indexed mapper artifact"], 2),
61
+ roleSpec("reducer", "Reducer", ["Synthesize mapper outputs only after fanin is verifier-ready."], ["reducer synthesis"], ["all mapper evidence"]),
62
+ ],
63
+ groups: [{ id: "map-reduce", title: "Map-Reduce Group", roleIds: ["mapper", "reducer"] }],
64
+ blackboardTopics: [
65
+ topicSpec("mapper-outputs", "Mapper Outputs", "Indexed mapper result artifacts and evidence."),
66
+ topicSpec("reducer-synthesis", "Reducer Synthesis", "Reducer fanin readiness and synthesis provenance."),
67
+ ],
68
+ phases: [
69
+ phaseSpec("map", "Map", ["mapper"], true, false, ["mapper output artifact"], ["artifact-index"]),
70
+ phaseSpec("reduce", "Reduce", ["reducer"], false, true, ["all mapper evidence"], ["fanin-readiness", "candidate-synthesis"]),
71
+ ],
72
+ fanoutStrategy: "one membership per mapper role over selected run tasks",
73
+ faninStrategy: "required mapper roles must report result evidence and indexed blackboard artifacts",
74
+ requiredEvidence: ["mapper output artifact", "blackboard artifact ref", "reducer synthesis"],
75
+ coordinatorDecisions: ["artifact-index", "fanin-readiness", "candidate-synthesis"],
76
+ candidateExpectations: ["Reducer result becomes a candidate only with mapper provenance."],
77
+ verifierGates: ["Reducer fanin must be ready before completion or commit."],
78
+ },
79
+ {
80
+ schemaVersion: 1,
81
+ id: "debate",
82
+ title: "Debate",
83
+ summary: "Record opposing claims, rebuttal rounds, conflict context, coordinator decisions, and final synthesis on shared topics.",
84
+ roles: [
85
+ roleSpec("position-a", "Position A", ["Argue one supported position with evidence."], ["claim message"], ["round messages"]),
86
+ roleSpec("position-b", "Position B", ["Argue a contrasting position with evidence."], ["counterclaim message"], ["round messages"]),
87
+ roleSpec("synthesizer", "Synthesis", ["Resolve or preserve conflicts with citations."], ["debate synthesis"], ["coordinator decisions"]),
88
+ ],
89
+ groups: [{ id: "debate", title: "Debate Group", roleIds: ["position-a", "position-b", "synthesizer"] }],
90
+ blackboardTopics: [
91
+ topicSpec("debate-rounds", "Debate Rounds", "Claim and rebuttal messages by round."),
92
+ topicSpec("debate-conflicts", "Conflict Context", "Conflicting or unresolved claims."),
93
+ topicSpec("debate-synthesis", "Final Synthesis", "Accepted, rejected, conflicting, and unresolved claims."),
94
+ ],
95
+ phases: [
96
+ phaseSpec("opening", "Opening Claims", ["position-a", "position-b"], true, false, ["claim evidence"], ["message-moderation"]),
97
+ phaseSpec("rebuttal", "Rebuttal Rounds", ["position-a", "position-b"], true, false, ["response evidence"], ["conflict-resolution"]),
98
+ phaseSpec("synthesis", "Synthesis", ["synthesizer"], false, true, ["debate messages", "coordinator decisions"], ["candidate-synthesis"]),
99
+ ],
100
+ fanoutStrategy: "opposing roles write blackboard messages for each round",
101
+ faninStrategy: "synthesis requires debate messages and coordinator claim decisions",
102
+ requiredEvidence: ["debate message", "conflict context", "coordinator decision", "final synthesis"],
103
+ coordinatorDecisions: ["message-moderation", "conflict-resolution", "candidate-synthesis"],
104
+ candidateExpectations: ["Final synthesis cites debate messages and decisions."],
105
+ verifierGates: ["Required debate rounds and synthesis evidence must be present."],
106
+ },
107
+ {
108
+ schemaVersion: 1,
109
+ id: "judge-panel",
110
+ title: "Judge Panel",
111
+ summary: "Collect independent judge outputs, aggregate scores, and select a panel decision with linked evidence.",
112
+ roles: [
113
+ roleSpec("judge", "Judge", ["Score candidates independently and cite evidence."], ["judge score artifact"], ["judge verdict"], 3),
114
+ roleSpec("panel-chair", "Panel Chair", ["Aggregate scores and write a panel decision."], ["panel decision"], ["judge evidence"]),
115
+ ],
116
+ groups: [{ id: "judge-panel", title: "Judge Panel Group", roleIds: ["judge", "panel-chair"] }],
117
+ blackboardTopics: [
118
+ topicSpec("judge-verdicts", "Judge Verdicts", "Independent judge outputs and score evidence."),
119
+ topicSpec("panel-decision", "Panel Decision", "Aggregated verdict and candidate selection rationale."),
120
+ ],
121
+ phases: [
122
+ phaseSpec("judge", "Judge", ["judge"], true, false, ["judge score artifact"], ["artifact-index"]),
123
+ phaseSpec("panel", "Panel", ["panel-chair"], false, true, ["judge evidence", "score records"], ["candidate-synthesis"]),
124
+ ],
125
+ fanoutStrategy: "one membership per independent judge role",
126
+ faninStrategy: "panel decision requires fanin over judge evidence and score records",
127
+ requiredEvidence: ["judge output", "score record", "panel decision", "candidate selection rationale"],
128
+ coordinatorDecisions: ["artifact-index", "candidate-synthesis"],
129
+ candidateExpectations: ["No single judge is authoritative without aggregated fanin and score evidence."],
130
+ verifierGates: ["Panel decision requires multiple judge outputs unless explicitly configured otherwise."],
131
+ },
132
+ ];
133
+ /** Module-level registry (mechanism); OFFICIAL_TOPOLOGIES + registrations
134
+ * are policy. `listTopologyDefinitions()` composes them — registered
135
+ * wins on id clash, last-write-wins. */
136
+ const topologyRegistry = new Map();
137
+ function registerTopology(definition) {
138
+ topologyRegistry.set(definition.id, clone(definition));
139
+ }
140
+ function listTopologyDefinitions() {
141
+ const merged = exports.OFFICIAL_TOPOLOGIES.map((definition) => clone(definition));
142
+ for (const registered of topologyRegistry.values()) {
143
+ const idx = merged.findIndex((d) => d.id === registered.id);
144
+ if (idx >= 0)
145
+ merged[idx] = clone(registered);
146
+ else
147
+ merged.push(clone(registered));
148
+ }
149
+ return merged;
150
+ }
151
+ function getTopologyDefinition(topologyId) {
152
+ const registered = topologyRegistry.get(topologyId);
153
+ if (registered)
154
+ return clone(registered);
155
+ return exports.OFFICIAL_TOPOLOGIES.find((definition) => definition.id === topologyId);
156
+ }
157
+ function validateTopologyDefinition(topologyId) {
158
+ const definition = getTopologyDefinition(topologyId);
159
+ if (!definition)
160
+ return { valid: false, topologyId, issues: [{ code: "unknown-topology", message: `Unknown topology id: ${topologyId}` }] };
161
+ const issues = [];
162
+ if (!definition.roles.length)
163
+ issues.push({ code: "missing-roles", message: "Topology must declare at least one role.", path: "roles" });
164
+ if (!definition.groups.length)
165
+ issues.push({ code: "missing-groups", message: "Topology must declare at least one group.", path: "groups" });
166
+ if (!definition.blackboardTopics.length)
167
+ issues.push({ code: "missing-topics", message: "Topology must declare blackboard topics.", path: "blackboardTopics" });
168
+ if (!definition.requiredEvidence.length)
169
+ issues.push({ code: "missing-evidence", message: "Topology must declare required evidence.", path: "requiredEvidence" });
170
+ const roleIds = new Set(definition.roles.map((role) => role.id));
171
+ for (const phase of definition.phases) {
172
+ for (const roleId of phase.roleIds) {
173
+ if (!roleIds.has(roleId))
174
+ issues.push({ code: "unknown-phase-role", message: `Phase ${phase.id} references unknown role ${roleId}.`, path: `phases.${phase.id}` });
175
+ }
176
+ }
177
+ return { valid: issues.length === 0, topologyId, issues, definition };
178
+ }
179
+ /** Fold the legacy id-keyed mapperCount/judgeCount flags into the uniform
180
+ * roleCounts map. An explicit roleCounts entry always wins; judgeCount
181
+ * floors at 2 (a panel never collapses to one judge via the legacy
182
+ * flag), mapperCount floors at 1. */
183
+ function withLegacyRoleCounts(input) {
184
+ const legacy = {
185
+ mapper: input.mapperCount === undefined ? undefined : Math.max(1, input.mapperCount),
186
+ judge: input.judgeCount === undefined ? undefined : Math.max(2, input.judgeCount),
187
+ };
188
+ const roleCounts = { ...input.roleCounts };
189
+ for (const [roleId, value] of Object.entries(legacy)) {
190
+ if (value !== undefined && roleCounts[roleId] === undefined)
191
+ roleCounts[roleId] = value;
192
+ }
193
+ return Object.keys(roleCounts).length ? { ...input, roleCounts } : input;
194
+ }
195
+ /** Width = `max(1, roleCounts[role.id] ?? role.count ?? 1)`; when width >
196
+ * 1, instances get ids `${role.id}-${index}` and titles `${role.title}
197
+ * ${index}` for index 1..N. Data-driven — no topology-id/role-id
198
+ * branching. */
199
+ function materializedRoles(definition, input) {
200
+ const roles = [];
201
+ for (const role of definition.roles) {
202
+ const roleCount = Math.max(1, input.roleCounts?.[role.id] ?? role.count ?? 1);
203
+ if (roleCount > 1) {
204
+ for (let index = 1; index <= roleCount; index += 1) {
205
+ roles.push({ ...role, id: `${role.id}-${index}`, title: `${role.title} ${index}` });
206
+ }
207
+ }
208
+ else {
209
+ roles.push({ ...role, id: role.id, title: role.title });
210
+ }
211
+ }
212
+ return roles;
213
+ }
214
+ /** Collector roles are excluded from fanout by id suffix
215
+ * (-reducer/-synthesizer/-panel-chair) — NOT by a flag or role position.
216
+ * If the filter empties the list, ALL roles fan out. */
217
+ function fanoutRoleIds(roleIds) {
218
+ const filtered = roleIds.filter((roleId) => !roleId.endsWith("-reducer") && !roleId.endsWith("-synthesizer") && !roleId.endsWith("-panel-chair"));
219
+ return filtered.length ? filtered : roleIds;
220
+ }
221
+ function selectedTaskIds(tasks, taskIds) {
222
+ const ids = taskIds?.length ? taskIds : [tasks.find((task) => task.status === "pending")?.id || tasks[0]?.id].filter(Boolean);
223
+ for (const id of ids) {
224
+ if (!tasks.some((task) => task.id === id))
225
+ throw new Error(`Unknown task id for topology: ${id}`);
226
+ }
227
+ return ids;
228
+ }
229
+ /** Deterministic default topology-run id: `${definition.id}-${hex16}`
230
+ * where `hex16` is the first 16 hex chars of a content-hash over
231
+ * `{definitionId, roleIds sorted, taskIds sorted, runId, sequence}`.
232
+ * `sha256`/`stableStringify` are injected so this file stays free of a
233
+ * direct core/hash.ts import cycle risk (matches the old build's own
234
+ * use of the shared hash module). */
235
+ function topologyRunId(definition, taskIds, runId, sequence, hash) {
236
+ const digest = hash({
237
+ definitionId: definition.id,
238
+ roleIds: [...definition.roles.map((role) => role.id)].sort(),
239
+ taskIds: [...taskIds].sort(),
240
+ runId,
241
+ sequence,
242
+ });
243
+ return `${definition.id}-${digest.replace("sha256:", "").slice(0, 16)}`;
244
+ }
245
+ function nextActionsFor(runId, topologyRunId, fanoutId) {
246
+ return [
247
+ `node scripts/cw.js dispatch ${runId} --multi-agent-fanout ${fanoutId}`,
248
+ `node scripts/cw.js multi-agent fanin ${runId} ${topologyRunId}-fanin --fanout ${fanoutId}`,
249
+ `node scripts/cw.js topology summary ${runId}`,
250
+ ];
251
+ }
252
+ function statusToNodeStatus(status) {
253
+ if (status === "completed" || status === "ready")
254
+ return "completed";
255
+ if (status === "blocked")
256
+ return "blocked";
257
+ if (status === "failed")
258
+ return "failed";
259
+ if (status === "running")
260
+ return "running";
261
+ return "pending";
262
+ }
263
+ function clone(value) {
264
+ return JSON.parse(JSON.stringify(value));
265
+ }
266
+ function uniqueEdges(edges) {
267
+ const seen = new Set();
268
+ return edges.filter((edge) => {
269
+ const key = `${edge.from}->${edge.to}:${edge.label || ""}`;
270
+ if (seen.has(key))
271
+ return false;
272
+ seen.add(key);
273
+ return true;
274
+ });
275
+ }
276
+ function buildTopologyGraphFromRuns(runId, records, runPath) {
277
+ const nodes = [];
278
+ const edges = [];
279
+ for (const record of records) {
280
+ nodes.push({ id: `${runId}:topology:${record.id}`, kind: "topology-run", status: record.status, label: `${record.topologyId}:${record.id}`, path: runPath(record.id) });
281
+ edges.push({ from: `${runId}:run`, to: `${runId}:topology:${record.id}` });
282
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:multi-agent:${record.multiAgentRunId}`, label: "multi-agent" });
283
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:blackboard:${record.blackboardId}`, label: "blackboard" });
284
+ for (const topicId of record.topicIds)
285
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:blackboard:topic:${topicId}`, label: "topic" });
286
+ for (const roleId of record.roleIds)
287
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:multi-agent:role:${roleId}`, label: "role" });
288
+ for (const groupId of record.groupIds)
289
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:multi-agent:group:${groupId}`, label: "group" });
290
+ for (const fanoutId of record.fanoutIds)
291
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:multi-agent:fanout:${fanoutId}`, label: "fanout" });
292
+ for (const faninId of record.faninIds)
293
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:multi-agent:fanin:${faninId}`, label: "fanin" });
294
+ for (const decisionId of record.coordinatorDecisionIds)
295
+ edges.push({ from: `${runId}:topology:${record.id}`, to: `${runId}:blackboard:decision:${decisionId}`, label: "decision" });
296
+ }
297
+ return { nodes, edges: uniqueEdges(edges) };
298
+ }
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ // core/multi-agent/trust-policy.ts — policyForRole/Group/Membership,
3
+ // authorizeMultiAgentAction's PURE decision half.
4
+ //
5
+ // MILESTONE 9. Byte-exact port of the decision logic in the old build's
6
+ // src/multi-agent-trust.ts. Audit-event recording (recordTrustAuditEvent,
7
+ // which appends to disk) is the caller's job — see
8
+ // shell/multi-agent-io.ts's `authorizeMultiAgentAction` wrapper, which
9
+ // calls `evaluateMultiAgentAction` below then records the two audit
10
+ // events exactly like the old build did.
11
+ //
12
+ // Evidence: SPEC/multi-agent.md section D ("Trust policies"), "Trust
13
+ // denial reasons"; plugins/cool-workflow/src/multi-agent-trust.ts
14
+ // (byte-exact source for policyForRole/Group/Membership + evaluatePolicy/
15
+ // missingEvidence).
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.policyForRole = policyForRole;
18
+ exports.policyForGroup = policyForGroup;
19
+ exports.policyForMembership = policyForMembership;
20
+ exports.evaluateMultiAgentAction = evaluateMultiAgentAction;
21
+ exports.resolvePolicy = resolvePolicy;
22
+ exports.sourceForActor = sourceForActor;
23
+ function unique(values) {
24
+ return Array.from(new Set(values.filter(Boolean)));
25
+ }
26
+ /** Chair detection: lowercased `metadata.topologyRoleId` (or title)
27
+ * contains "chair", "reducer", or "synthesizer". Judge detection:
28
+ * contains "judge". Renaming a role changes its authority (docs/rebuild/PLAN.md
29
+ * byte-compat / rebuild risk 4 — substring match, not a flag). */
30
+ function policyForRole(role) {
31
+ const topologyRole = String(role.metadata?.topologyRoleId || role.title || "").toLowerCase();
32
+ const isChair = topologyRole.includes("chair") || topologyRole.includes("reducer") || topologyRole.includes("synthesizer");
33
+ const isJudge = topologyRole.includes("judge");
34
+ return {
35
+ schemaVersion: 1,
36
+ id: `${role.id}-policy`,
37
+ policyRef: `multiAgent.roles.${role.id}.policy`,
38
+ subjectKind: "role",
39
+ subjectId: role.id,
40
+ allowedBlackboardTopicIds: unique(role.topicIds || ["*"]),
41
+ allowedWriteOperations: unique([
42
+ "message",
43
+ "context",
44
+ "artifact",
45
+ ...(isChair ? ["snapshot", "coordinator-decision"] : []),
46
+ ]),
47
+ allowedCandidateOperations: (isChair ? ["score", "select"] : ["score"]),
48
+ allowedJudgeOperations: unique([
49
+ ...(isJudge ? ["verdict", "rationale"] : []),
50
+ ...(isChair ? ["rationale", "panel-decision"] : []),
51
+ ]),
52
+ sandboxProfileHints: unique(role.sandboxProfileHints || []),
53
+ requiredEvidenceRefs: unique(role.requiredEvidence || []),
54
+ requiredEvidenceFor: {
55
+ "judge.rationale": ["judge rationale evidence"],
56
+ "judge.verdict": ["judge verdict evidence"],
57
+ "judge.panel-decision": ["judge messages", "score evidence", "coordinator decision"],
58
+ "candidate.select": ["score evidence", "judge rationale"],
59
+ },
60
+ deniedOperations: [],
61
+ metadata: { title: role.title, topologyRoleId: role.metadata?.topologyRoleId },
62
+ };
63
+ }
64
+ /** Wide-open group policy: all write/candidate/judge ops allowed, topics
65
+ * from the group or `["*"]`. */
66
+ function policyForGroup(group) {
67
+ return {
68
+ schemaVersion: 1,
69
+ id: `${group.id}-policy`,
70
+ policyRef: `multiAgent.groups.${group.id}.policy`,
71
+ subjectKind: "group",
72
+ subjectId: group.id,
73
+ allowedBlackboardTopicIds: unique(group.topicIds || ["*"]),
74
+ allowedWriteOperations: ["message", "context", "artifact", "snapshot", "coordinator-decision"],
75
+ allowedCandidateOperations: ["register", "score", "select"],
76
+ allowedJudgeOperations: ["verdict", "rationale", "panel-decision"],
77
+ sandboxProfileHints: [],
78
+ requiredEvidenceRefs: [],
79
+ deniedOperations: [],
80
+ metadata: { phase: group.phase },
81
+ };
82
+ }
83
+ /** Copies the role policy (or a minimal message/context/artifact-only
84
+ * policy if no role) and re-subjects it to the membership. */
85
+ function policyForMembership(membership, role) {
86
+ const source = role?.policy || (role ? policyForRole(role) : undefined);
87
+ return {
88
+ ...(source || {
89
+ schemaVersion: 1,
90
+ id: `${membership.id}-policy`,
91
+ policyRef: `multiAgent.memberships.${membership.id}.policy`,
92
+ subjectKind: "membership",
93
+ subjectId: membership.id,
94
+ allowedBlackboardTopicIds: unique(membership.topicIds || ["*"]),
95
+ allowedWriteOperations: ["message", "context", "artifact"],
96
+ allowedCandidateOperations: [],
97
+ allowedJudgeOperations: [],
98
+ sandboxProfileHints: [],
99
+ requiredEvidenceRefs: [],
100
+ deniedOperations: [],
101
+ }),
102
+ id: `${membership.id}-policy`,
103
+ policyRef: `multiAgent.memberships.${membership.id}.policy`,
104
+ subjectKind: "membership",
105
+ subjectId: membership.id,
106
+ };
107
+ }
108
+ /** The pure decision half of `authorizeMultiAgentAction`: resolves the
109
+ * policy (membership -> role -> group, in that priority order) and
110
+ * evaluates denied ops / topic scope / op-class membership / required
111
+ * evidence. Audit recording is NOT done here (shell/multi-agent-io.ts's
112
+ * wrapper does that, injecting the resolved policy + reason into
113
+ * recordTrustAuditEvent calls byte-identical to the old build's). */
114
+ function evaluateMultiAgentAction(policy, operation, topicId, evidenceRefs) {
115
+ const reason = evaluatePolicy(policy, operation, topicId, evidenceRefs);
116
+ const allowed = !reason;
117
+ return {
118
+ allowed,
119
+ decision: allowed ? "allowed" : "denied",
120
+ reason: reason || "allowed by explicit multi-agent policy",
121
+ policyRef: policy?.policyRef,
122
+ policy,
123
+ missingEvidenceRefs: missingEvidence(policy, operation, evidenceRefs),
124
+ };
125
+ }
126
+ /** Resolves the effective policy for an action: membership wins, else
127
+ * role, else group — same priority chain as the old build's
128
+ * `resolvePolicy`. Callers supply lookup functions so this stays pure
129
+ * (no direct WorkflowRun/state coupling beyond what's passed in). */
130
+ function resolvePolicy(input, lookups) {
131
+ const membership = input.membershipId ? lookups.membership?.(input.membershipId) : undefined;
132
+ if (membership?.policy)
133
+ return membership.policy;
134
+ const roleId = input.roleId || membership?.roleId;
135
+ const role = roleId ? lookups.role?.(roleId) : undefined;
136
+ if (role?.policy)
137
+ return role.policy;
138
+ if (role)
139
+ return policyForRole(role);
140
+ const group = input.groupId ? lookups.group?.(input.groupId) : undefined;
141
+ if (group?.policy)
142
+ return group.policy;
143
+ if (group)
144
+ return policyForGroup(group);
145
+ return undefined;
146
+ }
147
+ function evaluatePolicy(policy, operation, topicId, evidenceRefs) {
148
+ if (!policy)
149
+ return "missing role authority or policy";
150
+ const denied = policy.deniedOperations.find((entry) => entry.operation === operation);
151
+ if (denied)
152
+ return denied.reason;
153
+ if (topicId && policy.allowedBlackboardTopicIds.length && !policy.allowedBlackboardTopicIds.includes("*") && !policy.allowedBlackboardTopicIds.includes(topicId)) {
154
+ return `topic ${topicId} is outside policy ${policy.policyRef}`;
155
+ }
156
+ if (operation.startsWith("candidate.")) {
157
+ const op = operation.slice("candidate.".length);
158
+ if (!policy.allowedCandidateOperations.includes(op))
159
+ return `candidate operation ${op} is outside policy ${policy.policyRef}`;
160
+ }
161
+ else if (operation.startsWith("judge.")) {
162
+ const op = operation.slice("judge.".length);
163
+ if (!policy.allowedJudgeOperations.includes(op))
164
+ return `judge operation ${op} is outside policy ${policy.policyRef}`;
165
+ }
166
+ else if (!policy.allowedWriteOperations.includes(operation)) {
167
+ return `blackboard write operation ${operation} is outside policy ${policy.policyRef}`;
168
+ }
169
+ const missing = missingEvidence(policy, operation, evidenceRefs);
170
+ if (missing.length)
171
+ return `operation ${operation} requires evidence refs: ${missing.join(", ")}`;
172
+ return undefined;
173
+ }
174
+ function missingEvidence(policy, operation, evidenceRefs) {
175
+ if (!policy)
176
+ return [];
177
+ const required = unique([...(policy.requiredEvidenceFor?.[operation] || [])]);
178
+ if (!required.length)
179
+ return [];
180
+ if (evidenceRefs.length)
181
+ return [];
182
+ return required;
183
+ }
184
+ /** no actor -> operator-recorded; worker -> host-attested; operator ->
185
+ * operator-recorded; runtime/coordinator/verifier -> runtime-derived;
186
+ * else cw-validated. */
187
+ function sourceForActor(actor) {
188
+ if (!actor)
189
+ return "operator-recorded";
190
+ if (actor.kind === "worker")
191
+ return "host-attested";
192
+ if (actor.kind === "operator")
193
+ return "operator-recorded";
194
+ if (actor.kind === "runtime" || actor.kind === "coordinator" || actor.kind === "verifier")
195
+ return "runtime-derived";
196
+ return "cw-validated";
197
+ }