cool-workflow 0.1.98 → 0.2.0

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