cool-workflow 0.1.78

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 (193) hide show
  1. package/.claude-plugin/plugin.json +20 -0
  2. package/.codex-plugin/mcp.json +10 -0
  3. package/.codex-plugin/plugin.json +38 -0
  4. package/.mcp.json +10 -0
  5. package/LICENSE +24 -0
  6. package/README.md +638 -0
  7. package/apps/architecture-review/app.json +51 -0
  8. package/apps/architecture-review/workflow.js +116 -0
  9. package/apps/end-to-end-golden-path/app.json +30 -0
  10. package/apps/end-to-end-golden-path/workflow.js +33 -0
  11. package/apps/pr-review-fix-ci/app.json +59 -0
  12. package/apps/pr-review-fix-ci/workflow.js +90 -0
  13. package/apps/release-cut/app.json +54 -0
  14. package/apps/release-cut/workflow.js +82 -0
  15. package/apps/research-synthesis/app.json +50 -0
  16. package/apps/research-synthesis/workflow.js +76 -0
  17. package/apps/workflow-app-framework-demo/app.json +29 -0
  18. package/apps/workflow-app-framework-demo/workflow.js +44 -0
  19. package/dist/agent-config.js +223 -0
  20. package/dist/candidate-scoring.js +715 -0
  21. package/dist/capability-core.js +630 -0
  22. package/dist/capability-dispatcher.js +86 -0
  23. package/dist/capability-registry.js +523 -0
  24. package/dist/cli.js +1276 -0
  25. package/dist/collaboration.js +727 -0
  26. package/dist/commit.js +570 -0
  27. package/dist/contract-migration.js +234 -0
  28. package/dist/coordinator.js +1163 -0
  29. package/dist/daemon.js +44 -0
  30. package/dist/dispatch.js +201 -0
  31. package/dist/drive.js +503 -0
  32. package/dist/error-feedback.js +415 -0
  33. package/dist/evidence-grounding.js +179 -0
  34. package/dist/evidence-reasoning.js +733 -0
  35. package/dist/execution-backend.js +1279 -0
  36. package/dist/harness.js +61 -0
  37. package/dist/mcp-server.js +1615 -0
  38. package/dist/multi-agent-eval.js +857 -0
  39. package/dist/multi-agent-host.js +764 -0
  40. package/dist/multi-agent-operator-ux.js +537 -0
  41. package/dist/multi-agent-trust.js +366 -0
  42. package/dist/multi-agent.js +1173 -0
  43. package/dist/node-snapshot.js +270 -0
  44. package/dist/observability.js +922 -0
  45. package/dist/operator-ux.js +971 -0
  46. package/dist/orchestrator/audit-operations.js +182 -0
  47. package/dist/orchestrator/candidate-operations.js +117 -0
  48. package/dist/orchestrator/cli-options.js +288 -0
  49. package/dist/orchestrator/collaboration-operations.js +86 -0
  50. package/dist/orchestrator/feedback-operations.js +81 -0
  51. package/dist/orchestrator/host-operations.js +78 -0
  52. package/dist/orchestrator/lifecycle-operations.js +462 -0
  53. package/dist/orchestrator/migration-operations.js +44 -0
  54. package/dist/orchestrator/multi-agent-operations.js +362 -0
  55. package/dist/orchestrator/report.js +369 -0
  56. package/dist/orchestrator/topology-operations.js +84 -0
  57. package/dist/orchestrator.js +874 -0
  58. package/dist/pipeline-contract.js +92 -0
  59. package/dist/pipeline-runner.js +285 -0
  60. package/dist/reclamation.js +882 -0
  61. package/dist/result-normalize.js +194 -0
  62. package/dist/run-export.js +64 -0
  63. package/dist/run-registry.js +1347 -0
  64. package/dist/run-state-schema.js +67 -0
  65. package/dist/sandbox-profile.js +471 -0
  66. package/dist/scheduler.js +266 -0
  67. package/dist/scheduling.js +184 -0
  68. package/dist/schema-validate.js +98 -0
  69. package/dist/state-explosion.js +1213 -0
  70. package/dist/state-migrations.js +463 -0
  71. package/dist/state-node.js +301 -0
  72. package/dist/state.js +308 -0
  73. package/dist/telemetry-attestation.js +156 -0
  74. package/dist/telemetry-ledger.js +145 -0
  75. package/dist/topology.js +527 -0
  76. package/dist/triggers.js +159 -0
  77. package/dist/trust-audit.js +475 -0
  78. package/dist/types/blackboard.js +2 -0
  79. package/dist/types/boundary.js +29 -0
  80. package/dist/types/candidate.js +2 -0
  81. package/dist/types/collaboration.js +2 -0
  82. package/dist/types/core.js +2 -0
  83. package/dist/types/drive.js +10 -0
  84. package/dist/types/error-feedback.js +2 -0
  85. package/dist/types/evidence-reasoning.js +2 -0
  86. package/dist/types/execution-backend.js +2 -0
  87. package/dist/types/multi-agent.js +2 -0
  88. package/dist/types/observability.js +2 -0
  89. package/dist/types/pipeline.js +2 -0
  90. package/dist/types/reclamation.js +8 -0
  91. package/dist/types/result.js +2 -0
  92. package/dist/types/run-registry.js +2 -0
  93. package/dist/types/run.js +2 -0
  94. package/dist/types/sandbox.js +2 -0
  95. package/dist/types/schedule.js +2 -0
  96. package/dist/types/state-node.js +2 -0
  97. package/dist/types/topology.js +2 -0
  98. package/dist/types/trust.js +2 -0
  99. package/dist/types/workbench.js +2 -0
  100. package/dist/types/worker.js +2 -0
  101. package/dist/types/workflow-app.js +2 -0
  102. package/dist/types.js +43 -0
  103. package/dist/verifier-registry.js +46 -0
  104. package/dist/verifier.js +78 -0
  105. package/dist/version.js +8 -0
  106. package/dist/workbench-host.js +172 -0
  107. package/dist/workbench.js +190 -0
  108. package/dist/worker-isolation.js +1028 -0
  109. package/dist/workflow-api.js +98 -0
  110. package/dist/workflow-app-framework.js +626 -0
  111. package/docs/agent-delegation-drive.7.md +190 -0
  112. package/docs/agent-framework.md +176 -0
  113. package/docs/candidate-scoring.7.md +106 -0
  114. package/docs/canonical-workflow-apps.7.md +137 -0
  115. package/docs/capability-topology-registry.7.md +168 -0
  116. package/docs/cli-mcp-parity.7.md +373 -0
  117. package/docs/contract-migration-tooling.7.md +123 -0
  118. package/docs/control-plane-scheduling.7.md +110 -0
  119. package/docs/coordinator-blackboard.7.md +183 -0
  120. package/docs/dogfood/architecture-review-cool-workflow.md +16 -0
  121. package/docs/dogfood-one-real-repo.7.md +168 -0
  122. package/docs/durable-state-and-locking.7.md +107 -0
  123. package/docs/end-to-end-golden-path.7.md +117 -0
  124. package/docs/error-feedback.7.md +153 -0
  125. package/docs/evidence-adoption-reasoning-chain.7.md +270 -0
  126. package/docs/execution-backends.7.md +300 -0
  127. package/docs/getting-started.md +99 -0
  128. package/docs/index.md +41 -0
  129. package/docs/mcp-app-surface.7.md +235 -0
  130. package/docs/multi-agent-cli-mcp-surface.7.md +265 -0
  131. package/docs/multi-agent-eval-replay-harness.7.md +302 -0
  132. package/docs/multi-agent-operator-ux.7.md +314 -0
  133. package/docs/multi-agent-runtime-core.7.md +231 -0
  134. package/docs/multi-agent-topologies.7.md +103 -0
  135. package/docs/multi-agent-trust-policy-audit.7.md +154 -0
  136. package/docs/node-snapshot-diff-replay.7.md +135 -0
  137. package/docs/observability-cost-accounting.7.md +194 -0
  138. package/docs/operator-ux.7.md +180 -0
  139. package/docs/pipeline-runner.7.md +136 -0
  140. package/docs/project-index.md +261 -0
  141. package/docs/real-execution-backends.7.md +142 -0
  142. package/docs/release-and-migration.7.md +280 -0
  143. package/docs/release-tooling.7.md +159 -0
  144. package/docs/routines.md +48 -0
  145. package/docs/run-registry-control-plane.7.md +312 -0
  146. package/docs/run-retention-reclamation.7.md +191 -0
  147. package/docs/sandbox-profiles.7.md +137 -0
  148. package/docs/scheduled-tasks.md +80 -0
  149. package/docs/security-trust-hardening.7.md +117 -0
  150. package/docs/state-explosion-management.7.md +264 -0
  151. package/docs/state-node.7.md +96 -0
  152. package/docs/team-collaboration.7.md +207 -0
  153. package/docs/unix-principles.md +192 -0
  154. package/docs/verifier-gated-commit.7.md +140 -0
  155. package/docs/web-desktop-workbench.7.md +215 -0
  156. package/docs/worker-isolation.7.md +167 -0
  157. package/docs/workflow-app-framework.7.md +274 -0
  158. package/manifest/README.md +43 -0
  159. package/manifest/plugin.manifest.json +316 -0
  160. package/manifest/pricing.policy.json +14 -0
  161. package/package.json +79 -0
  162. package/scripts/agents/claude-p-agent.js +104 -0
  163. package/scripts/agents/claude-p-agent.sh +9 -0
  164. package/scripts/agents/cw-attest-keygen.js +55 -0
  165. package/scripts/agents/cw-attest-wrap.js +143 -0
  166. package/scripts/block-unapproved-tag.sh +39 -0
  167. package/scripts/bump-version.js +249 -0
  168. package/scripts/canonical-apps.js +171 -0
  169. package/scripts/cw.js +4 -0
  170. package/scripts/dist-drift-check.js +79 -0
  171. package/scripts/dogfood-architecture-review.js +237 -0
  172. package/scripts/dogfood-release.js +624 -0
  173. package/scripts/forward-ref-docs.js +73 -0
  174. package/scripts/gen-manifests.js +232 -0
  175. package/scripts/golden-path.js +300 -0
  176. package/scripts/mcp-server.js +4 -0
  177. package/scripts/new-feature.js +121 -0
  178. package/scripts/parity-check.js +213 -0
  179. package/scripts/release-check.js +118 -0
  180. package/scripts/release-flow.js +272 -0
  181. package/scripts/release-gate.sh +85 -0
  182. package/scripts/sync-project-index.js +387 -0
  183. package/scripts/validate-run-state-schema.js +126 -0
  184. package/scripts/verify-container-selfref.js +64 -0
  185. package/scripts/version-sync-check.js +237 -0
  186. package/skills/cool-workflow/SKILL.md +162 -0
  187. package/skills/cool-workflow/references/commands.md +282 -0
  188. package/tsconfig.json +16 -0
  189. package/ui/workbench/app.css +76 -0
  190. package/ui/workbench/app.js +159 -0
  191. package/ui/workbench/index.html +32 -0
  192. package/workflows/architecture-review.workflow.js +84 -0
  193. package/workflows/research-synthesis.workflow.js +47 -0
