opencode-swarm 7.17.1 → 7.17.2
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/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.17.
|
|
37
|
+
version: "7.17.2",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* No I/O — fully unit-testable with mock inputs. All file reads/writes happen in
|
|
9
9
|
* sibling modules (criteria-store, council-evidence-writer).
|
|
10
10
|
*/
|
|
11
|
-
import type { CouncilConfig, CouncilCriteria, CouncilMemberVerdict, CouncilSynthesis, PhaseCouncilSynthesis } from './types';
|
|
11
|
+
import type { CouncilConfig, CouncilCriteria, CouncilMemberVerdict, CouncilSynthesis, FinalCouncilSynthesis, PhaseCouncilSynthesis } from './types';
|
|
12
12
|
export declare function synthesizeCouncilVerdicts(taskId: string, swarmId: string, verdicts: CouncilMemberVerdict[], criteria: CouncilCriteria | null, roundNumber: number, config?: Partial<CouncilConfig>): CouncilSynthesis;
|
|
13
13
|
/**
|
|
14
14
|
* Synthesize phase-level council verdicts into a PhaseCouncilSynthesis.
|
|
@@ -19,3 +19,9 @@ export declare function synthesizeCouncilVerdicts(taskId: string, swarmId: strin
|
|
|
19
19
|
* (see writePhaseCouncilEvidence in submit-phase-council-verdicts.ts).
|
|
20
20
|
*/
|
|
21
21
|
export declare function synthesizePhaseCouncilAdvisory(phaseNumber: number, phaseSummary: string, verdicts: CouncilMemberVerdict[], roundNumber: number, config?: Partial<CouncilConfig>, _workingDir?: string): PhaseCouncilSynthesis;
|
|
22
|
+
/**
|
|
23
|
+
* Synthesize project-level final council verdicts into a FinalCouncilSynthesis.
|
|
24
|
+
* This uses the same five-member verdict semantics as phase council, but the
|
|
25
|
+
* output is scoped to completed-project review and the final_council gate.
|
|
26
|
+
*/
|
|
27
|
+
export declare function synthesizeFinalCouncilAdvisory(projectSummary: string, verdicts: CouncilMemberVerdict[], roundNumber: number, config?: Partial<CouncilConfig>): FinalCouncilSynthesis;
|
package/dist/council/types.d.ts
CHANGED
|
@@ -87,6 +87,38 @@ export interface PhaseCouncilSynthesis {
|
|
|
87
87
|
/** Summary of the phase being reviewed */
|
|
88
88
|
phaseSummary?: string;
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Project-level final council synthesis result.
|
|
92
|
+
* Distinct from task-level and phase-level council results: this is the
|
|
93
|
+
* final project close gate and writes to .swarm/evidence/final-council.json.
|
|
94
|
+
*/
|
|
95
|
+
export interface FinalCouncilSynthesis {
|
|
96
|
+
/** Always 'project' - distinguishes final council from task/phase councils */
|
|
97
|
+
scope: 'project';
|
|
98
|
+
/** ISO 8601 */
|
|
99
|
+
timestamp: string;
|
|
100
|
+
overallVerdict: CouncilVerdict;
|
|
101
|
+
vetoedBy: CouncilAgent[] | null;
|
|
102
|
+
memberVerdicts: CouncilMemberVerdict[];
|
|
103
|
+
unresolvedConflicts: string[];
|
|
104
|
+
/** Severity HIGH + MEDIUM from veto members */
|
|
105
|
+
requiredFixes: CouncilFinding[];
|
|
106
|
+
/** Severity LOW or from non-veto members */
|
|
107
|
+
advisoryFindings: CouncilFinding[];
|
|
108
|
+
/** Project-level advisory notes for the architect */
|
|
109
|
+
advisoryNotes: string[];
|
|
110
|
+
/** Single markdown document for final project review */
|
|
111
|
+
unifiedFeedbackMd: string;
|
|
112
|
+
/** 1-indexed */
|
|
113
|
+
roundNumber: number;
|
|
114
|
+
allCriteriaMet: boolean;
|
|
115
|
+
/** Distinct council members that produced verdicts */
|
|
116
|
+
quorumSize: number;
|
|
117
|
+
/** Path where evidence was written */
|
|
118
|
+
evidencePath: '.swarm/evidence/final-council.json';
|
|
119
|
+
/** Summary of the completed project being reviewed */
|
|
120
|
+
projectSummary: string;
|
|
121
|
+
}
|
|
90
122
|
export interface CouncilCriteriaItem {
|
|
91
123
|
id: string;
|
|
92
124
|
description: string;
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var package_default;
|
|
|
33
33
|
var init_package = __esm(() => {
|
|
34
34
|
package_default = {
|
|
35
35
|
name: "opencode-swarm",
|
|
36
|
-
version: "7.17.
|
|
36
|
+
version: "7.17.2",
|
|
37
37
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
38
38
|
main: "dist/index.js",
|
|
39
39
|
types: "dist/index.d.ts",
|
|
@@ -61439,7 +61439,7 @@ Present the eleven gates with their defaults (DEFAULT_QA_GATES) as a single user
|
|
|
61439
61439
|
- mutation_test (default: OFF) — when enabled, runs mutation testing on source files touched this phase via generate_mutants + mutation_test + write_mutation_evidence at PHASE-WRAP; FAIL verdict blocks phase_complete; WARN is non-blocking (recommended for projects with coverage gaps or safety-critical code)
|
|
61440
61440
|
- council_general_review (default: OFF) — when enabled, MODE: SPECIFY runs convene_general_council on the draft spec before the critic-gate; the architect runs a curated web_search pass, dispatches council_generalist / council_skeptic / council_domain_expert in parallel with a shared RESEARCH CONTEXT block, deliberates on disagreements, and synthesizes the result directly into the spec (recommended for novel architecture, unclear best practices, or high-risk design decisions). Requires council.general.enabled: true and a configured search API key.
|
|
61441
61441
|
- drift_check (default: ON) — when enabled, mandatory per-phase drift verification via critic_drift_verifier at PHASE-WRAP; compares implemented changes against spec.md intent; hard-blocks phase_complete when spec.md exists and drift evidence is missing or REJECTED; advisory-only when no spec.md exists (recommended for all projects with a specification)
|
|
61442
|
-
- final_council (default: OFF)
|
|
61442
|
+
- final_council (default: OFF) - when enabled, after all phases complete the architect dispatches the same five phase-council members (\`critic\`, \`reviewer\`, \`sme\`, \`test_engineer\`, \`explorer\`) at project scope, collects \`CouncilMemberVerdict\` objects, and calls \`write_final_council_evidence\`. This is not General Council mode and does not require \`council.general.enabled\`.
|
|
61443
61443
|
|
|
61444
61444
|
One question, one message, defaults pre-stated. Wait for the user's answer.
|
|
61445
61445
|
|
|
@@ -63279,19 +63279,15 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
|
|
|
63279
63279
|
- \`.swarm/evidence/{phase}/mutation-gate.json\` exists with verdict 'pass' or 'warn' (written by YOU via the \`write_mutation_evidence\` tool after step 5.56) — ONLY required when \`mutation_test\` is enabled in the QA gate profile
|
|
63280
63280
|
If any required file is missing, run the missing gate first. Turbo mode skips all gates automatically.
|
|
63281
63281
|
NOTE: Steps 5.5, 5.55, and 5.56 are enforced by runtime hooks. If \`hallucination_guard\` is enabled and you skip the critic_hallucination_verifier delegation (or fail to call \`write_hallucination_evidence\`), phase_complete will be BLOCKED by the plugin. Similarly, if \`mutation_test\` is enabled and you skip step 5.56 (or fail to call \`write_mutation_evidence\`), phase_complete will be BLOCKED. These are not suggestions — they are hard enforcement mechanisms.
|
|
63282
|
-
5.7. **Final Council (conditional on QA gate
|
|
63282
|
+
5.7. **Final Council (conditional on QA gate - last phase only)**: Check whether \`final_council\` is enabled in the effective QA gate profile (visible via \`get_qa_gate_profile\`). If disabled, skip silently and proceed to step 6.
|
|
63283
63283
|
If enabled AND this is the LAST phase in the plan (all other phases have status 'complete' and no more phases remain):
|
|
63284
|
-
|
|
63285
|
-
2.
|
|
63286
|
-
3.
|
|
63287
|
-
4.
|
|
63288
|
-
5.
|
|
63289
|
-
6.
|
|
63290
|
-
|
|
63291
|
-
8. Present the final synthesis to the user as a project-close summary.
|
|
63292
|
-
9. Write the final council result to \`.swarm/evidence/final-council.json\`.
|
|
63293
|
-
10. Do NOT call \`/swarm close\` until the final council completes (if enabled). The evidence file \`.swarm/evidence/final-council.json\` must exist with an APPROVED verdict before \`/swarm close\` is permitted when final_council is enabled.
|
|
63294
|
-
If enabled but NOT the last phase, skip silently — final council only runs once, after all phases.
|
|
63284
|
+
1. Build a PROJECT DOSSIER from the completed plan, all phase summaries, changed-file summaries, and all relevant evidence artifacts. This is a completed-project review, not General Council mode.
|
|
63285
|
+
2. Dispatch \`{{AGENT_PREFIX}}critic\`, \`{{AGENT_PREFIX}}reviewer\`, \`{{AGENT_PREFIX}}sme\`, \`{{AGENT_PREFIX}}test_engineer\`, and \`{{AGENT_PREFIX}}explorer\` in PARALLEL with project-scoped context. Each member must review the entire completed body of work and return a \`CouncilMemberVerdict\` JSON object using \`agent\`, \`verdict\` (APPROVE|CONCERNS|REJECT), \`confidence\`, \`findings[]\`, \`criteriaAssessed[]\`, \`criteriaUnmet[]\`, and \`durationMs\`.
|
|
63286
|
+
3. Collect the five returned verdict objects. Do NOT fabricate, infer, or substitute verdicts. If a member does not return valid JSON, re-dispatch that member.
|
|
63287
|
+
4. Call \`write_final_council_evidence\` with \`phase\`, \`projectSummary\`, \`roundNumber\`, and the collected \`verdicts\` array. This writes \`.swarm/evidence/final-council.json\` with plan binding, member verdicts, and quorum metadata.
|
|
63288
|
+
5. Do NOT call \`convene_general_council\`, do NOT dispatch \`council_generalist\`, \`council_skeptic\`, or \`council_domain_expert\`, and do NOT require \`council.general.enabled\` for this gate. \`final_council\` is the same five-member phase council rerun at project scope.
|
|
63289
|
+
6. Do NOT call \`phase_complete\` or \`/swarm close\` until \`.swarm/evidence/final-council.json\` exists with an approved, plan-bound, quorumed final-council verdict. When \`final_council\` is enabled, \`phase_complete\` will block until that evidence exists.
|
|
63290
|
+
If enabled but NOT the last phase, skip silently - final council only runs once, after all phases.
|
|
63295
63291
|
6. Summarize to user
|
|
63296
63292
|
7. Ask: "Ready for Phase [N+1]?"
|
|
63297
63293
|
|
|
@@ -85356,6 +85352,99 @@ function buildPhaseCouncilFeedback(phaseNumber, phaseSummary, verdict, vetoedBy,
|
|
|
85356
85352
|
return lines.join(`
|
|
85357
85353
|
`);
|
|
85358
85354
|
}
|
|
85355
|
+
function synthesizeFinalCouncilAdvisory(projectSummary, verdicts, roundNumber, config3 = {}) {
|
|
85356
|
+
const cfg = { ...COUNCIL_DEFAULTS, ...config3 };
|
|
85357
|
+
const timestamp = new Date().toISOString();
|
|
85358
|
+
const quorumSize = new Set(verdicts.map((v) => v.agent)).size;
|
|
85359
|
+
const rejectingMembers = verdicts.filter((v) => v.verdict === "REJECT").map((v) => v.agent);
|
|
85360
|
+
let overallVerdict;
|
|
85361
|
+
if (cfg.vetoPriority && rejectingMembers.length > 0) {
|
|
85362
|
+
overallVerdict = "REJECT";
|
|
85363
|
+
} else if (verdicts.some((v) => v.verdict === "CONCERNS") || !cfg.vetoPriority && rejectingMembers.length > 0) {
|
|
85364
|
+
overallVerdict = "CONCERNS";
|
|
85365
|
+
} else {
|
|
85366
|
+
overallVerdict = "APPROVE";
|
|
85367
|
+
}
|
|
85368
|
+
const unresolvedConflicts = detectConflicts(verdicts);
|
|
85369
|
+
const rejectingSet = new Set(rejectingMembers);
|
|
85370
|
+
const vetoFindings = verdicts.filter((v) => rejectingSet.has(v.agent)).flatMap((v) => v.findings);
|
|
85371
|
+
const requiredFixes = vetoFindings.filter((f) => f.severity === "HIGH" || f.severity === "MEDIUM");
|
|
85372
|
+
const advisoryFindings = [
|
|
85373
|
+
...vetoFindings.filter((f) => f.severity === "LOW"),
|
|
85374
|
+
...verdicts.filter((v) => !rejectingSet.has(v.agent)).flatMap((v) => v.findings)
|
|
85375
|
+
];
|
|
85376
|
+
const advisoryNotes = [];
|
|
85377
|
+
if (advisoryFindings.length > 0) {
|
|
85378
|
+
advisoryNotes.push(`Final council found ${advisoryFindings.length} advisory finding(s). Review before project close.`);
|
|
85379
|
+
}
|
|
85380
|
+
if (quorumSize < 3) {
|
|
85381
|
+
advisoryNotes.push(`Final council quorum is ${quorumSize} members - dispatch additional project-scoped council members before closing the project.`);
|
|
85382
|
+
}
|
|
85383
|
+
const allUnmetIds = new Set(verdicts.flatMap((v) => v.criteriaUnmet));
|
|
85384
|
+
const allCriteriaMet = allUnmetIds.size === 0 && verdicts.length > 0;
|
|
85385
|
+
const unifiedFeedbackMd = buildFinalCouncilFeedback(projectSummary, overallVerdict, rejectingMembers, requiredFixes, advisoryFindings, unresolvedConflicts, roundNumber, cfg.maxRounds);
|
|
85386
|
+
return {
|
|
85387
|
+
scope: "project",
|
|
85388
|
+
timestamp,
|
|
85389
|
+
overallVerdict,
|
|
85390
|
+
vetoedBy: rejectingMembers.length > 0 ? rejectingMembers : null,
|
|
85391
|
+
memberVerdicts: verdicts,
|
|
85392
|
+
unresolvedConflicts,
|
|
85393
|
+
requiredFixes,
|
|
85394
|
+
advisoryFindings,
|
|
85395
|
+
advisoryNotes,
|
|
85396
|
+
unifiedFeedbackMd,
|
|
85397
|
+
roundNumber,
|
|
85398
|
+
allCriteriaMet,
|
|
85399
|
+
quorumSize,
|
|
85400
|
+
evidencePath: ".swarm/evidence/final-council.json",
|
|
85401
|
+
projectSummary
|
|
85402
|
+
};
|
|
85403
|
+
}
|
|
85404
|
+
function buildFinalCouncilFeedback(projectSummary, verdict, vetoedBy, requiredFixes, advisoryFindings, conflicts, roundNumber, maxRounds) {
|
|
85405
|
+
const lines = [
|
|
85406
|
+
`## Final Council Review - Round ${roundNumber}/${maxRounds}`,
|
|
85407
|
+
`**Scope:** completed project **Overall verdict:** ${verdict}`,
|
|
85408
|
+
""
|
|
85409
|
+
];
|
|
85410
|
+
if (projectSummary) {
|
|
85411
|
+
lines.push(`**Project Summary:** ${projectSummary}`);
|
|
85412
|
+
lines.push("");
|
|
85413
|
+
}
|
|
85414
|
+
if (vetoedBy.length > 0) {
|
|
85415
|
+
lines.push(`> BLOCKED: project close is blocked by ${vetoedBy.join(", ")}`);
|
|
85416
|
+
lines.push("");
|
|
85417
|
+
}
|
|
85418
|
+
if (requiredFixes.length > 0) {
|
|
85419
|
+
lines.push("### Required Fixes (must resolve before project close)");
|
|
85420
|
+
for (const f of requiredFixes) {
|
|
85421
|
+
lines.push(`- **[${f.severity}]** \`${f.location}\` - ${f.detail}`, ` _Evidence:_ ${f.evidence}`);
|
|
85422
|
+
}
|
|
85423
|
+
lines.push("");
|
|
85424
|
+
}
|
|
85425
|
+
if (conflicts.length > 0) {
|
|
85426
|
+
lines.push("### Conflicts to Resolve");
|
|
85427
|
+
lines.push("_The following council members gave contradictory project-close instructions. Architect must resolve before closing the project._");
|
|
85428
|
+
for (const c of conflicts) {
|
|
85429
|
+
lines.push(`- ${c}`);
|
|
85430
|
+
}
|
|
85431
|
+
lines.push("");
|
|
85432
|
+
}
|
|
85433
|
+
if (advisoryFindings.length > 0) {
|
|
85434
|
+
lines.push("### Advisory Findings (non-blocking)");
|
|
85435
|
+
for (const f of advisoryFindings) {
|
|
85436
|
+
lines.push(`- **[${f.severity}]** \`${f.location}\` - ${f.detail}`);
|
|
85437
|
+
}
|
|
85438
|
+
lines.push("");
|
|
85439
|
+
}
|
|
85440
|
+
if (verdict === "APPROVE") {
|
|
85441
|
+
lines.push("> Final council approved. Project may proceed to close.");
|
|
85442
|
+
} else if (roundNumber >= maxRounds) {
|
|
85443
|
+
lines.push(`> Max rounds (${maxRounds}) reached. Escalate to user - do not close the project automatically.`);
|
|
85444
|
+
}
|
|
85445
|
+
return lines.join(`
|
|
85446
|
+
`);
|
|
85447
|
+
}
|
|
85359
85448
|
|
|
85360
85449
|
// src/council/criteria-store.ts
|
|
85361
85450
|
import { existsSync as existsSync50, mkdirSync as mkdirSync23, readFileSync as readFileSync42, writeFileSync as writeFileSync16 } from "node:fs";
|
|
@@ -89823,6 +89912,41 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
|
|
|
89823
89912
|
}, null, 2);
|
|
89824
89913
|
}
|
|
89825
89914
|
}
|
|
89915
|
+
if (typeof entry.quorumSize !== "number" || !Number.isFinite(entry.quorumSize) || entry.quorumSize < 5) {
|
|
89916
|
+
return JSON.stringify({
|
|
89917
|
+
success: false,
|
|
89918
|
+
phase,
|
|
89919
|
+
status: "blocked",
|
|
89920
|
+
reason: "FINAL_COUNCIL_MISSING_QUORUM",
|
|
89921
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council evidence is missing valid quorum metadata. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate quorumed evidence.`,
|
|
89922
|
+
agentsDispatched,
|
|
89923
|
+
agentsMissing: [],
|
|
89924
|
+
warnings: []
|
|
89925
|
+
}, null, 2);
|
|
89926
|
+
}
|
|
89927
|
+
const requiredFinalCouncilMembers = [
|
|
89928
|
+
"critic",
|
|
89929
|
+
"reviewer",
|
|
89930
|
+
"sme",
|
|
89931
|
+
"test_engineer",
|
|
89932
|
+
"explorer"
|
|
89933
|
+
];
|
|
89934
|
+
const membersVoted = Array.isArray(entry.membersVoted) ? entry.membersVoted.filter((member) => typeof member === "string") : [];
|
|
89935
|
+
const membersAbsent = Array.isArray(entry.membersAbsent) ? entry.membersAbsent.filter((member) => typeof member === "string") : [];
|
|
89936
|
+
const distinctMembersVoted = new Set(membersVoted);
|
|
89937
|
+
const hasAllRequiredMembers = requiredFinalCouncilMembers.every((member) => distinctMembersVoted.has(member)) && distinctMembersVoted.size === requiredFinalCouncilMembers.length && membersAbsent.length === 0;
|
|
89938
|
+
if (!hasAllRequiredMembers) {
|
|
89939
|
+
return JSON.stringify({
|
|
89940
|
+
success: false,
|
|
89941
|
+
phase,
|
|
89942
|
+
status: "blocked",
|
|
89943
|
+
reason: "FINAL_COUNCIL_MISSING_QUORUM",
|
|
89944
|
+
message: `Phase ${phase} (last phase) cannot be completed: final council evidence does not prove all five required members voted. Re-run the project-scoped five-member final council and call write_final_council_evidence to generate complete evidence.`,
|
|
89945
|
+
agentsDispatched,
|
|
89946
|
+
agentsMissing: [],
|
|
89947
|
+
warnings: []
|
|
89948
|
+
}, null, 2);
|
|
89949
|
+
}
|
|
89826
89950
|
if (entry.verdict === "rejected" || entry.verdict === "REJECTED") {
|
|
89827
89951
|
return JSON.stringify({
|
|
89828
89952
|
success: false,
|
|
@@ -89862,11 +89986,11 @@ Advisory notes: ${advisoryNotes.join("; ")}` : "";
|
|
|
89862
89986
|
status: "blocked",
|
|
89863
89987
|
reason: "FINAL_COUNCIL_REQUIRED",
|
|
89864
89988
|
final_council_required: true,
|
|
89865
|
-
message: `Phase ${phase} (last phase) cannot be completed: final_council is enabled and final council evidence not found at .swarm/evidence/final-council.json.
|
|
89989
|
+
message: `Phase ${phase} (last phase) cannot be completed: final_council is enabled and final council evidence not found at .swarm/evidence/final-council.json. Dispatch critic, reviewer, sme, test_engineer, and explorer with project-scoped context, collect their CouncilMemberVerdict JSON, and call write_final_council_evidence before completing the project. Do not use convene_general_council for this gate.`,
|
|
89866
89990
|
agentsDispatched,
|
|
89867
89991
|
agentsMissing: [],
|
|
89868
89992
|
warnings: [
|
|
89869
|
-
`Final council required
|
|
89993
|
+
`Final council required - dispatch the five project-scoped council members, then call write_final_council_evidence to persist quorumed evidence.`
|
|
89870
89994
|
]
|
|
89871
89995
|
}, null, 2);
|
|
89872
89996
|
}
|
|
@@ -97343,7 +97467,7 @@ var set_qa_gates = createSwarmTool({
|
|
|
97343
97467
|
mutation_test: exports_external.boolean().optional().describe("Enable the mutation-testing gate (default: off). Requires mutation " + "tests to achieve a passing kill rate before phase completion; " + "WARN verdict allows advancement, FAIL blocks."),
|
|
97344
97468
|
council_general_review: exports_external.boolean().optional().describe("Enable the council_general_review gate (default: off). When on, " + "MODE: SPECIFY runs convene_general_council on the draft spec " + "before the critic-gate, folding multi-model deliberation into " + "the spec. Requires council.general.enabled and a search API key."),
|
|
97345
97469
|
drift_check: exports_external.boolean().optional().describe("Enable drift verification gate (default: on). Blocks phase_complete " + "until drift-verifier.json has an approved verdict. When disabled, " + "drift verification is skipped entirely."),
|
|
97346
|
-
final_council: exports_external.boolean().optional().describe("Enable the final_council gate (default: off). When on, " + "after all phases complete the architect
|
|
97470
|
+
final_council: exports_external.boolean().optional().describe("Enable the final_council gate (default: off). When on, " + "after all phases complete the architect dispatches critic, reviewer, " + "sme, test_engineer, and explorer with project-scoped context, " + "collects their CouncilMemberVerdict objects, and calls " + "write_final_council_evidence. This is not General Council mode " + "and does not require council.general.enabled."),
|
|
97347
97471
|
project_type: exports_external.string().optional().describe('Project type label (e.g. "ts", "python"). Only applied when the profile is being created for the first time.')
|
|
97348
97472
|
},
|
|
97349
97473
|
execute: async (args2, directory) => {
|
|
@@ -102301,59 +102425,94 @@ var write_drift_evidence = createSwarmTool({
|
|
|
102301
102425
|
});
|
|
102302
102426
|
// src/tools/write-final-council-evidence.ts
|
|
102303
102427
|
init_zod();
|
|
102428
|
+
init_loader();
|
|
102429
|
+
import fs107 from "node:fs";
|
|
102430
|
+
import path135 from "node:path";
|
|
102304
102431
|
init_utils2();
|
|
102305
102432
|
init_manager();
|
|
102306
102433
|
init_create_tool();
|
|
102307
|
-
|
|
102308
|
-
|
|
102309
|
-
|
|
102310
|
-
|
|
102311
|
-
|
|
102312
|
-
|
|
102313
|
-
|
|
102314
|
-
|
|
102315
|
-
|
|
102316
|
-
|
|
102317
|
-
|
|
102434
|
+
var FINAL_COUNCIL_MEMBERS = [
|
|
102435
|
+
"critic",
|
|
102436
|
+
"reviewer",
|
|
102437
|
+
"sme",
|
|
102438
|
+
"test_engineer",
|
|
102439
|
+
"explorer"
|
|
102440
|
+
];
|
|
102441
|
+
var VerdictSchema3 = exports_external.object({
|
|
102442
|
+
agent: exports_external.enum(FINAL_COUNCIL_MEMBERS),
|
|
102443
|
+
verdict: exports_external.enum(["APPROVE", "CONCERNS", "REJECT"]),
|
|
102444
|
+
confidence: exports_external.number().min(0).max(1),
|
|
102445
|
+
findings: exports_external.array(exports_external.object({
|
|
102446
|
+
severity: exports_external.enum(["HIGH", "MEDIUM", "LOW"]),
|
|
102447
|
+
category: exports_external.string().min(1),
|
|
102448
|
+
location: exports_external.string(),
|
|
102449
|
+
detail: exports_external.string(),
|
|
102450
|
+
evidence: exports_external.string()
|
|
102451
|
+
})),
|
|
102452
|
+
criteriaAssessed: exports_external.array(exports_external.string()),
|
|
102453
|
+
criteriaUnmet: exports_external.array(exports_external.string()),
|
|
102454
|
+
durationMs: exports_external.number().nonnegative()
|
|
102455
|
+
});
|
|
102456
|
+
var ArgsSchema6 = exports_external.object({
|
|
102457
|
+
phase: exports_external.number().int().min(1),
|
|
102458
|
+
projectSummary: exports_external.string().min(1),
|
|
102459
|
+
roundNumber: exports_external.number().int().min(1).max(10).optional(),
|
|
102460
|
+
verdicts: exports_external.array(VerdictSchema3).min(1).max(5)
|
|
102461
|
+
});
|
|
102462
|
+
function normalizeFinalVerdict(verdict) {
|
|
102463
|
+
return verdict === "APPROVE" ? "approved" : "rejected";
|
|
102318
102464
|
}
|
|
102319
102465
|
async function executeWriteFinalCouncilEvidence(args2, directory) {
|
|
102320
|
-
const
|
|
102321
|
-
if (!
|
|
102322
|
-
return JSON.stringify({
|
|
102323
|
-
success: false,
|
|
102324
|
-
phase,
|
|
102325
|
-
message: "Invalid phase: must be a positive integer"
|
|
102326
|
-
}, null, 2);
|
|
102327
|
-
}
|
|
102328
|
-
const validVerdicts = ["APPROVED", "NEEDS_REVISION"];
|
|
102329
|
-
if (!validVerdicts.includes(args2.verdict)) {
|
|
102466
|
+
const parsed = ArgsSchema6.safeParse(args2);
|
|
102467
|
+
if (!parsed.success) {
|
|
102330
102468
|
return JSON.stringify({
|
|
102331
102469
|
success: false,
|
|
102332
|
-
|
|
102333
|
-
|
|
102470
|
+
reason: "invalid arguments",
|
|
102471
|
+
errors: parsed.error.issues.map((i2) => ({
|
|
102472
|
+
path: i2.path.join("."),
|
|
102473
|
+
message: i2.message
|
|
102474
|
+
}))
|
|
102334
102475
|
}, null, 2);
|
|
102335
102476
|
}
|
|
102336
|
-
const
|
|
102337
|
-
|
|
102477
|
+
const input = parsed.data;
|
|
102478
|
+
const config3 = loadPluginConfig(directory);
|
|
102479
|
+
const requiredMembers = FINAL_COUNCIL_MEMBERS.length;
|
|
102480
|
+
const distinctMembers = new Set(input.verdicts.map((v) => v.agent));
|
|
102481
|
+
const membersVoted = [...distinctMembers];
|
|
102482
|
+
const membersAbsent = FINAL_COUNCIL_MEMBERS.filter((m) => !distinctMembers.has(m));
|
|
102483
|
+
if (membersVoted.length < requiredMembers) {
|
|
102338
102484
|
return JSON.stringify({
|
|
102339
102485
|
success: false,
|
|
102340
|
-
|
|
102341
|
-
message: "
|
|
102486
|
+
reason: "insufficient_quorum",
|
|
102487
|
+
message: `Final council quorum not met: ${membersVoted.length} of ${requiredMembers} required members provided verdicts. ` + `Members voted: [${membersVoted.join(", ")}]. ` + `Members absent: [${membersAbsent.join(", ")}]. ` + `Dispatch the absent council members with project-scoped context and collect their verdicts before calling write_final_council_evidence.`,
|
|
102488
|
+
membersVoted,
|
|
102489
|
+
membersAbsent,
|
|
102490
|
+
quorumRequired: requiredMembers
|
|
102342
102491
|
}, null, 2);
|
|
102343
102492
|
}
|
|
102344
|
-
const
|
|
102493
|
+
const synthesis = synthesizeFinalCouncilAdvisory(input.projectSummary.trim(), input.verdicts, input.roundNumber ?? 1, config3.council);
|
|
102345
102494
|
const plan = await loadPlan(directory);
|
|
102346
102495
|
const planId = plan ? derivePlanId(plan) : "unknown";
|
|
102496
|
+
const normalizedVerdict = normalizeFinalVerdict(synthesis.overallVerdict);
|
|
102347
102497
|
const evidenceEntry = {
|
|
102348
102498
|
type: "final-council",
|
|
102349
|
-
phase,
|
|
102499
|
+
phase: input.phase,
|
|
102350
102500
|
plan_id: planId,
|
|
102351
102501
|
verdict: normalizedVerdict,
|
|
102352
|
-
|
|
102353
|
-
|
|
102354
|
-
|
|
102355
|
-
|
|
102356
|
-
|
|
102502
|
+
rawCouncilVerdict: synthesis.overallVerdict,
|
|
102503
|
+
quorumSize: synthesis.quorumSize,
|
|
102504
|
+
membersVoted,
|
|
102505
|
+
membersAbsent,
|
|
102506
|
+
requiredFixes: synthesis.requiredFixes,
|
|
102507
|
+
advisoryFindings: synthesis.advisoryFindings,
|
|
102508
|
+
advisoryNotes: synthesis.advisoryNotes,
|
|
102509
|
+
unresolvedConflicts: synthesis.unresolvedConflicts,
|
|
102510
|
+
roundNumber: synthesis.roundNumber,
|
|
102511
|
+
allCriteriaMet: synthesis.allCriteriaMet,
|
|
102512
|
+
memberVerdicts: synthesis.memberVerdicts,
|
|
102513
|
+
unifiedFeedbackMd: synthesis.unifiedFeedbackMd,
|
|
102514
|
+
projectSummary: synthesis.projectSummary,
|
|
102515
|
+
timestamp: synthesis.timestamp
|
|
102357
102516
|
};
|
|
102358
102517
|
const filename = "final-council.json";
|
|
102359
102518
|
const relativePath = path135.join("evidence", filename);
|
|
@@ -102363,10 +102522,13 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
|
|
|
102363
102522
|
} catch (error93) {
|
|
102364
102523
|
return JSON.stringify({
|
|
102365
102524
|
success: false,
|
|
102366
|
-
phase,
|
|
102525
|
+
phase: input.phase,
|
|
102367
102526
|
message: error93 instanceof Error ? error93.message : "Failed to validate path"
|
|
102368
102527
|
}, null, 2);
|
|
102369
102528
|
}
|
|
102529
|
+
const evidenceContent = {
|
|
102530
|
+
entries: [evidenceEntry]
|
|
102531
|
+
};
|
|
102370
102532
|
const evidenceDir = path135.dirname(validatedPath);
|
|
102371
102533
|
try {
|
|
102372
102534
|
await fs107.promises.mkdir(evidenceDir, { recursive: true });
|
|
@@ -102375,41 +102537,53 @@ async function executeWriteFinalCouncilEvidence(args2, directory) {
|
|
|
102375
102537
|
await fs107.promises.rename(tempPath, validatedPath);
|
|
102376
102538
|
return JSON.stringify({
|
|
102377
102539
|
success: true,
|
|
102378
|
-
phase,
|
|
102540
|
+
phase: input.phase,
|
|
102541
|
+
overallVerdict: synthesis.overallVerdict,
|
|
102379
102542
|
verdict: normalizedVerdict,
|
|
102380
|
-
|
|
102543
|
+
vetoedBy: synthesis.vetoedBy,
|
|
102544
|
+
roundNumber: synthesis.roundNumber,
|
|
102545
|
+
allCriteriaMet: synthesis.allCriteriaMet,
|
|
102546
|
+
requiredFixesCount: synthesis.requiredFixes.length,
|
|
102547
|
+
advisoryFindingsCount: synthesis.advisoryFindings.length,
|
|
102548
|
+
unresolvedConflictsCount: synthesis.unresolvedConflicts.length,
|
|
102549
|
+
advisoryNotes: synthesis.advisoryNotes,
|
|
102550
|
+
membersVoted,
|
|
102551
|
+
membersAbsent,
|
|
102552
|
+
quorumSize: synthesis.quorumSize,
|
|
102553
|
+
quorumMet: true,
|
|
102554
|
+
evidencePath: synthesis.evidencePath,
|
|
102555
|
+
unifiedFeedbackMd: synthesis.unifiedFeedbackMd,
|
|
102556
|
+
message: "Final council evidence written to .swarm/evidence/final-council.json"
|
|
102381
102557
|
}, null, 2);
|
|
102382
102558
|
} catch (error93) {
|
|
102383
102559
|
return JSON.stringify({
|
|
102384
102560
|
success: false,
|
|
102385
|
-
phase,
|
|
102561
|
+
phase: input.phase,
|
|
102386
102562
|
message: error93 instanceof Error ? error93.message : String(error93)
|
|
102387
102563
|
}, null, 2);
|
|
102388
102564
|
}
|
|
102389
102565
|
}
|
|
102390
102566
|
var write_final_council_evidence = createSwarmTool({
|
|
102391
|
-
description: "Write final council evidence for a completed project.
|
|
102567
|
+
description: "Write final council evidence for a completed project. This is not General Council mode and does not use convene_general_council. PREREQUISITE: dispatch critic, reviewer, sme, test_engineer, and explorer as project-scoped Agent tasks, collect their CouncilMemberVerdict JSON, then call this tool to synthesize and persist .swarm/evidence/final-council.json.",
|
|
102392
102568
|
args: {
|
|
102393
|
-
phase: exports_external.number().int().min(1).describe("The phase number for the
|
|
102394
|
-
|
|
102395
|
-
|
|
102569
|
+
phase: exports_external.number().int().min(1).describe("The final phase number for the project being reviewed"),
|
|
102570
|
+
projectSummary: exports_external.string().min(1).describe("Summary of the completed project and total work reviewed"),
|
|
102571
|
+
roundNumber: exports_external.number().int().min(1).max(10).optional().describe("1-indexed final council round number. Defaults to 1."),
|
|
102572
|
+
verdicts: exports_external.array(VerdictSchema3).min(1).max(5).describe("Collected CouncilMemberVerdict objects from critic, reviewer, sme, test_engineer, and explorer.")
|
|
102396
102573
|
},
|
|
102397
102574
|
execute: async (args2, directory) => {
|
|
102398
|
-
const
|
|
102399
|
-
|
|
102400
|
-
const writeFinalCouncilEvidenceArgs = {
|
|
102401
|
-
phase: Number(args2.phase),
|
|
102402
|
-
verdict: String(args2.verdict),
|
|
102403
|
-
summary: String(args2.summary ?? "")
|
|
102404
|
-
};
|
|
102405
|
-
return await executeWriteFinalCouncilEvidence(writeFinalCouncilEvidenceArgs, directory);
|
|
102406
|
-
} catch (error93) {
|
|
102575
|
+
const parsed = ArgsSchema6.safeParse(args2);
|
|
102576
|
+
if (!parsed.success) {
|
|
102407
102577
|
return JSON.stringify({
|
|
102408
102578
|
success: false,
|
|
102409
|
-
|
|
102410
|
-
|
|
102579
|
+
reason: "invalid arguments",
|
|
102580
|
+
errors: parsed.error.issues.map((i2) => ({
|
|
102581
|
+
path: i2.path.join("."),
|
|
102582
|
+
message: i2.message
|
|
102583
|
+
}))
|
|
102411
102584
|
}, null, 2);
|
|
102412
102585
|
}
|
|
102586
|
+
return await executeWriteFinalCouncilEvidence(parsed.data, directory);
|
|
102413
102587
|
}
|
|
102414
102588
|
});
|
|
102415
102589
|
// src/tools/write-hallucination-evidence.ts
|
|
@@ -102418,7 +102592,7 @@ init_utils2();
|
|
|
102418
102592
|
init_create_tool();
|
|
102419
102593
|
import fs108 from "node:fs";
|
|
102420
102594
|
import path136 from "node:path";
|
|
102421
|
-
function
|
|
102595
|
+
function normalizeVerdict2(verdict) {
|
|
102422
102596
|
switch (verdict) {
|
|
102423
102597
|
case "APPROVED":
|
|
102424
102598
|
return "approved";
|
|
@@ -102453,7 +102627,7 @@ async function executeWriteHallucinationEvidence(args2, directory) {
|
|
|
102453
102627
|
message: "Invalid summary: must be a non-empty string"
|
|
102454
102628
|
}, null, 2);
|
|
102455
102629
|
}
|
|
102456
|
-
const normalizedVerdict =
|
|
102630
|
+
const normalizedVerdict = normalizeVerdict2(args2.verdict);
|
|
102457
102631
|
const evidenceEntry = {
|
|
102458
102632
|
type: "hallucination-verification",
|
|
102459
102633
|
verdict: normalizedVerdict,
|
|
@@ -102529,7 +102703,7 @@ init_utils2();
|
|
|
102529
102703
|
init_create_tool();
|
|
102530
102704
|
import fs109 from "node:fs";
|
|
102531
102705
|
import path137 from "node:path";
|
|
102532
|
-
function
|
|
102706
|
+
function normalizeVerdict3(verdict) {
|
|
102533
102707
|
switch (verdict) {
|
|
102534
102708
|
case "PASS":
|
|
102535
102709
|
return "pass";
|
|
@@ -102586,7 +102760,7 @@ async function executeWriteMutationEvidence(args2, directory) {
|
|
|
102586
102760
|
message: "Invalid summary: must be a non-empty string"
|
|
102587
102761
|
}, null, 2);
|
|
102588
102762
|
}
|
|
102589
|
-
const normalizedVerdict =
|
|
102763
|
+
const normalizedVerdict = normalizeVerdict3(args2.verdict);
|
|
102590
102764
|
const evidenceEntry = {
|
|
102591
102765
|
type: "mutation-gate",
|
|
102592
102766
|
verdict: normalizedVerdict,
|
|
@@ -1,29 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Write final council evidence
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Write final council evidence for the project-scoped final council gate.
|
|
3
|
+
*
|
|
4
|
+
* The final council is not General Council mode. It accepts the same
|
|
5
|
+
* five-member CouncilMemberVerdict objects used by phase council, synthesized
|
|
6
|
+
* at completed-project scope.
|
|
5
7
|
*/
|
|
6
8
|
import type { ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import type { CouncilMemberVerdict } from '../council/types';
|
|
11
|
+
export declare const ArgsSchema: z.ZodObject<{
|
|
12
|
+
phase: z.ZodNumber;
|
|
13
|
+
projectSummary: z.ZodString;
|
|
14
|
+
roundNumber: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
verdicts: z.ZodArray<z.ZodObject<{
|
|
16
|
+
agent: z.ZodEnum<{
|
|
17
|
+
reviewer: "reviewer";
|
|
18
|
+
test_engineer: "test_engineer";
|
|
19
|
+
explorer: "explorer";
|
|
20
|
+
sme: "sme";
|
|
21
|
+
critic: "critic";
|
|
22
|
+
}>;
|
|
23
|
+
verdict: z.ZodEnum<{
|
|
24
|
+
APPROVE: "APPROVE";
|
|
25
|
+
REJECT: "REJECT";
|
|
26
|
+
CONCERNS: "CONCERNS";
|
|
27
|
+
}>;
|
|
28
|
+
confidence: z.ZodNumber;
|
|
29
|
+
findings: z.ZodArray<z.ZodObject<{
|
|
30
|
+
severity: z.ZodEnum<{
|
|
31
|
+
HIGH: "HIGH";
|
|
32
|
+
MEDIUM: "MEDIUM";
|
|
33
|
+
LOW: "LOW";
|
|
34
|
+
}>;
|
|
35
|
+
category: z.ZodString;
|
|
36
|
+
location: z.ZodString;
|
|
37
|
+
detail: z.ZodString;
|
|
38
|
+
evidence: z.ZodString;
|
|
39
|
+
}, z.core.$strip>>;
|
|
40
|
+
criteriaAssessed: z.ZodArray<z.ZodString>;
|
|
41
|
+
criteriaUnmet: z.ZodArray<z.ZodString>;
|
|
42
|
+
durationMs: z.ZodNumber;
|
|
43
|
+
}, z.core.$strip>>;
|
|
44
|
+
}, z.core.$strip>;
|
|
7
45
|
/**
|
|
8
|
-
* Arguments for the write_final_council_evidence tool
|
|
46
|
+
* Arguments for the write_final_council_evidence tool.
|
|
9
47
|
*/
|
|
10
48
|
export interface WriteFinalCouncilEvidenceArgs {
|
|
11
49
|
/** The phase number for the final council verdict */
|
|
12
50
|
phase: number;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
|
|
51
|
+
/** Summary of the completed project being reviewed */
|
|
52
|
+
projectSummary: string;
|
|
53
|
+
/** 1-indexed final council round number */
|
|
54
|
+
roundNumber?: number;
|
|
55
|
+
/** Collected verdicts from critic, reviewer, sme, test_engineer, explorer */
|
|
56
|
+
verdicts: CouncilMemberVerdict[];
|
|
17
57
|
}
|
|
18
58
|
/**
|
|
19
59
|
* Execute the write_final_council_evidence tool.
|
|
20
|
-
* Validates input,
|
|
21
|
-
* @param args - The write final council evidence arguments
|
|
22
|
-
* @param directory - Working directory
|
|
23
|
-
* @returns JSON string with success status and details
|
|
60
|
+
* Validates input, synthesizes project-scoped council evidence, and writes it.
|
|
24
61
|
*/
|
|
25
|
-
export declare function executeWriteFinalCouncilEvidence(args:
|
|
62
|
+
export declare function executeWriteFinalCouncilEvidence(args: unknown, directory: string): Promise<string>;
|
|
26
63
|
/**
|
|
27
|
-
* Tool definition for write_final_council_evidence
|
|
64
|
+
* Tool definition for write_final_council_evidence.
|
|
28
65
|
*/
|
|
29
66
|
export declare const write_final_council_evidence: ToolDefinition;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.17.
|
|
3
|
+
"version": "7.17.2",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|