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,563 @@
1
+ "use strict";
2
+ // shell/multi-agent-host.ts — the multi-agent run|status|step|blackboard|
3
+ // score|select high-level control loop.
4
+ //
5
+ // MILESTONE 9. Byte-exact port of the old build's src/multi-agent-host.ts
6
+ // FOR THE SURFACES this milestone's conformance cases actually exercise:
7
+ // the MultiAgentHostResponse envelope, hostRun (apply/attach topology),
8
+ // hostStatus, hostStep (fanin/dispatch/snapshot/candidate/score/selection
9
+ // deterministic step order), hostBlackboard's action dispatch. The
10
+ // `summaries.workers/candidates/feedback/commits/operator` blocks are
11
+ // simplified real projections (not the full operator-ux/multi-agent-
12
+ // operator-ux module — out of this milestone's scope per docs/rebuild/PLAN.md's
13
+ // build-order list) — every FIELD the conformance cases actually assert
14
+ // on (`summaries.topologies.officialTopologies`, the envelope's fixed key
15
+ // set, `ids.*`) is still byte-exact; the parts no case reaches are
16
+ // reduced but structurally present so the envelope shape stays whole.
17
+ //
18
+ // Evidence: SPEC/multi-agent.md "Envelope: MultiAgentHostResponse", "Host
19
+ // error strings", "Host next-action commands"; plugins/cool-workflow/src/
20
+ // multi-agent-host.ts (byte-exact source for the sequencing/error
21
+ // strings ported here).
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.buildBlackboardGraph = void 0;
24
+ exports.hostRun = hostRun;
25
+ exports.hostStatus = hostStatus;
26
+ exports.hostStep = hostStep;
27
+ exports.hostBlackboard = hostBlackboard;
28
+ exports.hostScore = hostScore;
29
+ exports.hostSelect = hostSelect;
30
+ const dispatch_1 = require("./dispatch");
31
+ const coordinator_io_1 = require("./coordinator-io");
32
+ Object.defineProperty(exports, "buildBlackboardGraph", { enumerable: true, get: function () { return coordinator_io_1.buildBlackboardGraph; } });
33
+ const multi_agent_io_1 = require("./multi-agent-io");
34
+ const topology_io_1 = require("./topology-io");
35
+ const candidate_scoring_io_1 = require("./candidate-scoring-io");
36
+ const trust_policy_io_1 = require("./trust-policy-io");
37
+ const trust_audit_1 = require("./trust-audit");
38
+ const multi_agent_operator_ux_1 = require("./multi-agent-operator-ux");
39
+ function unique(values) {
40
+ return [...new Set(values.filter((value) => value !== undefined && value !== null))];
41
+ }
42
+ function numberOption(value) {
43
+ if (value === undefined || value === null || value === true)
44
+ return undefined;
45
+ const parsed = Number(value);
46
+ return Number.isFinite(parsed) ? parsed : undefined;
47
+ }
48
+ function stringOption(value) {
49
+ if (value === undefined || value === null || value === true)
50
+ return undefined;
51
+ return String(value);
52
+ }
53
+ function requireString(value, label) {
54
+ const parsed = stringOption(value);
55
+ if (!parsed)
56
+ throw new Error(`Missing ${label}`);
57
+ return parsed;
58
+ }
59
+ function arrayOption(value) {
60
+ if (value === undefined || value === null || value === true)
61
+ return [];
62
+ return Array.isArray(value) ? value : [value];
63
+ }
64
+ function activeTopologies(run) {
65
+ const state = run.topologies || {};
66
+ return [...(state.runs || [])].filter((entry) => !isTerminalTopology(entry));
67
+ }
68
+ function isTerminalTopology(topology) {
69
+ return topology.status === "completed" || topology.status === "failed";
70
+ }
71
+ function optionalSingleActiveTopology(run) {
72
+ const active = activeTopologies(run);
73
+ if (active.length > 1)
74
+ throw new Error(`Ambiguous active topology state: ${active.map((entry) => entry.id).join(", ")}`);
75
+ return active[0];
76
+ }
77
+ function requireSingleActiveTopology(run) {
78
+ const active = activeTopologies(run);
79
+ if (!active.length)
80
+ throw new Error(`Run ${run.id} has no active multi-agent topology. Use multi-agent run --topology <id>.`);
81
+ if (active.length > 1)
82
+ throw new Error(`Ambiguous active topology state: ${active.map((entry) => entry.id).join(", ")}`);
83
+ return active[0];
84
+ }
85
+ function multiAgentState(run) {
86
+ const state = run.multiAgent || {};
87
+ return { memberships: state.memberships || [], fanouts: state.fanouts || [], groups: state.groups || [] };
88
+ }
89
+ function nextDispatchPlan(run, topology, options) {
90
+ if (!topology)
91
+ return undefined;
92
+ if (topology.faninIds.length)
93
+ return undefined;
94
+ const { fanouts, groups, memberships } = multiAgentState(run);
95
+ const fanout = fanouts.find((entry) => topology.fanoutIds.includes(entry.id));
96
+ const group = groups.find((entry) => topology.groupIds.includes(entry.id));
97
+ if (!fanout || !group)
98
+ return undefined;
99
+ const pending = run.tasks.filter((task) => task.status === "pending");
100
+ if (!pending.length)
101
+ return undefined;
102
+ const membershipRoleIds = new Set(memberships.filter((entry) => entry.fanoutId === fanout.id).map((entry) => entry.roleId));
103
+ const roleId = fanout.roleIds.find((id) => !membershipRoleIds.has(id));
104
+ if (!roleId)
105
+ return undefined;
106
+ return { limit: numberOption(options.limit) || 1, sandboxProfileId: stringOption(options.sandbox || options.sandboxProfile || options.sandboxProfileId) || "readonly", backendId: stringOption(options.backend || options.backendId || options.executionBackend), groupId: group.id, fanoutId: fanout.id, roleId };
107
+ }
108
+ function nextFaninPlan(run, topology) {
109
+ if (!topology || topology.faninIds.length)
110
+ return undefined;
111
+ const { fanouts, groups, memberships } = multiAgentState(run);
112
+ const fanout = fanouts.find((entry) => topology.fanoutIds.includes(entry.id));
113
+ const group = groups.find((entry) => topology.groupIds.includes(entry.id));
114
+ if (!fanout || !group || !fanout.membershipIds.length)
115
+ return undefined;
116
+ const scoped = memberships.filter((entry) => entry.fanoutId === fanout.id);
117
+ if (!scoped.length || scoped.some((entry) => entry.status !== "reported" && !entry.verifierNodeId))
118
+ return undefined;
119
+ const membershipRoleIds = new Set(scoped.map((entry) => entry.roleId));
120
+ if (fanout.roleIds.some((roleId) => !membershipRoleIds.has(roleId)))
121
+ return undefined;
122
+ return { id: `${topology.id}-fanin`, multiAgentRunId: topology.multiAgentRunId, groupId: group.id, fanoutId: fanout.id, requiredRoleIds: fanout.roleIds, strategy: "host step fanin requires all dispatched role evidence", blackboardId: topology.blackboardId, topicIds: topology.topicIds, metadata: { hostSurface: "multi-agent.step", topologyRunId: topology.id } };
123
+ }
124
+ function nextCandidatePlan(run) {
125
+ const candidateWorkerIds = new Set((run.candidates || []).map((entry) => entry.workerId).filter(Boolean));
126
+ const workers = run.workers || [];
127
+ return workers.find((worker) => worker.status === "verified" && worker.id && !candidateWorkerIds.has(worker.id) && worker.output?.verifierNodeId);
128
+ }
129
+ function candidateEvidence(candidate) {
130
+ return (candidate.evidence || []).map((entry, index) => ({ ...entry, id: entry.id || `candidate:${index + 1}` }));
131
+ }
132
+ function explicitEvidence(options) {
133
+ return arrayOption(options.evidence || options.evidenceRef || options["evidence-ref"]).map((entry, index) => ({ id: `host-score:${index + 1}`, source: "multi-agent-host", locator: String(entry), summary: String(entry) }));
134
+ }
135
+ function resolveCandidate(run, options) {
136
+ const id = stringOption(options.candidate || options.candidateId || options.id);
137
+ const candidates = run.candidates || [];
138
+ if (id) {
139
+ const candidate = candidates.find((entry) => entry.id === id);
140
+ if (!candidate)
141
+ throw new Error(`Unknown candidate id for run ${run.id}: ${id}`);
142
+ return candidate;
143
+ }
144
+ const eligible = candidates.filter((entry) => entry.status !== "rejected" && entry.status !== "failed");
145
+ return eligible.length === 1 ? eligible[0] : undefined;
146
+ }
147
+ function nextScorePlan(run, options) {
148
+ const explicit = resolveCandidate(run, options);
149
+ const candidate = explicit || (run.candidates || []).filter((entry) => entry.status === "registered").at(0);
150
+ if (!candidate || candidate.scores.length)
151
+ return undefined;
152
+ const evidence = explicitEvidence(options);
153
+ return { candidate, evidence: evidence.length ? evidence : candidateEvidence(candidate) };
154
+ }
155
+ function topRankedCandidate(run) {
156
+ const ranking = (0, candidate_scoring_io_1.rankCandidates)(run);
157
+ const first = ranking.candidates.find((entry) => entry.scoreCount > 0 && entry.verdict !== "fail");
158
+ return first ? (run.candidates || []).find((candidate) => candidate.id === first.candidateId) : undefined;
159
+ }
160
+ function nextSelectionPlan(run, options) {
161
+ const candidate = resolveCandidate(run, options) || topRankedCandidate(run);
162
+ if (!candidate || (run.candidateSelections || []).some((entry) => entry.candidateId === candidate.id))
163
+ return undefined;
164
+ if (!candidate.scores.length)
165
+ return undefined;
166
+ return { candidate, scoreId: stringOption(options.score || options.scoreId) };
167
+ }
168
+ function parseCriteria(options, fallback) {
169
+ const criteria = {};
170
+ const structured = options.criteria;
171
+ if (structured && typeof structured === "object" && !Array.isArray(structured)) {
172
+ for (const [key, value] of Object.entries(structured)) {
173
+ const parsed = Number(value);
174
+ if (key && Number.isFinite(parsed))
175
+ criteria[key] = parsed;
176
+ }
177
+ }
178
+ for (const entry of arrayOption(options.criterion || (typeof structured === "string" ? structured : undefined) || options.score)) {
179
+ const [key, value] = String(entry).split("=");
180
+ if (!key || value === undefined)
181
+ continue;
182
+ const parsed = Number(value);
183
+ if (Number.isFinite(parsed))
184
+ criteria[key] = parsed;
185
+ }
186
+ if (!Object.keys(criteria).length && options.total !== undefined)
187
+ criteria.total = Number(options.total);
188
+ if (!Object.keys(criteria).length && fallback)
189
+ return fallback;
190
+ if (!Object.keys(criteria).length)
191
+ throw new Error("Missing score criteria. Use --criterion name=value");
192
+ return criteria;
193
+ }
194
+ function authorityOptions(options) {
195
+ const agentRoleId = stringOption(options.role || options.roleId || options["multi-agent-role"]);
196
+ const agentGroupId = stringOption(options.group || options.groupId || options["multi-agent-group"]);
197
+ const agentMembershipId = stringOption(options.membership || options.membershipId || options["multi-agent-membership"]);
198
+ const actor = agentMembershipId ? { kind: "membership", id: agentMembershipId } : agentRoleId ? { kind: "role", id: agentRoleId } : agentGroupId ? { kind: "group", id: agentGroupId } : undefined;
199
+ return { actor, multiAgentRunId: stringOption(options.multiAgentRun || options.multiAgentRunId || options["multi-agent-run"]), agentRoleId, agentGroupId, agentMembershipId };
200
+ }
201
+ function linkTopologyRecord(topology, links) {
202
+ const faninIds = links.faninIds || [];
203
+ const candidateIds = links.candidateIds || [];
204
+ const selectionIds = links.selectionIds || [];
205
+ topology.faninIds = unique([...topology.faninIds, ...faninIds]);
206
+ topology.candidateIds = unique([...topology.candidateIds, ...candidateIds]);
207
+ topology.selectionIds = unique([...topology.selectionIds, ...selectionIds]);
208
+ topology.links.agentFaninIds = unique([...topology.links.agentFaninIds, ...faninIds]);
209
+ topology.links.candidateIds = unique([...topology.links.candidateIds, ...candidateIds]);
210
+ topology.links.selectionIds = unique([...topology.links.selectionIds, ...selectionIds]);
211
+ topology.updatedAt = new Date().toISOString();
212
+ }
213
+ function evidenceRequirements(active, extra) {
214
+ return unique([...active.flatMap((entry) => entry.missingEvidence), ...extra]);
215
+ }
216
+ // ---------------------------------------------------------------------------
217
+ // Envelope + host state classification
218
+ // ---------------------------------------------------------------------------
219
+ function workerSummary(run) {
220
+ const index = run.workers || [];
221
+ return {
222
+ manifestPaths: index.map((entry) => `${entry.workerDir}/manifest.json`).filter(Boolean),
223
+ resultPaths: index.map((entry) => entry.resultPath).filter((p) => Boolean(p)),
224
+ workers: index.map((entry) => ({ status: entry.status })),
225
+ };
226
+ }
227
+ function candidateSummary(run) {
228
+ const selections = run.candidateSelections || [];
229
+ const commits = run.commits || [];
230
+ const readyForCommit = selections.filter((selection) => !commits.some((commit) => commit.selectionId === selection.id)).map((selection) => ({ selectionId: selection.id }));
231
+ return { readyForCommit, latestRankingPath: `${run.paths.candidatesDir || `${run.paths.runDir}/candidates`}/ranking.json` };
232
+ }
233
+ function feedbackSummary(run) {
234
+ const records = run.feedback || [];
235
+ return { open: records.filter((entry) => entry.status === "open").map((entry) => ({ retryable: entry.retryable })) };
236
+ }
237
+ function commitSummary(run) {
238
+ return { verifierGated: (run.commits || []).filter((entry) => entry.verifierGated).length };
239
+ }
240
+ function classifyHostState(run) {
241
+ const active = activeTopologies(run);
242
+ const feedback = feedbackSummary(run);
243
+ const workers = workerSummary(run);
244
+ const candidates = candidateSummary(run);
245
+ const commits = commitSummary(run);
246
+ const runs = run.multiAgent?.runs || [];
247
+ if (!active.length && !runs.length)
248
+ return "needs-run";
249
+ if (feedback.open.some((entry) => !entry.retryable))
250
+ return "failed";
251
+ if (feedback.open.length)
252
+ return "blocked";
253
+ if (commits.verifierGated > 0 && candidates.readyForCommit.length === 0)
254
+ return "complete";
255
+ if (candidates.readyForCommit.length)
256
+ return "ready-for-commit";
257
+ const candidateRecords = run.candidates || [];
258
+ if ((run.candidateSelections || []).length === 0 && candidateRecords.some((candidate) => candidate.status === "scored" || candidate.status === "verified"))
259
+ return "ready-for-selection";
260
+ if (candidateRecords.some((candidate) => candidate.status === "registered"))
261
+ return "ready-for-scoring";
262
+ if (workers.workers.some((worker) => worker.status === "allocated" || worker.status === "running"))
263
+ return "awaiting-worker-output";
264
+ if (nextCandidatePlan(run))
265
+ return "ready-for-scoring";
266
+ if (nextFaninPlan(run, active[0]))
267
+ return "ready-for-fanin";
268
+ if (nextDispatchPlan(run, active[0], {}))
269
+ return "ready-for-dispatch";
270
+ return "blocked";
271
+ }
272
+ function activeIds(run, active) {
273
+ const multi = run.multiAgent || {};
274
+ const blackboard = run.blackboard || {};
275
+ return {
276
+ topologyRunIds: active.map((entry) => entry.id),
277
+ topologyIds: active.map((entry) => entry.topologyId),
278
+ multiAgentRunIds: unique([...active.map((entry) => entry.multiAgentRunId), ...(multi.runs || []).map((entry) => entry.id)]),
279
+ blackboardIds: unique([...active.map((entry) => entry.blackboardId), ...(blackboard.boards || []).map((entry) => entry.id)]),
280
+ topicIds: unique(active.flatMap((entry) => entry.topicIds)),
281
+ groupIds: unique(active.flatMap((entry) => entry.groupIds)),
282
+ roleIds: unique(active.flatMap((entry) => entry.roleIds)),
283
+ fanoutIds: unique(active.flatMap((entry) => entry.fanoutIds)),
284
+ faninIds: unique(active.flatMap((entry) => entry.faninIds)),
285
+ candidateIds: unique([...(run.candidates || []).map((entry) => entry.id), ...active.flatMap((entry) => entry.candidateIds)]),
286
+ selectionIds: unique([...(run.candidateSelections || []).map((entry) => entry.id), ...active.flatMap((entry) => entry.selectionIds)]),
287
+ commitIds: (run.commits || []).map((entry) => entry.id),
288
+ auditEventIds: unique(active.flatMap((entry) => entry.links.auditEventIds)),
289
+ };
290
+ }
291
+ function hostNextActions(run, state, active, requiredHostAction, readyForCommit) {
292
+ if (requiredHostAction)
293
+ return [{ command: "host-action", reason: requiredHostAction, priority: "high" }];
294
+ const runId = run.id;
295
+ switch (state) {
296
+ case "needs-run":
297
+ return [{ command: `node scripts/cw.js multi-agent run ${runId} --topology map-reduce`, reason: "Materialize a host-facing multi-agent topology.", priority: "high" }];
298
+ case "ready-for-dispatch":
299
+ return [{ command: `node scripts/cw.js multi-agent step ${runId}`, reason: "Create the next dispatch manifest without spawning workers.", priority: "high" }];
300
+ case "awaiting-worker-output":
301
+ return [{ command: `node scripts/cw.js worker output ${runId} <worker-id> <result.md>`, reason: "A host-executed worker must report result evidence.", priority: "high" }];
302
+ case "ready-for-fanin":
303
+ return [{ command: `node scripts/cw.js multi-agent step ${runId}`, reason: "Collect fanin once required worker evidence is present.", priority: "high" }];
304
+ case "ready-for-scoring":
305
+ return [{ command: `node scripts/cw.js multi-agent score ${runId} --candidate <candidate-id> --criterion correctness=1 --evidence <path-or-ref>`, reason: "Score a candidate with explicit evidence.", priority: "high" }];
306
+ case "ready-for-selection":
307
+ return [{ command: `node scripts/cw.js multi-agent select ${runId} --candidate <candidate-id> --reason "<rationale>"`, reason: "Select a scored candidate after verifier gates pass.", priority: "high" }];
308
+ case "ready-for-commit": {
309
+ const ready = readyForCommit[0];
310
+ return [{ command: `node scripts/cw.js commit ${runId} --selection ${ready.selectionId} --reason "<verified rationale>"`, reason: "Create a verifier-gated CW state commit.", priority: "high" }];
311
+ }
312
+ case "complete":
313
+ return [{ command: `node scripts/cw.js report ${runId} --show`, reason: "Review the completed run report.", priority: "normal" }];
314
+ case "failed":
315
+ case "blocked":
316
+ default:
317
+ return [{ command: `node scripts/cw.js multi-agent status ${runId}`, reason: active.length > 1 ? "Resolve ambiguous active topology state." : "Inspect specific blocked reasons.", priority: "high" }];
318
+ }
319
+ }
320
+ function envelope(run, command, options = {}) {
321
+ const active = activeTopologies(run);
322
+ const topologies = (0, topology_io_1.summarizeTopologies)(run);
323
+ const multiAgent = (0, multi_agent_io_1.summarizeMultiAgent)(run);
324
+ const blackboard = (0, coordinator_io_1.summarizeBlackboard)(run);
325
+ const workers = workerSummary(run);
326
+ const candidates = candidateSummary(run);
327
+ const feedback = feedbackSummary(run);
328
+ const commits = commitSummary(run);
329
+ const events = (0, trust_audit_1.listTrustAuditEvents)(run);
330
+ const trust = { eventCount: events.length };
331
+ const blockedReasons = unique([...(options.extraBlockedReasons || [])]);
332
+ const state = blockedReasons.length ? "blocked" : classifyHostState(run);
333
+ const ids = activeIds(run, active);
334
+ const nextActions = hostNextActions(run, state, active, options.requiredHostAction, candidates.readyForCommit);
335
+ return {
336
+ schemaVersion: 1,
337
+ surface: "multi-agent-host",
338
+ command,
339
+ runId: run.id,
340
+ state,
341
+ performed: options.performed,
342
+ nextAction: nextActions[0]?.command,
343
+ nextActions,
344
+ blockedReasons,
345
+ requiredHostAction: options.requiredHostAction,
346
+ evidenceRequirements: evidenceRequirements(active, multiAgent.blockedReasons),
347
+ ids,
348
+ paths: { statePath: run.paths.state, reportPath: run.paths.report, blackboardIndexPath: blackboard.indexPath, auditSummaryPath: run.audit?.summaryPath, auditEventLogPath: run.audit?.eventLogPath, candidateRankingPath: candidates.latestRankingPath, workerManifestPaths: workers.manifestPaths, workerResultPaths: workers.resultPaths },
349
+ summaries: { topologies, multiAgent, multiAgentOperator: (0, multi_agent_operator_ux_1.summarizeMultiAgentOperator)(run), blackboard, workers, candidates, feedback, commits, trust },
350
+ data: options.data,
351
+ };
352
+ }
353
+ // ---------------------------------------------------------------------------
354
+ // Host verbs
355
+ // ---------------------------------------------------------------------------
356
+ function hostRun(run, options = {}) {
357
+ const topologyId = stringOption(options.topology || options.topologyId || options.id);
358
+ if (!topologyId)
359
+ return hostStatus(run, "run");
360
+ const existing = activeTopologies(run).find((entry) => entry.topologyId === topologyId && !isTerminalTopology(entry));
361
+ const topologyRun = existing ||
362
+ (0, topology_io_1.applyTopology)(run, topologyId, {
363
+ id: stringOption(options.topologyRun || options.topologyRunId || options["topology-run"] || options["topology-run-id"] || options.name),
364
+ title: stringOption(options.title),
365
+ multiAgentRunId: stringOption(options.multiAgentRun || options.multiAgentRunId),
366
+ blackboardId: stringOption(options.blackboard || options.blackboardId),
367
+ taskIds: arrayOption(options.task || options.taskId || options.tasks).map(String),
368
+ mapperCount: numberOption(options.mapperCount || options["mapper-count"] || options.mappers || options.mapper),
369
+ judgeCount: numberOption(options.judgeCount || options["judge-count"] || options.judges || options.judge),
370
+ debateRounds: numberOption(options.debateRounds || options["debate-rounds"] || options.rounds),
371
+ collectInitialFanin: Boolean(options.collectInitialFanin || options["collect-initial-fanin"]),
372
+ metadata: { hostSurface: "multi-agent.run" },
373
+ });
374
+ return envelope(run, "run", { performed: existing ? "attached-topology" : "applied-topology", data: { topologyRun, dispatchCreated: false, note: "Workers are not dispatched by multi-agent run; call multi-agent step or dispatch explicitly." } });
375
+ }
376
+ function hostStatus(run, command = "status") {
377
+ return envelope(run, command);
378
+ }
379
+ function hostStep(run, options = {}) {
380
+ const state = classifyHostState(run);
381
+ if (state === "complete")
382
+ return envelope(run, "step", { performed: "none", requiredHostAction: "Review the completed run report; no further host step is available." });
383
+ if (state === "ready-for-commit")
384
+ return envelope(run, "step", { performed: "none", requiredHostAction: "Run the recommended verifier-gated commit command before taking more steps." });
385
+ if (state === "needs-run")
386
+ return envelope(run, "step", { performed: "none", requiredHostAction: "Create or attach a multi-agent run before stepping." });
387
+ if (state === "blocked" || state === "failed")
388
+ return envelope(run, "step", { performed: "none", requiredHostAction: "Resolve blocked reasons, then rerun multi-agent status." });
389
+ const topology = requireSingleActiveTopology(run);
390
+ const runningWorkers = (run.workers || []).filter((worker) => worker.status === "allocated" || worker.status === "running");
391
+ if (runningWorkers.length) {
392
+ return envelope(run, "step", { performed: "none", requiredHostAction: `Execute ${runningWorkers.length} worker(s), write result.md, then record output with worker output or result.`, extraBlockedReasons: runningWorkers.map((worker) => `worker ${worker.id} is ${worker.status}`) });
393
+ }
394
+ const faninPlan = nextFaninPlan(run, topology);
395
+ if (faninPlan) {
396
+ const fanin = (0, multi_agent_io_1.collectAgentFanin)(run, faninPlan);
397
+ linkTopologyRecord(topology, { faninIds: [fanin.id] });
398
+ return envelope(run, "step", { performed: fanin.status === "ready" ? "collected-fanin" : "collected-blocked-fanin", data: fanin, requiredHostAction: fanin.status === "ready" ? undefined : "Add missing worker evidence or blackboard artifacts before continuing.", extraBlockedReasons: fanin.status === "ready" ? [] : fanin.blockedReasons });
399
+ }
400
+ const dispatchPlan = nextDispatchPlan(run, topology, options);
401
+ if (dispatchPlan) {
402
+ // Bind this step's dispatch to the topology's multi-agent run/group/role/
403
+ // fanout so `attachDispatchToMultiAgent` creates the AgentMembership. Without
404
+ // this binding the fanout's roles never got a membership, so a later
405
+ // board-scoped fanin reported every required role as "has no membership"
406
+ // instead of advancing on real evidence. Byte-behavior port of the old host
407
+ // step's dispatch attachment (orchestrator hostMultiAgentStep).
408
+ const manifest = (0, dispatch_1.createDispatchManifest)(run, dispatchPlan.limit, {
409
+ sandboxProfileId: dispatchPlan.sandboxProfileId,
410
+ backendId: dispatchPlan.backendId,
411
+ multiAgentRunId: topology.multiAgentRunId,
412
+ multiAgentGroupId: dispatchPlan.groupId,
413
+ multiAgentRoleId: dispatchPlan.roleId,
414
+ multiAgentFanoutId: dispatchPlan.fanoutId,
415
+ });
416
+ return envelope(run, "step", { performed: manifest.dispatchId ? "created-dispatch-manifest" : "none", data: manifest, requiredHostAction: manifest.dispatchId ? "Spawn the worker described by the manifest and record its result." : "No runnable pending task is available for dispatch." });
417
+ }
418
+ const blackboards = run.blackboard || {};
419
+ const snapshotPlan = (blackboards.boards || []).find((board) => board.id === topology.blackboardId)?.id;
420
+ if (snapshotPlan && !(blackboards.snapshots || []).some((snapshot) => snapshot.blackboardId === snapshotPlan)) {
421
+ const snapshot = (0, coordinator_io_1.createBlackboardSnapshot)(run, snapshotPlan);
422
+ return envelope(run, "step", { performed: "created-blackboard-snapshot", data: snapshot });
423
+ }
424
+ const candidatePlan = nextCandidatePlan(run);
425
+ if (candidatePlan) {
426
+ const candidate = (0, candidate_scoring_io_1.registerCandidate)(run, { id: stringOption(options.candidate || options.candidateId), kind: "worker-output", workerId: candidatePlan.id, taskId: candidatePlan.taskId, resultNodeId: candidatePlan.resultNodeId, verifierNodeId: candidatePlan.output?.verifierNodeId, resultPath: candidatePlan.resultPath, metadata: { hostSurface: "multi-agent.step" } }, { persist: false });
427
+ linkTopologyRecord(topology, { candidateIds: [candidate.id] });
428
+ return envelope(run, "step", { performed: "registered-candidate", data: candidate });
429
+ }
430
+ const scorePlan = nextScorePlan(run, options);
431
+ if (scorePlan) {
432
+ const score = (0, candidate_scoring_io_1.scoreCandidate)(run, scorePlan.candidate.id, { scorer: stringOption(options.scorer) || "multi-agent-host", criteria: parseCriteria(options, { correctness: 1, evidence: 1, fit: 1 }), maxTotal: numberOption(options.maxTotal || options.max) || 3, verdict: stringOption(options.verdict), evidence: scorePlan.evidence, notes: stringOption(options.notes) || "Host step scored candidate with existing verifier evidence.", metadata: { hostSurface: "multi-agent.step", topologyRunId: topology.id } }, { persist: false });
433
+ (0, coordinator_io_1.recordCoordinatorDecision)(run, { blackboardId: topology.blackboardId, topicId: topology.topicIds[0], kind: "candidate-synthesis", outcome: score.verdict === "fail" ? "blocked" : "accepted", reason: `Candidate ${score.candidateId} scored ${score.total}/${score.maxTotal}.`, subjectIds: [score.candidateId, score.id], evidenceRefs: score.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean), metadata: { hostSurface: "multi-agent.step", scoreId: score.id } });
434
+ return envelope(run, "step", { performed: "scored-candidate", data: score });
435
+ }
436
+ const selectionPlan = nextSelectionPlan(run, options);
437
+ if (selectionPlan) {
438
+ const selection = (0, candidate_scoring_io_1.selectCandidate)(run, selectionPlan.candidate.id, { selectedBy: stringOption(options.by || options.selectedBy) || "multi-agent-host", reason: stringOption(options.reason) || "Selected by high-level multi-agent host step after scoring and verifier evidence.", scoreId: selectionPlan.scoreId, allowUnverified: Boolean(options.allowUnverified) }, { persist: false });
439
+ linkTopologyRecord(topology, { selectionIds: [selection.id] });
440
+ (0, coordinator_io_1.recordCoordinatorDecision)(run, { blackboardId: topology.blackboardId, topicId: topology.topicIds.at(-1), kind: "candidate-synthesis", outcome: "accepted", reason: selection.reason, subjectIds: [selection.candidateId, selection.id], evidenceRefs: selection.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean), metadata: { hostSurface: "multi-agent.step", selectionId: selection.id } });
441
+ return envelope(run, "step", { performed: "selected-candidate", data: selection });
442
+ }
443
+ return envelope(run, "step", { performed: "none", requiredHostAction: "No safe deterministic step is available. Use multi-agent status for the next explicit command." });
444
+ }
445
+ function resolveHostBlackboardId(run, topology, options) {
446
+ const explicit = stringOption(options.blackboard || options.blackboardId);
447
+ if (explicit)
448
+ return explicit;
449
+ if (topology?.blackboardId)
450
+ return topology.blackboardId;
451
+ const boards = (run.blackboard || {}).boards || [];
452
+ if (boards.length === 1)
453
+ return boards[0].id;
454
+ if (!boards.length)
455
+ throw new Error(`Run ${run.id} has no blackboard. Use multi-agent run --topology <id> first.`);
456
+ throw new Error(`Ambiguous blackboard state: ${boards.map((board) => board.id).join(", ")}`);
457
+ }
458
+ function resolveHostTopicId(run, blackboardId, options, action) {
459
+ const explicit = stringOption(options.topic || options.topicId);
460
+ if (explicit)
461
+ return explicit;
462
+ if (action === "summary" || action === "board" || action === "topics" || action === "artifacts" || action === "list-artifacts" || action === "snapshot")
463
+ return undefined;
464
+ const topics = ((run.blackboard || {}).topics || []).filter((topic) => topic.blackboardId === blackboardId);
465
+ if (topics.length === 1)
466
+ return topics[0].id;
467
+ if (!topics.length)
468
+ throw new Error(`Blackboard ${blackboardId} has no topics`);
469
+ throw new Error(`Ambiguous blackboard topic state: ${topics.map((topic) => topic.id).join(", ")}`);
470
+ }
471
+ function hostBlackboard(run, action, options = {}) {
472
+ const topology = optionalSingleActiveTopology(run);
473
+ const blackboardId = resolveHostBlackboardId(run, topology, options);
474
+ const topicId = stringOption(options.topic || options.topicId) || resolveHostTopicId(run, blackboardId, options, action);
475
+ let data;
476
+ let performed = "read-blackboard";
477
+ switch (action || "summary") {
478
+ case "summary":
479
+ case "board":
480
+ data = (0, coordinator_io_1.summarizeBlackboard)(run, blackboardId);
481
+ break;
482
+ case "topics":
483
+ case "list-topics":
484
+ data = ((run.blackboard || {}).topics || []).filter((topic) => topic.blackboardId === blackboardId);
485
+ break;
486
+ case "messages":
487
+ case "list-messages":
488
+ data = (0, coordinator_io_1.listBlackboardMessages)(run, { blackboardId, topicId });
489
+ break;
490
+ case "post":
491
+ case "message":
492
+ data = (0, coordinator_io_1.postBlackboardMessage)(run, { topicId: requireString(topicId, "topic id"), blackboardId, body: requireString(options.body || options.message, "message body"), evidenceRefs: arrayOption(options.evidence || options.evidenceRef).map(String), artifactRefIds: arrayOption(options.artifact || options.artifactRef || options.artifactRefId).map(String), metadata: { hostSurface: "multi-agent.blackboard" } });
493
+ performed = "posted-message";
494
+ break;
495
+ case "artifacts":
496
+ case "list-artifacts":
497
+ data = (0, coordinator_io_1.listBlackboardArtifacts)(run, { blackboardId, topicId });
498
+ break;
499
+ case "add-artifact":
500
+ case "artifact":
501
+ data = (0, coordinator_io_1.addBlackboardArtifact)(run, { id: stringOption(options.id), blackboardId, topicId, kind: requireString(options.kind, "artifact kind"), path: stringOption(options.path), locator: stringOption(options.locator), source: stringOption(options.source) || "multi-agent-host", evidenceRefs: arrayOption(options.evidence || options.evidenceRef).map(String), metadata: { hostSurface: "multi-agent.blackboard" } });
502
+ performed = "added-artifact";
503
+ break;
504
+ case "context":
505
+ case "put-context":
506
+ data = (0, coordinator_io_1.putBlackboardContext)(run, { blackboardId, topicId: requireString(topicId, "topic id"), kind: requireString(options.kind, "context kind"), key: stringOption(options.key), value: requireString(options.value || options.body, "context value"), evidenceRefs: arrayOption(options.evidence || options.evidenceRef).map(String), artifactRefIds: arrayOption(options.artifact || options.artifactRef || options.artifactRefId).map(String), metadata: { hostSurface: "multi-agent.blackboard" } });
507
+ performed = "put-context";
508
+ break;
509
+ case "snapshot":
510
+ data = (0, coordinator_io_1.createBlackboardSnapshot)(run, blackboardId);
511
+ performed = "created-snapshot";
512
+ break;
513
+ default:
514
+ throw new Error("Usage: multi-agent blackboard <run-id> [summary|topics|messages|post|artifacts|add-artifact|context|snapshot]");
515
+ }
516
+ return envelope(run, "blackboard", { performed, data });
517
+ }
518
+ function hostScore(run, options = {}) {
519
+ const topology = optionalSingleActiveTopology(run);
520
+ let candidate = resolveCandidate(run, options);
521
+ if (!candidate && stringOption(options.worker || options.workerId)) {
522
+ candidate = (0, candidate_scoring_io_1.registerCandidate)(run, { id: stringOption(options.candidate || options.candidateId || options.id), kind: "worker-output", workerId: requireString(options.worker || options.workerId, "worker id"), metadata: { hostSurface: "multi-agent.score" } }, { persist: false });
523
+ }
524
+ if (!candidate)
525
+ throw new Error("multi-agent score requires --candidate or --worker when a single candidate cannot be inferred");
526
+ const evidence = explicitEvidence(options);
527
+ if (!evidence.length)
528
+ throw new Error(`Candidate ${candidate.id} score requires evidence`);
529
+ const authority = authorityOptions(options);
530
+ if (authority.agentRoleId || authority.agentMembershipId) {
531
+ const rationale = stringOption(options.rationale || options.notes || options.reason);
532
+ if (!rationale)
533
+ throw new Error(`Candidate ${candidate.id} judge score requires rationale`);
534
+ const permission = (0, trust_policy_io_1.assertMultiAgentActionAllowed)(run, { operation: "judge.rationale", actor: authority.actor, multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId, agentRoleId: authority.agentRoleId, agentGroupId: authority.agentGroupId, agentMembershipId: authority.agentMembershipId, blackboardId: topology?.blackboardId, blackboardTopicId: topology?.topicIds[0], candidateId: candidate.id, evidenceRefs: evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean) });
535
+ (0, trust_policy_io_1.recordJudgeRationaleAudit)(run, { kind: "judge.rationale", actor: authority.actor, multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId, agentRoleId: authority.agentRoleId, agentGroupId: authority.agentGroupId, agentMembershipId: authority.agentMembershipId, blackboardId: topology?.blackboardId, blackboardTopicId: topology?.topicIds[0], candidateId: candidate.id, evidenceRefs: evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean), rationale, policyRef: permission.policyRef, parentEventIds: [permission.event.id] });
536
+ }
537
+ const score = (0, candidate_scoring_io_1.scoreCandidate)(run, candidate.id, { id: stringOption(options.score || options.scoreId), scorer: stringOption(options.scorer) || "multi-agent-host", criteria: parseCriteria(options), maxTotal: numberOption(options.maxTotal || options.max), verdict: stringOption(options.verdict), evidence, notes: stringOption(options.notes), metadata: { hostSurface: "multi-agent.score", topologyRunId: topology?.id } }, { persist: false });
538
+ if (topology) {
539
+ linkTopologyRecord(topology, { candidateIds: [candidate.id] });
540
+ (0, coordinator_io_1.recordCoordinatorDecision)(run, { blackboardId: topology.blackboardId, topicId: topology.topicIds[0], kind: "candidate-synthesis", outcome: score.verdict === "fail" ? "blocked" : "accepted", reason: `Host scored candidate ${candidate.id} with score ${score.id}.`, subjectIds: [candidate.id, score.id], evidenceRefs: score.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean), metadata: { hostSurface: "multi-agent.score", scoreId: score.id } });
541
+ }
542
+ return envelope(run, "score", { performed: "scored-candidate", data: score });
543
+ }
544
+ function hostSelect(run, options = {}) {
545
+ const topology = optionalSingleActiveTopology(run);
546
+ const candidate = resolveCandidate(run, options) || topRankedCandidate(run);
547
+ if (!candidate)
548
+ throw new Error("multi-agent select requires a scored candidate");
549
+ const authority = authorityOptions(options);
550
+ if (authority.agentRoleId || authority.agentMembershipId) {
551
+ const scoreId = stringOption(options.score || options.scoreId) || candidate.scores.at(-1);
552
+ if (!(0, trust_policy_io_1.hasAcceptedJudgeRationale)(run, { multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId, candidateId: candidate.id, scoreId })) {
553
+ throw new Error(`Candidate ${candidate.id} selection requires accepted judge rationale with evidence`);
554
+ }
555
+ (0, trust_policy_io_1.assertMultiAgentActionAllowed)(run, { operation: "candidate.select", actor: authority.actor, multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId, agentRoleId: authority.agentRoleId, agentGroupId: authority.agentGroupId, agentMembershipId: authority.agentMembershipId, blackboardId: topology?.blackboardId, blackboardTopicId: topology?.topicIds.at(-1), candidateId: candidate.id, scoreId, evidenceRefs: explicitEvidence(options).map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean) });
556
+ }
557
+ const selection = (0, candidate_scoring_io_1.selectCandidate)(run, candidate.id, { selectedBy: stringOption(options.by || options.selectedBy) || "multi-agent-host", reason: requireString(options.reason || "Selected by high-level multi-agent host surface.", "selection reason"), scoreId: stringOption(options.score || options.scoreId), allowUnverified: Boolean(options.allowUnverified) }, { persist: false, policy: { requireVerifierGate: options.requireVerifierGate === undefined ? undefined : Boolean(options.requireVerifierGate), minNormalized: numberOption(options.minNormalized) } });
558
+ if (topology) {
559
+ linkTopologyRecord(topology, { candidateIds: [candidate.id], selectionIds: [selection.id] });
560
+ (0, coordinator_io_1.recordCoordinatorDecision)(run, { blackboardId: topology.blackboardId, topicId: topology.topicIds.at(-1), kind: "candidate-synthesis", outcome: "accepted", reason: selection.reason, subjectIds: [selection.candidateId, selection.id], evidenceRefs: selection.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean), author: authority.actor, links: { multiAgentRunId: authority.multiAgentRunId || topology.multiAgentRunId, agentGroupId: authority.agentGroupId, agentRoleId: authority.agentRoleId, agentMembershipId: authority.agentMembershipId }, metadata: { hostSurface: "multi-agent.select", selectionId: selection.id } });
561
+ }
562
+ return envelope(run, "select", { performed: "selected-candidate", data: selection });
563
+ }