@@ -0,0 +1,764 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hostRun = hostRun;
4
+ exports.hostStatus = hostStatus;
5
+ exports.hostStep = hostStep;
6
+ exports.hostAutoStep = hostAutoStep;
7
+ exports.hostBlackboard = hostBlackboard;
8
+ exports.hostScore = hostScore;
9
+ exports.hostSelect = hostSelect;
10
+ // Multi-Agent Host — the high-level orchestrator for topology-backed multi-agent
11
+ // workflows. Delegates common lifecycle steps (plan, dispatch, commit) to
12
+ // orchestrator/lifecycle-operations.ts; handles ONLY multi-agent-specific steps
13
+ // here (fanin collection, judge rationale, topology application). Adding a new
14
+ // lifecycle step: prefer adding it to lifecycle-operations.ts and calling it
15
+ // from here — keep the single-agent and multi-agent paths sharing one mechanism.
16
+ // See P2-2 (v0.1.48) and src/orchestrator/lifecycle-operations.ts.
17
+ const dispatch_1 = require("./dispatch");
18
+ const coordinator_1 = require("./coordinator");
19
+ const multi_agent_1 = require("./multi-agent");
20
+ const topology_1 = require("./topology");
21
+ const operator_ux_1 = require("./operator-ux");
22
+ const multi_agent_operator_ux_1 = require("./multi-agent-operator-ux");
23
+ const candidate_scoring_1 = require("./candidate-scoring");
24
+ const trust_audit_1 = require("./trust-audit");
25
+ const multi_agent_trust_1 = require("./multi-agent-trust");
26
+ function hostRun(run, options = {}) {
27
+ const topologyId = stringOption(options.topology || options.topologyId || options.id);
28
+ if (!topologyId)
29
+ return hostStatus(run, "run");
30
+ const existing = activeTopologies(run).find((entry) => entry.topologyId === topologyId && !isTerminalTopology(entry));
31
+ const topologyRun = existing || (0, topology_1.applyTopology)(run, topologyId, {
32
+ id: stringOption(options.topologyRun || options.topologyRunId || options["topology-run"] || options["topology-run-id"] || options.name),
33
+ title: stringOption(options.title),
34
+ multiAgentRunId: stringOption(options.multiAgentRun || options.multiAgentRunId),
35
+ blackboardId: stringOption(options.blackboard || options.blackboardId),
36
+ taskIds: arrayOption(options.task || options.taskId || options.tasks).map(String),
37
+ mapperCount: numberOption(options.mapperCount || options["mapper-count"] || options.mappers || options.mapper),
38
+ judgeCount: numberOption(options.judgeCount || options["judge-count"] || options.judges || options.judge),
39
+ debateRounds: numberOption(options.debateRounds || options["debate-rounds"] || options.rounds),
40
+ collectInitialFanin: Boolean(options.collectInitialFanin || options["collect-initial-fanin"]),
41
+ metadata: { hostSurface: "multi-agent.run" }
42
+ });
43
+ return envelope(run, "run", {
44
+ performed: existing ? "attached-topology" : "applied-topology",
45
+ data: {
46
+ topologyRun,
47
+ dispatchCreated: false,
48
+ note: "Workers are not dispatched by multi-agent run; call multi-agent step or dispatch explicitly."
49
+ }
50
+ });
51
+ }
52
+ function hostStatus(run, command = "status") {
53
+ return envelope(run, command);
54
+ }
55
+ function hostStep(run, options = {}) {
56
+ const state = classifyHostState(run);
57
+ if (state === "complete") {
58
+ return envelope(run, "step", {
59
+ performed: "none",
60
+ requiredHostAction: "Review the completed run report; no further host step is available."
61
+ });
62
+ }
63
+ if (state === "ready-for-commit") {
64
+ return envelope(run, "step", {
65
+ performed: "none",
66
+ requiredHostAction: "Run the recommended verifier-gated commit command before taking more steps."
67
+ });
68
+ }
69
+ if (state === "needs-run") {
70
+ return envelope(run, "step", {
71
+ performed: "none",
72
+ requiredHostAction: "Create or attach a multi-agent run before stepping."
73
+ });
74
+ }
75
+ if (state === "blocked" || state === "failed")
76
+ return envelope(run, "step", { performed: "none", requiredHostAction: "Resolve blocked reasons, then rerun multi-agent status." });
77
+ const topology = requireSingleActiveTopology(run);
78
+ const runningWorkers = (run.workers || []).filter((worker) => worker.status === "allocated" || worker.status === "running");
79
+ if (runningWorkers.length) {
80
+ return envelope(run, "step", {
81
+ performed: "none",
82
+ requiredHostAction: `Execute ${runningWorkers.length} worker(s), write result.md, then record output with worker output or result.`,
83
+ extraBlockedReasons: runningWorkers.map((worker) => `worker ${worker.id} is ${worker.status}`)
84
+ });
85
+ }
86
+ const faninPlan = nextFaninPlan(run, topology);
87
+ if (faninPlan) {
88
+ const fanin = (0, multi_agent_1.collectAgentFanin)(run, faninPlan);
89
+ linkTopologyRecord(topology, { faninIds: [fanin.id] });
90
+ return envelope(run, "step", {
91
+ performed: fanin.status === "ready" ? "collected-fanin" : "collected-blocked-fanin",
92
+ data: fanin,
93
+ requiredHostAction: fanin.status === "ready" ? undefined : "Add missing worker evidence or blackboard artifacts before continuing.",
94
+ extraBlockedReasons: fanin.status === "ready" ? [] : fanin.blockedReasons
95
+ });
96
+ }
97
+ const dispatchPlan = nextDispatchPlan(run, topology, options);
98
+ if (dispatchPlan) {
99
+ const manifest = (0, dispatch_1.createDispatchManifest)(run, dispatchPlan.limit, {
100
+ sandboxProfileId: dispatchPlan.sandboxProfileId,
101
+ backendId: dispatchPlan.backendId,
102
+ multiAgentRunId: topology.multiAgentRunId,
103
+ multiAgentGroupId: dispatchPlan.groupId,
104
+ multiAgentRoleId: dispatchPlan.roleId,
105
+ multiAgentFanoutId: dispatchPlan.fanoutId
106
+ });
107
+ return envelope(run, "step", {
108
+ performed: manifest.dispatchId ? "created-dispatch-manifest" : "none",
109
+ data: manifest,
110
+ requiredHostAction: manifest.dispatchId ? "Spawn the worker described by the manifest and record its result." : "No runnable pending task is available for dispatch."
111
+ });
112
+ }
113
+ const snapshotPlan = activeBlackboardId(run, topology);
114
+ if (snapshotPlan && !(run.blackboard?.snapshots || []).some((snapshot) => snapshot.blackboardId === snapshotPlan)) {
115
+ const snapshot = (0, coordinator_1.createBlackboardSnapshot)(run, snapshotPlan);
116
+ return envelope(run, "step", { performed: "created-blackboard-snapshot", data: snapshot });
117
+ }
118
+ const candidatePlan = nextCandidatePlan(run);
119
+ if (candidatePlan) {
120
+ const candidate = (0, candidate_scoring_1.registerCandidate)(run, {
121
+ id: stringOption(options.candidate || options.candidateId),
122
+ kind: "worker-output",
123
+ workerId: candidatePlan.id,
124
+ taskId: candidatePlan.taskId,
125
+ resultNodeId: candidatePlan.resultNodeId,
126
+ verifierNodeId: candidatePlan.output?.verifierNodeId,
127
+ resultPath: candidatePlan.resultPath,
128
+ metadata: { hostSurface: "multi-agent.step" }
129
+ }, { persist: false });
130
+ linkTopologyRecord(topology, { candidateIds: [candidate.id] });
131
+ return envelope(run, "step", { performed: "registered-candidate", data: candidate });
132
+ }
133
+ const scorePlan = nextScorePlan(run, options);
134
+ if (scorePlan) {
135
+ const score = (0, candidate_scoring_1.scoreCandidate)(run, scorePlan.candidate.id, {
136
+ scorer: stringOption(options.scorer) || "multi-agent-host",
137
+ criteria: parseCriteria(options, { correctness: 1, evidence: 1, fit: 1 }),
138
+ maxTotal: numberOption(options.maxTotal || options.max) || 3,
139
+ verdict: stringOption(options.verdict),
140
+ evidence: scorePlan.evidence,
141
+ notes: stringOption(options.notes) || "Host step scored candidate with existing verifier evidence.",
142
+ metadata: { hostSurface: "multi-agent.step", topologyRunId: topology.id }
143
+ }, { persist: false });
144
+ (0, coordinator_1.recordCoordinatorDecision)(run, {
145
+ blackboardId: topology.blackboardId,
146
+ topicId: topology.topicIds[0],
147
+ kind: "candidate-synthesis",
148
+ outcome: score.verdict === "fail" ? "blocked" : "ready",
149
+ reason: `Candidate ${score.candidateId} scored ${score.total}/${score.maxTotal}.`,
150
+ subjectIds: [score.candidateId, score.id],
151
+ evidenceRefs: score.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean),
152
+ metadata: { hostSurface: "multi-agent.step", scoreId: score.id }
153
+ });
154
+ return envelope(run, "step", { performed: "scored-candidate", data: score });
155
+ }
156
+ const selectionPlan = nextSelectionPlan(run, options);
157
+ if (selectionPlan) {
158
+ const selection = (0, candidate_scoring_1.selectCandidate)(run, selectionPlan.candidate.id, {
159
+ selectedBy: stringOption(options.by || options.selectedBy) || "multi-agent-host",
160
+ reason: stringOption(options.reason) || "Selected by high-level multi-agent host step after scoring and verifier evidence.",
161
+ scoreId: selectionPlan.scoreId,
162
+ allowUnverified: Boolean(options.allowUnverified)
163
+ }, { persist: false });
164
+ linkTopologyRecord(topology, { selectionIds: [selection.id] });
165
+ (0, coordinator_1.recordCoordinatorDecision)(run, {
166
+ blackboardId: topology.blackboardId,
167
+ topicId: topology.topicIds.at(-1),
168
+ kind: "candidate-synthesis",
169
+ outcome: "accepted",
170
+ reason: selection.reason,
171
+ subjectIds: [selection.candidateId, selection.id],
172
+ evidenceRefs: selection.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean),
173
+ metadata: { hostSurface: "multi-agent.step", selectionId: selection.id }
174
+ });
175
+ return envelope(run, "step", { performed: "selected-candidate", data: selection });
176
+ }
177
+ return envelope(run, "step", {
178
+ performed: "none",
179
+ requiredHostAction: "No safe deterministic step is available. Use multi-agent status for the next explicit command."
180
+ });
181
+ }
182
+ /** Auto-step: loop hostStep until blocked, complete, or max iterations reached
183
+ * (v0.1.74). Each iteration performs one deterministic step. Returns the final
184
+ * response and the number of steps taken. */
185
+ function hostAutoStep(run, options = {}) {
186
+ const maxSteps = Number(options.maxSteps || options["max-steps"] || 20);
187
+ const steps = [];
188
+ let response = envelope(run, "step", { performed: "none" });
189
+ for (let i = 0; i < maxSteps; i++) {
190
+ response = hostStep(run, options);
191
+ const performed = response.data?.performed;
192
+ steps.push({ step: i + 1, performed: String(performed || "none") });
193
+ if (performed === "none" || performed === undefined)
194
+ break;
195
+ if (response.data?.requiredHostAction)
196
+ break;
197
+ }
198
+ return { finalResponse: response, stepsTaken: steps.length, steps };
199
+ }
200
+ function hostBlackboard(run, action, options = {}) {
201
+ const topology = optionalSingleActiveTopology(run);
202
+ const blackboardId = resolveHostBlackboardId(run, topology, options);
203
+ const topicId = stringOption(options.topic || options.topicId) || resolveHostTopicId(run, blackboardId, options, action);
204
+ let data;
205
+ let performed = "read-blackboard";
206
+ switch (action || "summary") {
207
+ case "summary":
208
+ case "board":
209
+ data = (0, coordinator_1.summarizeBlackboard)(run, blackboardId);
210
+ break;
211
+ case "topics":
212
+ case "list-topics":
213
+ data = (run.blackboard?.topics || []).filter((topic) => topic.blackboardId === blackboardId);
214
+ break;
215
+ case "messages":
216
+ case "list-messages":
217
+ data = (0, coordinator_1.listBlackboardMessages)(run, { blackboardId, topicId });
218
+ break;
219
+ case "post":
220
+ case "message":
221
+ data = (0, coordinator_1.postBlackboardMessage)(run, {
222
+ topicId: requireString(topicId, "topic id"),
223
+ blackboardId,
224
+ body: requireString(options.body || options.message, "message body"),
225
+ evidenceRefs: arrayOption(options.evidence || options.evidenceRef).map(String),
226
+ artifactRefIds: arrayOption(options.artifact || options.artifactRef || options.artifactRefId).map(String),
227
+ metadata: { hostSurface: "multi-agent.blackboard" }
228
+ });
229
+ performed = "posted-message";
230
+ break;
231
+ case "artifacts":
232
+ case "list-artifacts":
233
+ data = (0, coordinator_1.listBlackboardArtifacts)(run, { blackboardId, topicId });
234
+ break;
235
+ case "add-artifact":
236
+ case "artifact":
237
+ data = (0, coordinator_1.addBlackboardArtifact)(run, {
238
+ id: stringOption(options.id),
239
+ blackboardId,
240
+ topicId,
241
+ kind: requireString(options.kind, "artifact kind"),
242
+ path: stringOption(options.path),
243
+ locator: stringOption(options.locator),
244
+ source: stringOption(options.source) || "multi-agent-host",
245
+ evidenceRefs: arrayOption(options.evidence || options.evidenceRef).map(String),
246
+ metadata: { hostSurface: "multi-agent.blackboard" }
247
+ });
248
+ performed = "added-artifact";
249
+ break;
250
+ case "context":
251
+ case "put-context":
252
+ data = (0, coordinator_1.putBlackboardContext)(run, {
253
+ blackboardId,
254
+ topicId: requireString(topicId, "topic id"),
255
+ kind: requireString(options.kind, "context kind"),
256
+ key: stringOption(options.key),
257
+ value: requireString(options.value || options.body, "context value"),
258
+ evidenceRefs: arrayOption(options.evidence || options.evidenceRef).map(String),
259
+ artifactRefIds: arrayOption(options.artifact || options.artifactRef || options.artifactRefId).map(String),
260
+ metadata: { hostSurface: "multi-agent.blackboard" }
261
+ });
262
+ performed = "put-context";
263
+ break;
264
+ case "snapshot":
265
+ data = (0, coordinator_1.createBlackboardSnapshot)(run, blackboardId);
266
+ performed = "created-snapshot";
267
+ break;
268
+ default:
269
+ throw new Error("Usage: multi-agent blackboard <run-id> [summary|topics|messages|post|artifacts|add-artifact|context|snapshot]");
270
+ }
271
+ return envelope(run, "blackboard", { performed, data });
272
+ }
273
+ function hostScore(run, options = {}) {
274
+ const topology = optionalSingleActiveTopology(run);
275
+ let candidate = resolveCandidate(run, options);
276
+ if (!candidate && stringOption(options.worker || options.workerId)) {
277
+ candidate = (0, candidate_scoring_1.registerCandidate)(run, {
278
+ id: stringOption(options.candidate || options.candidateId || options.id),
279
+ kind: "worker-output",
280
+ workerId: requireString(options.worker || options.workerId, "worker id"),
281
+ metadata: { hostSurface: "multi-agent.score" }
282
+ }, { persist: false });
283
+ }
284
+ if (!candidate)
285
+ throw new Error("multi-agent score requires --candidate or --worker when a single candidate cannot be inferred");
286
+ const evidence = explicitEvidence(options);
287
+ if (!evidence.length)
288
+ throw new Error(`Candidate ${candidate.id} score requires evidence`);
289
+ const authority = authorityOptions(options);
290
+ if (authority.agentRoleId || authority.agentMembershipId) {
291
+ const rationale = stringOption(options.rationale || options.notes || options.reason);
292
+ if (!rationale)
293
+ throw new Error(`Candidate ${candidate.id} judge score requires rationale`);
294
+ const permission = (0, multi_agent_trust_1.assertMultiAgentActionAllowed)(run, {
295
+ operation: "judge.rationale",
296
+ actor: authority.actor,
297
+ multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId,
298
+ agentRoleId: authority.agentRoleId,
299
+ agentGroupId: authority.agentGroupId,
300
+ agentMembershipId: authority.agentMembershipId,
301
+ blackboardId: topology?.blackboardId,
302
+ blackboardTopicId: topology?.topicIds[0],
303
+ candidateId: candidate.id,
304
+ evidenceRefs: evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean)
305
+ });
306
+ (0, multi_agent_trust_1.recordJudgeRationaleAudit)(run, {
307
+ kind: "judge.rationale",
308
+ actor: authority.actor,
309
+ multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId,
310
+ agentRoleId: authority.agentRoleId,
311
+ agentGroupId: authority.agentGroupId,
312
+ agentMembershipId: authority.agentMembershipId,
313
+ blackboardId: topology?.blackboardId,
314
+ blackboardTopicId: topology?.topicIds[0],
315
+ candidateId: candidate.id,
316
+ evidenceRefs: evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean),
317
+ rationale,
318
+ policyRef: permission.policyRef,
319
+ parentEventIds: [permission.event.id]
320
+ });
321
+ }
322
+ const score = (0, candidate_scoring_1.scoreCandidate)(run, candidate.id, {
323
+ id: stringOption(options.score || options.scoreId),
324
+ scorer: stringOption(options.scorer) || "multi-agent-host",
325
+ criteria: parseCriteria(options),
326
+ maxTotal: numberOption(options.maxTotal || options.max),
327
+ verdict: stringOption(options.verdict),
328
+ evidence,
329
+ notes: stringOption(options.notes),
330
+ metadata: { hostSurface: "multi-agent.score", topologyRunId: topology?.id }
331
+ }, { persist: false });
332
+ if (topology) {
333
+ linkTopologyRecord(topology, { candidateIds: [candidate.id] });
334
+ (0, coordinator_1.recordCoordinatorDecision)(run, {
335
+ blackboardId: topology.blackboardId,
336
+ topicId: topology.topicIds[0],
337
+ kind: "candidate-synthesis",
338
+ outcome: score.verdict === "fail" ? "blocked" : "ready",
339
+ reason: `Host scored candidate ${candidate.id} with score ${score.id}.`,
340
+ subjectIds: [candidate.id, score.id],
341
+ evidenceRefs: score.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean),
342
+ metadata: { hostSurface: "multi-agent.score", scoreId: score.id }
343
+ });
344
+ }
345
+ return envelope(run, "score", { performed: "scored-candidate", data: score });
346
+ }
347
+ function hostSelect(run, options = {}) {
348
+ const topology = optionalSingleActiveTopology(run);
349
+ const candidate = resolveCandidate(run, options) || topRankedCandidate(run);
350
+ if (!candidate)
351
+ throw new Error("multi-agent select requires a scored candidate");
352
+ const authority = authorityOptions(options);
353
+ if (authority.agentRoleId || authority.agentMembershipId) {
354
+ const scoreId = stringOption(options.score || options.scoreId) || candidate.scores.at(-1);
355
+ if (!(0, multi_agent_trust_1.hasAcceptedJudgeRationale)(run, { multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId, candidateId: candidate.id, scoreId })) {
356
+ throw new Error(`Candidate ${candidate.id} selection requires accepted judge rationale with evidence`);
357
+ }
358
+ (0, multi_agent_trust_1.assertMultiAgentActionAllowed)(run, {
359
+ operation: "candidate.select",
360
+ actor: authority.actor,
361
+ multiAgentRunId: authority.multiAgentRunId || topology?.multiAgentRunId,
362
+ agentRoleId: authority.agentRoleId,
363
+ agentGroupId: authority.agentGroupId,
364
+ agentMembershipId: authority.agentMembershipId,
365
+ blackboardId: topology?.blackboardId,
366
+ blackboardTopicId: topology?.topicIds.at(-1),
367
+ candidateId: candidate.id,
368
+ scoreId,
369
+ evidenceRefs: explicitEvidence(options).map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean)
370
+ });
371
+ }
372
+ const selection = (0, candidate_scoring_1.selectCandidate)(run, candidate.id, {
373
+ selectedBy: stringOption(options.by || options.selectedBy) || "multi-agent-host",
374
+ reason: requireString(options.reason || "Selected by high-level multi-agent host surface.", "selection reason"),
375
+ scoreId: stringOption(options.score || options.scoreId),
376
+ allowUnverified: Boolean(options.allowUnverified)
377
+ }, {
378
+ persist: false,
379
+ policy: {
380
+ requireVerifierGate: options.requireVerifierGate === undefined ? undefined : Boolean(options.requireVerifierGate),
381
+ minNormalized: numberOption(options.minNormalized)
382
+ }
383
+ });
384
+ if (topology) {
385
+ linkTopologyRecord(topology, { candidateIds: [candidate.id], selectionIds: [selection.id] });
386
+ (0, coordinator_1.recordCoordinatorDecision)(run, {
387
+ blackboardId: topology.blackboardId,
388
+ topicId: topology.topicIds.at(-1),
389
+ kind: "candidate-synthesis",
390
+ outcome: "accepted",
391
+ reason: selection.reason,
392
+ subjectIds: [selection.candidateId, selection.id],
393
+ evidenceRefs: selection.evidence.map((entry) => entry.locator || entry.summary || entry.id).filter(Boolean),
394
+ author: authority.actor,
395
+ links: {
396
+ multiAgentRunId: authority.multiAgentRunId || topology.multiAgentRunId,
397
+ agentGroupId: authority.agentGroupId,
398
+ agentRoleId: authority.agentRoleId,
399
+ agentMembershipId: authority.agentMembershipId
400
+ },
401
+ metadata: { hostSurface: "multi-agent.select", selectionId: selection.id }
402
+ });
403
+ }
404
+ return envelope(run, "select", { performed: "selected-candidate", data: selection });
405
+ }
406
+ function envelope(run, command, options = {}) {
407
+ const topologies = (0, topology_1.summarizeTopologies)(run);
408
+ const multiAgent = (0, multi_agent_1.summarizeMultiAgent)(run);
409
+ const blackboard = (0, coordinator_1.summarizeBlackboard)(run);
410
+ const workers = (0, operator_ux_1.summarizeOperatorWorkers)(run);
411
+ const candidates = (0, operator_ux_1.summarizeOperatorCandidates)(run);
412
+ const feedback = (0, operator_ux_1.summarizeOperatorFeedback)(run);
413
+ const commits = (0, operator_ux_1.summarizeOperatorCommits)(run);
414
+ const trust = (0, trust_audit_1.summarizeTrustAudit)(run);
415
+ const operator = (0, operator_ux_1.summarizeOperatorRun)(run);
416
+ const multiAgentOperator = (0, multi_agent_operator_ux_1.summarizeMultiAgentOperator)(run);
417
+ const active = activeTopologies(run);
418
+ const blockedReasons = unique([...operator.blockedReasons, ...(options.extraBlockedReasons || [])]);
419
+ const state = blockedReasons.length ? "blocked" : classifyHostState(run);
420
+ const ids = activeIds(run, active);
421
+ const nextActions = hostNextActions(run, state, active, options.requiredHostAction);
422
+ return {
423
+ schemaVersion: 1,
424
+ surface: "multi-agent-host",
425
+ command,
426
+ runId: run.id,
427
+ state,
428
+ performed: options.performed,
429
+ nextAction: nextActions[0]?.command,
430
+ nextActions,
431
+ blockedReasons,
432
+ requiredHostAction: options.requiredHostAction,
433
+ evidenceRequirements: evidenceRequirements(active, multiAgent.blockedReasons),
434
+ ids,
435
+ paths: {
436
+ statePath: run.paths.state,
437
+ reportPath: run.paths.report,
438
+ blackboardIndexPath: blackboard.indexPath,
439
+ auditSummaryPath: run.audit?.summaryPath,
440
+ auditEventLogPath: run.audit?.eventLogPath,
441
+ candidateRankingPath: candidates.latestRankingPath,
442
+ workerManifestPaths: workers.manifestPaths,
443
+ workerResultPaths: workers.resultPaths
444
+ },
445
+ summaries: { topologies, multiAgent, multiAgentOperator, blackboard, workers, candidates, feedback, commits, trust },
446
+ data: options.data
447
+ };
448
+ }
449
+ function classifyHostState(run) {
450
+ const active = activeTopologies(run);
451
+ const feedback = (0, operator_ux_1.summarizeOperatorFeedback)(run);
452
+ const workers = (0, operator_ux_1.summarizeOperatorWorkers)(run);
453
+ const candidates = (0, operator_ux_1.summarizeOperatorCandidates)(run);
454
+ const commits = (0, operator_ux_1.summarizeOperatorCommits)(run);
455
+ if (!active.length && !(run.multiAgent?.runs || []).length)
456
+ return "needs-run";
457
+ if (feedback.open.some((entry) => !entry.retryable))
458
+ return "failed";
459
+ if (feedback.open.length)
460
+ return "blocked";
461
+ if (commits.verifierGated > 0 && candidates.readyForCommit.length === 0)
462
+ return "complete";
463
+ if (candidates.readyForCommit.length)
464
+ return "ready-for-commit";
465
+ if ((run.candidateSelections || []).length === 0 && (run.candidates || []).some((candidate) => candidate.status === "scored" || candidate.status === "verified")) {
466
+ return "ready-for-selection";
467
+ }
468
+ if ((run.candidates || []).some((candidate) => candidate.status === "registered"))
469
+ return "ready-for-scoring";
470
+ if (workers.workers.some((worker) => worker.status === "allocated" || worker.status === "running"))
471
+ return "awaiting-worker-output";
472
+ if (nextCandidatePlan(run))
473
+ return "ready-for-scoring";
474
+ if (nextFaninPlan(run, active[0]))
475
+ return "ready-for-fanin";
476
+ if (nextDispatchPlan(run, active[0], {}))
477
+ return "ready-for-dispatch";
478
+ return "blocked";
479
+ }
480
+ function activeIds(run, active) {
481
+ return {
482
+ topologyRunIds: active.map((entry) => entry.id),
483
+ topologyIds: active.map((entry) => entry.topologyId),
484
+ multiAgentRunIds: unique([...active.map((entry) => entry.multiAgentRunId), ...(run.multiAgent?.runs || []).map((entry) => entry.id)]),
485
+ blackboardIds: unique([...active.map((entry) => entry.blackboardId), ...(run.blackboard?.boards || []).map((entry) => entry.id)]),
486
+ topicIds: unique(active.flatMap((entry) => entry.topicIds)),
487
+ groupIds: unique(active.flatMap((entry) => entry.groupIds)),
488
+ roleIds: unique(active.flatMap((entry) => entry.roleIds)),
489
+ fanoutIds: unique(active.flatMap((entry) => entry.fanoutIds)),
490
+ faninIds: unique(active.flatMap((entry) => entry.faninIds)),
491
+ candidateIds: unique([...(run.candidates || []).map((entry) => entry.id), ...active.flatMap((entry) => entry.candidateIds)]),
492
+ selectionIds: unique([...(run.candidateSelections || []).map((entry) => entry.id), ...active.flatMap((entry) => entry.selectionIds)]),
493
+ commitIds: (run.commits || []).map((entry) => entry.id),
494
+ auditEventIds: unique(active.flatMap((entry) => entry.links.auditEventIds))
495
+ };
496
+ }
497
+ function hostNextActions(run, state, active, requiredHostAction) {
498
+ if (requiredHostAction)
499
+ return [{ command: "host-action", reason: requiredHostAction, priority: "high" }];
500
+ const runId = run.id;
501
+ switch (state) {
502
+ case "needs-run":
503
+ return [{ command: `node scripts/cw.js multi-agent run ${runId} --topology map-reduce`, reason: "Materialize a host-facing multi-agent topology.", priority: "high" }];
504
+ case "ready-for-dispatch":
505
+ return [{ command: `node scripts/cw.js multi-agent step ${runId}`, reason: "Create the next dispatch manifest without spawning workers.", priority: "high" }];
506
+ case "awaiting-worker-output":
507
+ return [{ command: `node scripts/cw.js worker output ${runId} <worker-id> <result.md>`, reason: "A host-executed worker must report result evidence.", priority: "high" }];
508
+ case "ready-for-fanin":
509
+ return [{ command: `node scripts/cw.js multi-agent step ${runId}`, reason: "Collect fanin once required worker evidence is present.", priority: "high" }];
510
+ case "ready-for-scoring":
511
+ 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" }];
512
+ case "ready-for-selection":
513
+ 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" }];
514
+ case "ready-for-commit": {
515
+ const ready = (0, operator_ux_1.summarizeOperatorCandidates)(run).readyForCommit[0];
516
+ return [{ command: `node scripts/cw.js commit ${runId} --selection ${ready.selectionId} --reason "<verified rationale>"`, reason: "Create a verifier-gated CW state commit.", priority: "high" }];
517
+ }
518
+ case "complete":
519
+ return [{ command: `node scripts/cw.js report ${runId} --show`, reason: "Review the completed run report.", priority: "normal" }];
520
+ case "failed":
521
+ case "blocked":
522
+ default:
523
+ 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" }];
524
+ }
525
+ }
526
+ function nextDispatchPlan(run, topology, options) {
527
+ if (!topology)
528
+ return undefined;
529
+ if (topology.faninIds.length)
530
+ return undefined;
531
+ const fanout = (run.multiAgent?.fanouts || []).find((entry) => topology.fanoutIds.includes(entry.id));
532
+ const group = (run.multiAgent?.groups || []).find((entry) => topology.groupIds.includes(entry.id));
533
+ if (!fanout || !group)
534
+ return undefined;
535
+ const pending = run.tasks.filter((task) => task.status === "pending");
536
+ if (!pending.length)
537
+ return undefined;
538
+ const membershipRoleIds = new Set((run.multiAgent?.memberships || []).filter((entry) => entry.fanoutId === fanout.id).map((entry) => entry.roleId));
539
+ const roleId = fanout.roleIds.find((id) => !membershipRoleIds.has(id));
540
+ if (!roleId)
541
+ return undefined;
542
+ return {
543
+ limit: numberOption(options.limit) || 1,
544
+ sandboxProfileId: stringOption(options.sandbox || options.sandboxProfile || options.sandboxProfileId) || "readonly",
545
+ backendId: stringOption(options.backend || options.backendId || options.executionBackend),
546
+ groupId: group.id,
547
+ fanoutId: fanout.id,
548
+ roleId
549
+ };
550
+ }
551
+ function nextFaninPlan(run, topology) {
552
+ if (!topology || topology.faninIds.length)
553
+ return undefined;
554
+ const fanout = (run.multiAgent?.fanouts || []).find((entry) => topology.fanoutIds.includes(entry.id));
555
+ const group = (run.multiAgent?.groups || []).find((entry) => topology.groupIds.includes(entry.id));
556
+ if (!fanout || !group || !fanout.membershipIds.length)
557
+ return undefined;
558
+ const memberships = (run.multiAgent?.memberships || []).filter((entry) => entry.fanoutId === fanout.id);
559
+ if (!memberships.length || memberships.some((entry) => entry.status !== "reported" && !entry.verifierNodeId))
560
+ return undefined;
561
+ const membershipRoleIds = new Set(memberships.map((entry) => entry.roleId));
562
+ if (fanout.roleIds.some((roleId) => !membershipRoleIds.has(roleId)))
563
+ return undefined;
564
+ return {
565
+ id: `${topology.id}-fanin`,
566
+ multiAgentRunId: topology.multiAgentRunId,
567
+ groupId: group.id,
568
+ fanoutId: fanout.id,
569
+ requiredRoleIds: fanout.roleIds,
570
+ strategy: "host step fanin requires all dispatched role evidence",
571
+ blackboardId: topology.blackboardId,
572
+ topicIds: topology.topicIds,
573
+ metadata: { hostSurface: "multi-agent.step", topologyRunId: topology.id }
574
+ };
575
+ }
576
+ function nextCandidatePlan(run) {
577
+ const candidateWorkerIds = new Set((run.candidates || []).map((entry) => entry.workerId).filter(Boolean));
578
+ return (run.workers || []).find((worker) => worker.status === "verified" &&
579
+ worker.id &&
580
+ !candidateWorkerIds.has(worker.id) &&
581
+ worker.output?.verifierNodeId);
582
+ }
583
+ function nextScorePlan(run, options) {
584
+ const explicit = resolveCandidate(run, options);
585
+ const candidate = explicit || (run.candidates || []).filter((entry) => entry.status === "registered").at(0);
586
+ if (!candidate || candidate.scores.length)
587
+ return undefined;
588
+ const evidence = explicitEvidence(options);
589
+ return { candidate, evidence: evidence.length ? evidence : candidateEvidence(candidate) };
590
+ }
591
+ function nextSelectionPlan(run, options) {
592
+ const candidate = resolveCandidate(run, options) || topRankedCandidate(run);
593
+ if (!candidate || (run.candidateSelections || []).some((entry) => entry.candidateId === candidate.id))
594
+ return undefined;
595
+ if (!candidate.scores.length)
596
+ return undefined;
597
+ return { candidate, scoreId: stringOption(options.score || options.scoreId) };
598
+ }
599
+ function resolveCandidate(run, options) {
600
+ const id = stringOption(options.candidate || options.candidateId || options.id);
601
+ if (id) {
602
+ const candidate = (run.candidates || []).find((entry) => entry.id === id);
603
+ if (!candidate)
604
+ throw new Error(`Unknown candidate id for run ${run.id}: ${id}`);
605
+ return candidate;
606
+ }
607
+ const candidates = (run.candidates || []).filter((entry) => entry.status !== "rejected" && entry.status !== "failed");
608
+ return candidates.length === 1 ? candidates[0] : undefined;
609
+ }
610
+ function topRankedCandidate(run) {
611
+ const ranking = (0, candidate_scoring_1.rankCandidates)(run);
612
+ const first = ranking.candidates.find((entry) => entry.scoreCount > 0 && entry.verdict !== "fail");
613
+ return first ? (run.candidates || []).find((candidate) => candidate.id === first.candidateId) : undefined;
614
+ }
615
+ function activeTopologies(run) {
616
+ return [...(run.topologies?.runs || [])].filter((entry) => !isTerminalTopology(entry));
617
+ }
618
+ function optionalSingleActiveTopology(run) {
619
+ const active = activeTopologies(run);
620
+ if (active.length > 1)
621
+ throw new Error(`Ambiguous active topology state: ${active.map((entry) => entry.id).join(", ")}`);
622
+ return active[0];
623
+ }
624
+ function requireSingleActiveTopology(run) {
625
+ const active = activeTopologies(run);
626
+ if (!active.length)
627
+ throw new Error(`Run ${run.id} has no active multi-agent topology. Use multi-agent run --topology <id>.`);
628
+ if (active.length > 1)
629
+ throw new Error(`Ambiguous active topology state: ${active.map((entry) => entry.id).join(", ")}`);
630
+ return active[0];
631
+ }
632
+ function resolveHostBlackboardId(run, topology, options) {
633
+ const explicit = stringOption(options.blackboard || options.blackboardId);
634
+ if (explicit)
635
+ return explicit;
636
+ if (topology?.blackboardId)
637
+ return topology.blackboardId;
638
+ const boards = run.blackboard?.boards || [];
639
+ if (boards.length === 1)
640
+ return boards[0].id;
641
+ if (!boards.length)
642
+ throw new Error(`Run ${run.id} has no blackboard. Use multi-agent run --topology <id> first.`);
643
+ throw new Error(`Ambiguous blackboard state: ${boards.map((board) => board.id).join(", ")}`);
644
+ }
645
+ function resolveHostTopicId(run, blackboardId, options, action) {
646
+ const explicit = stringOption(options.topic || options.topicId);
647
+ if (explicit)
648
+ return explicit;
649
+ if (action === "summary" || action === "board" || action === "topics" || action === "artifacts" || action === "list-artifacts" || action === "snapshot") {
650
+ return undefined;
651
+ }
652
+ const topics = (run.blackboard?.topics || []).filter((topic) => topic.blackboardId === blackboardId);
653
+ if (topics.length === 1)
654
+ return topics[0].id;
655
+ if (!topics.length)
656
+ throw new Error(`Blackboard ${blackboardId} has no topics`);
657
+ throw new Error(`Ambiguous blackboard topic state: ${topics.map((topic) => topic.id).join(", ")}`);
658
+ }
659
+ function activeBlackboardId(run, topology) {
660
+ const board = (run.blackboard?.boards || []).find((entry) => entry.id === topology.blackboardId);
661
+ return board?.id;
662
+ }
663
+ function linkTopologyRecord(topology, links) {
664
+ const faninIds = links.faninIds || [];
665
+ const candidateIds = links.candidateIds || [];
666
+ const selectionIds = links.selectionIds || [];
667
+ topology.faninIds = unique([...topology.faninIds, ...faninIds]);
668
+ topology.candidateIds = unique([...topology.candidateIds, ...candidateIds]);
669
+ topology.selectionIds = unique([...topology.selectionIds, ...selectionIds]);
670
+ topology.links.agentFaninIds = unique([...topology.links.agentFaninIds, ...faninIds]);
671
+ topology.links.candidateIds = unique([...topology.links.candidateIds, ...candidateIds]);
672
+ topology.links.selectionIds = unique([...topology.links.selectionIds, ...selectionIds]);
673
+ topology.updatedAt = new Date().toISOString();
674
+ }
675
+ function evidenceRequirements(active, extra) {
676
+ return unique([...active.flatMap((entry) => entry.missingEvidence), ...extra]);
677
+ }
678
+ function isTerminalTopology(topology) {
679
+ return topology.status === "completed" || topology.status === "failed";
680
+ }
681
+ function parseCriteria(options, fallback) {
682
+ const criteria = {};
683
+ const structured = options.criteria;
684
+ if (structured && typeof structured === "object" && !Array.isArray(structured)) {
685
+ for (const [key, value] of Object.entries(structured)) {
686
+ const parsed = Number(value);
687
+ if (key && Number.isFinite(parsed))
688
+ criteria[key] = parsed;
689
+ }
690
+ }
691
+ for (const entry of arrayOption(options.criterion || (typeof structured === "string" ? structured : undefined) || options.score)) {
692
+ const [key, value] = String(entry).split("=");
693
+ if (!key || value === undefined)
694
+ continue;
695
+ const parsed = Number(value);
696
+ if (Number.isFinite(parsed))
697
+ criteria[key] = parsed;
698
+ }
699
+ if (!Object.keys(criteria).length && options.total !== undefined)
700
+ criteria.total = Number(options.total);
701
+ if (!Object.keys(criteria).length && fallback)
702
+ return fallback;
703
+ if (!Object.keys(criteria).length)
704
+ throw new Error("Missing score criteria. Use --criterion name=value");
705
+ return criteria;
706
+ }
707
+ function explicitEvidence(options) {
708
+ return arrayOption(options.evidence || options.evidenceRef || options["evidence-ref"]).map((entry, index) => ({
709
+ id: `host-score:${index + 1}`,
710
+ source: "multi-agent-host",
711
+ locator: String(entry),
712
+ summary: String(entry)
713
+ }));
714
+ }
715
+ function authorityOptions(options) {
716
+ const agentRoleId = stringOption(options.role || options.roleId || options["multi-agent-role"]);
717
+ const agentGroupId = stringOption(options.group || options.groupId || options["multi-agent-group"]);
718
+ const agentMembershipId = stringOption(options.membership || options.membershipId || options["multi-agent-membership"]);
719
+ const actor = agentMembershipId
720
+ ? { kind: "membership", id: agentMembershipId }
721
+ : agentRoleId
722
+ ? { kind: "role", id: agentRoleId }
723
+ : agentGroupId
724
+ ? { kind: "group", id: agentGroupId }
725
+ : undefined;
726
+ return {
727
+ actor,
728
+ multiAgentRunId: stringOption(options.multiAgentRun || options.multiAgentRunId || options["multi-agent-run"]),
729
+ agentRoleId,
730
+ agentGroupId,
731
+ agentMembershipId
732
+ };
733
+ }
734
+ function candidateEvidence(candidate) {
735
+ return (candidate.evidence || []).map((entry, index) => ({
736
+ ...entry,
737
+ id: entry.id || `candidate:${index + 1}`
738
+ }));
739
+ }
740
+ function numberOption(value) {
741
+ if (value === undefined || value === null || value === true)
742
+ return undefined;
743
+ const parsed = Number(value);
744
+ return Number.isFinite(parsed) ? parsed : undefined;
745
+ }
746
+ function stringOption(value) {
747
+ if (value === undefined || value === null || value === true)
748
+ return undefined;
749
+ return String(value);
750
+ }
751
+ function requireString(value, label) {
752
+ const parsed = stringOption(value);
753
+ if (!parsed)
754
+ throw new Error(`Missing ${label}`);
755
+ return parsed;
756
+ }
757
+ function arrayOption(value) {
758
+ if (value === undefined || value === null || value === true)
759
+ return [];
760
+ return Array.isArray(value) ? value : [value];
761
+ }
762
+ function unique(values) {
763
+ return [...new Set(values.filter((value) => value !== undefined && value !== null))];
764
+ }