cool-workflow 0.1.97 → 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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +11 -2
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +236 -0
- package/dist/cli/entry.js +120 -0
- package/dist/cli/io.js +21 -4
- package/dist/cli/parseargv.js +157 -0
- package/dist/cli.js +6 -33
- package/dist/core/capability-table.js +3534 -0
- package/dist/core/format/help.js +314 -0
- package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
- package/dist/core/hash.js +137 -0
- package/dist/core/multi-agent/candidate-scoring.js +219 -0
- package/dist/core/multi-agent/collaboration.js +481 -0
- package/dist/core/multi-agent/coordinator.js +515 -0
- package/dist/core/multi-agent/eval-replay.js +306 -0
- package/dist/core/multi-agent/runtime.js +929 -0
- package/dist/core/multi-agent/topology.js +298 -0
- package/dist/core/multi-agent/trust-policy.js +197 -0
- package/dist/core/pipeline/commit-gate.js +320 -0
- package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
- package/dist/core/pipeline/dispatch.js +103 -0
- package/dist/core/pipeline/drive-decide.js +227 -0
- package/dist/core/pipeline/error-feedback.js +161 -0
- package/dist/core/pipeline/loop-expansion.js +124 -0
- package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
- package/dist/core/pipeline/runner.js +230 -0
- package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
- package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
- package/dist/core/state/node-projection.js +95 -0
- package/dist/core/state/node-snapshot.js +230 -0
- package/dist/core/state/run-paths.js +93 -0
- package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
- package/dist/core/state/schema.js +50 -0
- package/dist/core/state/state-explosion/digest.js +243 -0
- package/dist/core/state/state-explosion/graph.js +527 -0
- package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
- package/dist/core/state/state-explosion/report.js +187 -0
- package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
- package/dist/{state-node.js → core/state/state-node.js} +90 -113
- package/dist/core/state/types.js +19 -0
- package/dist/{validation.js → core/state/validation.js} +64 -131
- package/dist/core/trust/evidence-grounding.js +134 -0
- package/dist/core/trust/ledger.js +199 -0
- package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
- package/dist/core/trust/telemetry-ledger.js +127 -0
- package/dist/core/types.js +20 -0
- package/dist/core/version.js +16 -0
- package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
- package/dist/mcp/dispatch.js +104 -0
- package/dist/mcp/server.js +144 -0
- package/dist/mcp-server.js +6 -84
- package/dist/{agent-config.js → shell/agent-config.js} +118 -71
- package/dist/shell/app-run-cli.js +88 -0
- package/dist/shell/audit-cli.js +259 -0
- package/dist/shell/audit-provenance.js +83 -0
- package/dist/shell/candidate-scoring-io.js +459 -0
- package/dist/shell/collaboration-io.js +264 -0
- package/dist/shell/commit-summary.js +104 -0
- package/dist/shell/commit.js +290 -0
- package/dist/shell/coordinator-io.js +476 -0
- package/dist/shell/demo-cli.js +19 -0
- package/dist/shell/dispatch.js +162 -0
- package/dist/{doctor.js → shell/doctor.js} +123 -56
- package/dist/shell/drive.js +873 -0
- package/dist/shell/error-feedback-io.js +305 -0
- package/dist/shell/eval-io.js +473 -0
- package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
- package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
- package/dist/shell/exec-backend-cli.js +88 -0
- package/dist/shell/execution-backend/agent.js +475 -0
- package/dist/shell/execution-backend/ci.js +15 -0
- package/dist/shell/execution-backend/container.js +69 -0
- package/dist/shell/execution-backend/envelopes.js +55 -0
- package/dist/shell/execution-backend/local.js +113 -0
- package/dist/shell/execution-backend/probes.js +175 -0
- package/dist/shell/execution-backend/registry.js +402 -0
- package/dist/shell/execution-backend/remote.js +128 -0
- package/dist/shell/execution-backend/types.js +11 -0
- package/dist/shell/feedback-cli.js +81 -0
- package/dist/shell/feedback-operations.js +48 -0
- package/dist/shell/fs-atomic.js +276 -0
- package/dist/shell/harness.js +98 -0
- package/dist/shell/ledger-cli.js +212 -0
- package/dist/shell/ledger-io.js +169 -0
- package/dist/shell/man-cli.js +89 -0
- package/dist/shell/metrics-cli.js +98 -0
- package/dist/shell/multi-agent-cli.js +1002 -0
- package/dist/shell/multi-agent-host.js +563 -0
- package/dist/shell/multi-agent-io.js +387 -0
- package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
- package/dist/shell/node-store.js +124 -0
- package/dist/{observability/format.js → shell/observability-format.js} +7 -1
- package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
- package/dist/{observability.js → shell/observability.js} +159 -332
- package/dist/{onramp.js → shell/onramp.js} +11 -0
- package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
- package/dist/shell/operator-ux.js +431 -0
- package/dist/shell/orchestrator.js +231 -0
- package/dist/shell/pipeline-cli.js +667 -0
- package/dist/shell/pipeline.js +217 -0
- package/dist/shell/reclamation-io.js +1366 -0
- package/dist/shell/registry-cli.js +329 -0
- package/dist/{remote-source.js → shell/remote-source.js} +12 -5
- package/dist/shell/report-cli.js +101 -0
- package/dist/shell/report-view-cli.js +117 -0
- package/dist/{orchestrator → shell}/report.js +289 -282
- package/dist/shell/reporter.js +62 -0
- package/dist/shell/run-export-cli.js +106 -0
- package/dist/shell/run-export.js +680 -0
- package/dist/shell/run-registry-io.js +1014 -0
- package/dist/shell/run-store.js +164 -0
- package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
- package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
- package/dist/shell/scheduling-io.js +311 -0
- package/dist/shell/state-cli.js +181 -0
- package/dist/shell/state-explosion-cli.js +197 -0
- package/dist/shell/telemetry-cli.js +85 -0
- package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
- package/dist/shell/telemetry-ledger-io.js +132 -0
- package/dist/{term.js → shell/term.js} +36 -46
- package/dist/shell/topology-io.js +361 -0
- package/dist/shell/trust-audit.js +471 -0
- package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
- package/dist/shell/verifier.js +48 -0
- package/dist/shell/workbench-host.js +250 -0
- package/dist/shell/workbench-text.js +18 -0
- package/dist/shell/workbench.js +175 -0
- package/dist/shell/worker-cli.js +124 -0
- package/dist/shell/worker-isolation.js +852 -0
- package/dist/shell/workflow-app-loader.js +650 -0
- package/docs/agent-delegation-drive.7.md +4 -0
- package/docs/cli-mcp-parity.7.md +284 -211
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +4 -0
- package/docs/cross-agent-ledger.7.md +217 -0
- package/docs/designs/handoff-ledger.md +145 -0
- package/docs/durable-state-and-locking.7.md +4 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
- package/docs/execution-backends.7.md +4 -0
- package/docs/handoff-setup.md +120 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
- package/docs/multi-agent-eval-replay-harness.7.md +4 -0
- package/docs/multi-agent-operator-ux.7.md +4 -0
- package/docs/node-snapshot-diff-replay.7.md +4 -0
- package/docs/observability-cost-accounting.7.md +4 -0
- package/docs/project-index.md +143 -71
- package/docs/real-execution-backends.7.md +4 -0
- package/docs/release-and-migration.7.md +4 -0
- package/docs/release-tooling.7.md +4 -0
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +25 -0
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +4 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +9 -13
- package/package.json +1 -1
- package/scripts/agents/codex-agent.js +34 -4
- package/scripts/agents/cw-attest-wrap.js +2 -2
- package/scripts/bump-version.js +4 -3
- package/scripts/canonical-apps.js +4 -4
- package/scripts/children/batch-delegate-child.js +30 -10
- package/scripts/dogfood-architecture-review.js +2 -3
- package/scripts/dogfood-release.js +3 -3
- package/scripts/gen-parity-doc.js +15 -2
- package/scripts/golden-path.js +4 -4
- package/scripts/onramp-check.js +1 -1
- package/scripts/parity-check.js +38 -21
- package/scripts/release-flow.js +9 -3
- package/scripts/sync-project-index.js +51 -27
- package/scripts/validate-run-state-schema.js +2 -2
- package/scripts/version-sync-check.js +30 -30
- package/dist/candidate-scoring.js +0 -729
- package/dist/capability-core.js +0 -1186
- package/dist/capability-registry.js +0 -879
- package/dist/cli/command-surface.js +0 -490
- package/dist/cli/format.js +0 -56
- package/dist/cli/handlers/audit.js +0 -82
- package/dist/cli/handlers/blackboard.js +0 -81
- package/dist/cli/handlers/candidate.js +0 -40
- package/dist/cli/handlers/clones.js +0 -34
- package/dist/cli/handlers/collaboration.js +0 -61
- package/dist/cli/handlers/eval.js +0 -40
- package/dist/cli/handlers/maintenance.js +0 -107
- package/dist/cli/handlers/multi-agent.js +0 -165
- package/dist/cli/handlers/node.js +0 -41
- package/dist/cli/handlers/operational.js +0 -155
- package/dist/cli/handlers/operator.js +0 -146
- package/dist/cli/handlers/registry.js +0 -68
- package/dist/cli/handlers/run.js +0 -153
- package/dist/cli/handlers/scheduling.js +0 -132
- package/dist/cli/handlers/workbench.js +0 -41
- package/dist/cli/handlers/worker.js +0 -45
- package/dist/cli/run-summary.js +0 -45
- package/dist/clones.js +0 -162
- package/dist/collaboration.js +0 -726
- package/dist/commit.js +0 -592
- package/dist/compare.js +0 -18
- package/dist/coordinator/classify.js +0 -45
- package/dist/coordinator/paths.js +0 -42
- package/dist/coordinator/util.js +0 -126
- package/dist/coordinator.js +0 -990
- package/dist/daemon.js +0 -44
- package/dist/dispatch.js +0 -250
- package/dist/drive.js +0 -827
- package/dist/error-feedback.js +0 -419
- package/dist/evidence-grounding.js +0 -184
- package/dist/execution-backend/agent.js +0 -294
- package/dist/execution-backend/probes.js +0 -112
- package/dist/execution-backend/util.js +0 -47
- package/dist/execution-backend.js +0 -961
- package/dist/gates.js +0 -48
- package/dist/harness.js +0 -61
- package/dist/loop-expansion.js +0 -60
- package/dist/mcp/tool-call.js +0 -434
- package/dist/mcp/tool-definitions.js +0 -1040
- package/dist/mcp-surface.js +0 -30
- package/dist/multi-agent/graph.js +0 -84
- package/dist/multi-agent/helpers.js +0 -141
- package/dist/multi-agent/ids.js +0 -20
- package/dist/multi-agent/paths.js +0 -22
- package/dist/multi-agent-eval/normalize.js +0 -51
- package/dist/multi-agent-eval.js +0 -678
- package/dist/multi-agent-host.js +0 -777
- package/dist/multi-agent.js +0 -984
- package/dist/node-projection.js +0 -59
- package/dist/node-snapshot.js +0 -260
- package/dist/operator-ux.js +0 -631
- package/dist/orchestrator/app-operations.js +0 -211
- package/dist/orchestrator/audit-operations.js +0 -182
- package/dist/orchestrator/candidate-operations.js +0 -117
- package/dist/orchestrator/cli-options.js +0 -294
- package/dist/orchestrator/collaboration-operations.js +0 -86
- package/dist/orchestrator/feedback-operations.js +0 -81
- package/dist/orchestrator/host-operations.js +0 -78
- package/dist/orchestrator/lifecycle-operations.js +0 -626
- package/dist/orchestrator/migration-operations.js +0 -44
- package/dist/orchestrator/multi-agent-operations.js +0 -362
- package/dist/orchestrator/topology-operations.js +0 -84
- package/dist/orchestrator.js +0 -917
- package/dist/pipeline-runner.js +0 -285
- package/dist/reclamation/hash.js +0 -72
- package/dist/reclamation.js +0 -812
- package/dist/reporter.js +0 -67
- package/dist/run-export.js +0 -784
- package/dist/run-registry/derive.js +0 -175
- package/dist/run-registry/format.js +0 -124
- package/dist/run-registry/gc.js +0 -251
- package/dist/run-registry/policy.js +0 -16
- package/dist/run-registry/queue.js +0 -115
- package/dist/run-registry.js +0 -850
- package/dist/run-state-schema.js +0 -68
- package/dist/scheduling.js +0 -184
- package/dist/state-explosion.js +0 -1014
- package/dist/state.js +0 -367
- package/dist/telemetry-ledger.js +0 -196
- package/dist/topology.js +0 -565
- package/dist/triggers.js +0 -184
- package/dist/trust-audit.js +0 -644
- package/dist/types/blackboard.js +0 -2
- package/dist/types/candidate.js +0 -2
- package/dist/types/collaboration.js +0 -2
- package/dist/types/core.js +0 -2
- package/dist/types/drive.js +0 -10
- package/dist/types/error-feedback.js +0 -2
- package/dist/types/evidence-reasoning.js +0 -2
- package/dist/types/execution-backend.js +0 -2
- package/dist/types/multi-agent.js +0 -2
- package/dist/types/observability.js +0 -2
- package/dist/types/pipeline.js +0 -2
- package/dist/types/reclamation.js +0 -8
- package/dist/types/report-bundle.js +0 -6
- package/dist/types/result.js +0 -2
- package/dist/types/run-registry.js +0 -2
- package/dist/types/run.js +0 -2
- package/dist/types/sandbox.js +0 -2
- package/dist/types/schedule.js +0 -2
- package/dist/types/state-node.js +0 -2
- package/dist/types/topology.js +0 -2
- package/dist/types/trust.js +0 -2
- package/dist/types/workbench.js +0 -2
- package/dist/types/worker.js +0 -2
- package/dist/types/workflow-app.js +0 -2
- package/dist/types.js +0 -44
- package/dist/util/fingerprint.js +0 -19
- package/dist/util/fingerprint.test.js +0 -27
- package/dist/verifier.js +0 -78
- package/dist/version.js +0 -8
- package/dist/workbench-host.js +0 -182
- package/dist/workbench.js +0 -192
- package/dist/worker-accept/acceptance.js +0 -114
- package/dist/worker-accept/blackboard-fanout.js +0 -80
- package/dist/worker-accept/blackboard-linkage.js +0 -19
- package/dist/worker-accept/context.js +0 -2
- package/dist/worker-accept/telemetry-ledger.js +0 -126
- package/dist/worker-accept/validation.js +0 -77
- package/dist/worker-accept/verifier-completion.js +0 -73
- package/dist/worker-isolation/helpers.js +0 -51
- package/dist/worker-isolation/paths.js +0 -46
- package/dist/worker-isolation.js +0 -656
- package/dist/workflow-api.js +0 -131
- /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
|
+
}
|