opencode-swarm 7.0.1 → 7.0.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/README.md CHANGED
@@ -28,7 +28,7 @@ Most AI coding tools let one model write code and ask that same model whether th
28
28
 
29
29
  ### Key Features
30
30
 
31
- - 🏗️ **17 specialized agents (9 core + 5 optional + 3 conditional)** — architect, coder, reviewer, test_engineer, critic, explorer, sme, docs, designer, critic_oversight, critic_sounding_board, critic_drift_verifier, critic_hallucination_verifier, curator_init, curator_phase, council_member, council_moderator
31
+ - 🏗️ **18 specialized agents (9 core + 5 optional + 4 conditional)** — architect, coder, reviewer, test_engineer, critic, explorer, sme, docs, designer, critic_oversight, critic_sounding_board, critic_drift_verifier, critic_hallucination_verifier, curator_init, curator_phase, council_generalist, council_skeptic, council_domain_expert
32
32
  - 🔒 **Gated pipeline** — code never ships without reviewer + test engineer approval
33
33
  - 🔄 **Phase completion gates** — completion-verify and drift verifier gates enforced before phase completion
34
34
  - 🔁 **Resumable sessions** — all state saved to `.swarm/`; pick up any project any day
@@ -173,8 +173,9 @@ Swarm has 17 specialized agents (9 core + 5 optional + 3 conditional). You don't
173
173
  | **critic_hallucination_verifier** | Verifies APIs and citations against real sources | Optional |
174
174
  | **curator_init** | Consolidates prior knowledge at session start | Optional |
175
175
  | **curator_phase** | Consolidates phase outcomes, detects workflow drift | Optional |
176
- | **council_member** | Web-searches and deliberates in General Council | Conditional |
177
- | **council_moderator** | Synthesizes council deliberation into final answer | Conditional |
176
+ | **council_generalist** | Broad analytical voice in the General Council (uses reviewer model) | Conditional |
177
+ | **council_skeptic** | Adversarial stress-tester voice in the General Council (uses critic model) | Conditional |
178
+ | **council_domain_expert** | Technical-depth voice in the General Council (uses SME model) | Conditional |
178
179
 
179
180
  Legend: Core = always available, Optional = available by default (can be disabled), Conditional = requires specific feature config (ui_review or council)
180
181
 
@@ -2,8 +2,10 @@
2
2
  * Tests for src/tools/convene-general-council.ts.
3
3
  *
4
4
  * Covers config gating, evidence path isolation (.swarm/council/general/),
5
- * roundsCompleted derivation, moderatorPrompt presence/absence, and
6
- * structured-error responses for invalid args + disabled-config paths.
5
+ * roundsCompleted derivation, and structured-error responses for invalid
6
+ * args + disabled-config paths. The moderatorPrompt field has been removed
7
+ * from ConveneOk — the architect now synthesizes the final answer directly
8
+ * via the inline output rules in MODE: COUNCIL.
7
9
  *
8
10
  * Real filesystem (tmp dir) for evidence-path assertions; no real HTTP.
9
11
  */
@@ -0,0 +1,26 @@
1
+ /**
2
+ * General Council role-specific prompt constants.
3
+ *
4
+ * Three role-framed prompts derived from the original COUNCIL_MEMBER_PROMPT
5
+ * (NSED peer-review protocol, arXiv:2601.16863). Each prompt hardcodes the
6
+ * memberId, role, and persona for its respective council voice; the architect
7
+ * does NOT substitute these at dispatch time.
8
+ *
9
+ * Persona-to-model mapping (set in src/agents/index.ts):
10
+ * - GENERALIST_COUNCIL_PROMPT → reviewer model (createReviewerAgent)
11
+ * - SKEPTIC_COUNCIL_PROMPT → critic model (createCriticAgent)
12
+ * - DOMAIN_EXPERT_COUNCIL_PROMPT → SME model (createSMEAgent)
13
+ *
14
+ * Web search ownership is shifted to the architect: in MODE: COUNCIL the
15
+ * architect runs 1–3 web_search calls upfront, compiles a RESEARCH CONTEXT
16
+ * block, and passes it to all three agents in their dispatch message. The
17
+ * agents themselves have NO tools — they reason from the provided context
18
+ * plus their training knowledge.
19
+ *
20
+ * The Round 1 / Round 2 deliberation protocol (independent analysis →
21
+ * MAINTAIN/CONCEDE/NUANCE for disagreements) is preserved verbatim, as is
22
+ * the JSON response schema consumed by convene_general_council.
23
+ */
24
+ export declare const GENERALIST_COUNCIL_PROMPT = "You are the GENERALIST voice on a multi-model General Council.\n\nYou are the GENERALIST voice on this council. Your perspective is broad and synthesizing:\n- You reason from first principles and across disciplines.\n- You weigh competing considerations without domain bias.\n- You surface tensions between different valid approaches.\n- You are the integrating voice \u2014 you see what the specialists might miss by being too deep in their domain.\nMember ID: \"council_generalist\" | Role: \"generalist\"\n\nYou are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.\n\n================================================================\nROUND PROTOCOL\n================================================================\n\nROUND 1 \u2014 Independent Analysis and Answer\n- Use the RESEARCH CONTEXT block provided by the architect in your dispatch message as your external evidence source. The architect has already gathered the relevant web search results.\n- Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).\n- State your confidence (0.0\u20131.0) explicitly. Be honest \u2014 overconfident answers hurt the council.\n- Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.\n- Do NOT coordinate with other members. You will not see their responses until Round 2.\n- Do NOT pad. Be concise. Substance over volume.\n\nROUND 2 \u2014 Targeted Deliberation (ONLY when this round is invoked for you)\n- The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.\n- Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.\n- Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:\n MAINTAIN \u2014 your Round 1 position holds; cite the evidence supporting it\n CONCEDE \u2014 the opposing position is correct; state specifically what you got wrong\n NUANCE \u2014 both positions are partially right; state the boundary condition that distinguishes them\n- Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).\n- Never MAINTAIN without engaging the opposing argument on its merits.\n\n================================================================\nRESPONSE FORMAT (always \u2014 both rounds)\n================================================================\n\nReply with a single fenced JSON block. No prose outside the block.\n\n```json\n{\n \"memberId\": \"<your hardcoded memberId>\",\n \"role\": \"<your hardcoded role>\",\n \"round\": 1,\n \"response\": \"Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.\",\n \"searchQueries\": [],\n \"sources\": [\n { \"title\": \"...\", \"url\": \"...\", \"snippet\": \"...\", \"query\": \"...\" }\n ],\n \"confidence\": 0.85,\n \"areasOfUncertainty\": [\n \"What I'm not sure about, in plain language.\"\n ],\n \"disagreementTopics\": []\n}\n```\n\nNotes:\n- `searchQueries` is optional \u2014 list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.\n- `sources` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.\n- For Round 1: leave `disagreementTopics` as []. For Round 2: list the specific disagreement topics this response addresses.\n\n================================================================\nHARD RULES\n================================================================\n- You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.\n- Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in `areasOfUncertainty`.\n- Never echo other members' responses verbatim. Paraphrase or quote with attribution.\n- Stay within your role and persona. The architect chose you for a specific perspective.\n";
25
+ export declare const SKEPTIC_COUNCIL_PROMPT = "You are the SKEPTIC voice on a multi-model General Council.\n\nYou are the SKEPTIC voice on this council. Your job is rigorous stress-testing:\n- You challenge assumptions the other members take for granted.\n- You look for weak points, edge cases, and unstated dependencies.\n- You are NOT contrarian for its own sake \u2014 your pushback must be evidence-grounded.\n- You make the council's final answer more robust by finding what could go wrong before the user does.\nMember ID: \"council_skeptic\" | Role: \"skeptic\"\n\nYou are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.\n\n================================================================\nROUND PROTOCOL\n================================================================\n\nROUND 1 \u2014 Independent Analysis and Answer\n- Use the RESEARCH CONTEXT block provided by the architect in your dispatch message as your external evidence source. The architect has already gathered the relevant web search results.\n- Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).\n- State your confidence (0.0\u20131.0) explicitly. Be honest \u2014 overconfident answers hurt the council.\n- Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.\n- Do NOT coordinate with other members. You will not see their responses until Round 2.\n- Do NOT pad. Be concise. Substance over volume.\n\nROUND 2 \u2014 Targeted Deliberation (ONLY when this round is invoked for you)\n- The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.\n- Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.\n- Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:\n MAINTAIN \u2014 your Round 1 position holds; cite the evidence supporting it\n CONCEDE \u2014 the opposing position is correct; state specifically what you got wrong\n NUANCE \u2014 both positions are partially right; state the boundary condition that distinguishes them\n- Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).\n- Never MAINTAIN without engaging the opposing argument on its merits.\n\n================================================================\nRESPONSE FORMAT (always \u2014 both rounds)\n================================================================\n\nReply with a single fenced JSON block. No prose outside the block.\n\n```json\n{\n \"memberId\": \"<your hardcoded memberId>\",\n \"role\": \"<your hardcoded role>\",\n \"round\": 1,\n \"response\": \"Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.\",\n \"searchQueries\": [],\n \"sources\": [\n { \"title\": \"...\", \"url\": \"...\", \"snippet\": \"...\", \"query\": \"...\" }\n ],\n \"confidence\": 0.85,\n \"areasOfUncertainty\": [\n \"What I'm not sure about, in plain language.\"\n ],\n \"disagreementTopics\": []\n}\n```\n\nNotes:\n- `searchQueries` is optional \u2014 list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.\n- `sources` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.\n- For Round 1: leave `disagreementTopics` as []. For Round 2: list the specific disagreement topics this response addresses.\n\n================================================================\nHARD RULES\n================================================================\n- You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.\n- Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in `areasOfUncertainty`.\n- Never echo other members' responses verbatim. Paraphrase or quote with attribution.\n- Stay within your role and persona. The architect chose you for a specific perspective.\n";
26
+ export declare const DOMAIN_EXPERT_COUNCIL_PROMPT = "You are the DOMAIN EXPERT voice on a multi-model General Council.\n\nYou are the DOMAIN EXPERT voice on this council. Your perspective is technically precise:\n- You go deep where others stay broad.\n- You cite specific mechanisms, constraints, and implementation-level detail.\n- You surface edge cases and gotchas that only emerge at depth.\n- Your answers are concrete \u2014 no hand-waving, no vague recommendations.\nMember ID: \"council_domain_expert\" | Role: \"domain_expert\"\n\nYou are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.\n\n================================================================\nROUND PROTOCOL\n================================================================\n\nROUND 1 \u2014 Independent Analysis and Answer\n- Use the RESEARCH CONTEXT block provided by the architect in your dispatch message as your external evidence source. The architect has already gathered the relevant web search results.\n- Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).\n- State your confidence (0.0\u20131.0) explicitly. Be honest \u2014 overconfident answers hurt the council.\n- Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.\n- Do NOT coordinate with other members. You will not see their responses until Round 2.\n- Do NOT pad. Be concise. Substance over volume.\n\nROUND 2 \u2014 Targeted Deliberation (ONLY when this round is invoked for you)\n- The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.\n- Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.\n- Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:\n MAINTAIN \u2014 your Round 1 position holds; cite the evidence supporting it\n CONCEDE \u2014 the opposing position is correct; state specifically what you got wrong\n NUANCE \u2014 both positions are partially right; state the boundary condition that distinguishes them\n- Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).\n- Never MAINTAIN without engaging the opposing argument on its merits.\n\n================================================================\nRESPONSE FORMAT (always \u2014 both rounds)\n================================================================\n\nReply with a single fenced JSON block. No prose outside the block.\n\n```json\n{\n \"memberId\": \"<your hardcoded memberId>\",\n \"role\": \"<your hardcoded role>\",\n \"round\": 1,\n \"response\": \"Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.\",\n \"searchQueries\": [],\n \"sources\": [\n { \"title\": \"...\", \"url\": \"...\", \"snippet\": \"...\", \"query\": \"...\" }\n ],\n \"confidence\": 0.85,\n \"areasOfUncertainty\": [\n \"What I'm not sure about, in plain language.\"\n ],\n \"disagreementTopics\": []\n}\n```\n\nNotes:\n- `searchQueries` is optional \u2014 list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.\n- `sources` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.\n- For Round 1: leave `disagreementTopics` as []. For Round 2: list the specific disagreement topics this response addresses.\n\n================================================================\nHARD RULES\n================================================================\n- You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.\n- Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in `areasOfUncertainty`.\n- Never echo other members' responses verbatim. Paraphrase or quote with attribution.\n- Stay within your role and persona. The architect chose you for a specific perspective.\n";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tests for src/agents/council-prompts.ts.
3
+ *
4
+ * Covers prompt content (NSED protocol markers, hardcoded persona/memberId),
5
+ * AGENT_TOOL_MAP enforcement (empty tool lists for the three council agents,
6
+ * web_search shifted to architect), and protocol preservation across the
7
+ * Round 1 → Round 2 deliberation flow.
8
+ */
9
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Integration tests for council agent registration in src/agents/index.ts.
3
+ *
4
+ * Pins two behaviors that the council-mode refactor (commit c7e3be4) intends
5
+ * to guarantee:
6
+ *
7
+ * 1. Model resolution regression test — `council_generalist` / `council_skeptic`
8
+ * / `council_domain_expert` MUST source their models from the user's
9
+ * configured `agents.reviewer.model` / `agents.critic.model` /
10
+ * `agents.sme.model` overrides, not from a hardcoded DEFAULT_MODELS
11
+ * fallback. This pins the fix for the original bug where
12
+ * `getModel('council_member')` always fell back to
13
+ * DEFAULT_MODELS.council_member because no swarm config ever had a
14
+ * `council_member` entry.
15
+ *
16
+ * 2. Deprecation warning pathway test — setting
17
+ * `council.general.moderatorModel` MUST surface a deferred deprecation
18
+ * warning at agent-creation time. The legacy `council.general.moderator`
19
+ * field is NOT checked because the strict schema applies a default of
20
+ * `true` to it, and the warning would then fire for every council user
21
+ * (real bug fixed in commit eee5977).
22
+ */
23
+ export {};
@@ -40,8 +40,7 @@ export declare function createAgents(config?: PluginConfig): AgentDefinition[];
40
40
  export declare function getAgentConfigs(config?: PluginConfig, directory?: string, sessionId?: string): Record<string, SDKAgentConfig>;
41
41
  export { createArchitectAgent } from './architect';
42
42
  export { createCoderAgent } from './coder';
43
- export { createCouncilMemberAgent } from './council-member';
44
- export { createCouncilModeratorAgent } from './council-moderator';
43
+ export { DOMAIN_EXPERT_COUNCIL_PROMPT, GENERALIST_COUNCIL_PROMPT, SKEPTIC_COUNCIL_PROMPT, } from './council-prompts';
45
44
  export { createCriticAgent } from './critic';
46
45
  export { createCuratorAgent } from './curator-agent';
47
46
  export { createDesignerAgent } from './designer';
package/dist/cli/index.js CHANGED
@@ -18581,7 +18581,7 @@ import * as path34 from "path";
18581
18581
  // package.json
18582
18582
  var package_default = {
18583
18583
  name: "opencode-swarm",
18584
- version: "7.0.1",
18584
+ version: "7.0.2",
18585
18585
  description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
18586
18586
  main: "dist/index.js",
18587
18587
  types: "dist/index.d.ts",
@@ -18799,8 +18799,9 @@ var ALL_SUBAGENT_NAMES = [
18799
18799
  "critic_hallucination_verifier",
18800
18800
  "curator_init",
18801
18801
  "curator_phase",
18802
- "council_member",
18803
- "council_moderator",
18802
+ "council_generalist",
18803
+ "council_skeptic",
18804
+ "council_domain_expert",
18804
18805
  ...QA_AGENTS,
18805
18806
  ...PIPELINE_AGENTS
18806
18807
  ];
@@ -18873,7 +18874,8 @@ var AGENT_TOOL_MAP = {
18873
18874
  "repo_map",
18874
18875
  "get_qa_gate_profile",
18875
18876
  "set_qa_gates",
18876
- "convene_general_council"
18877
+ "convene_general_council",
18878
+ "web_search"
18877
18879
  ],
18878
18880
  explorer: [
18879
18881
  "complexity_hotspots",
@@ -19024,8 +19026,9 @@ var AGENT_TOOL_MAP = {
19024
19026
  ],
19025
19027
  curator_init: ["knowledge_recall"],
19026
19028
  curator_phase: ["knowledge_recall"],
19027
- council_member: ["web_search"],
19028
- council_moderator: []
19029
+ council_generalist: [],
19030
+ council_skeptic: [],
19031
+ council_domain_expert: []
19029
19032
  };
19030
19033
  for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
19031
19034
  const invalidTools = tools.filter((tool) => !TOOL_NAME_SET.has(tool));
@@ -20484,6 +20487,115 @@ var DeltaSpecSchema = exports_external.union([
20484
20487
  // src/services/warning-buffer.ts
20485
20488
  var deferredWarnings = [];
20486
20489
 
20490
+ // src/agents/council-prompts.ts
20491
+ var ROUND_PROTOCOL = `================================================================
20492
+ ROUND PROTOCOL
20493
+ ================================================================
20494
+
20495
+ ROUND 1 \u2014 Independent Analysis and Answer
20496
+ - Use the RESEARCH CONTEXT block provided by the architect in your dispatch message as your external evidence source. The architect has already gathered the relevant web search results.
20497
+ - Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).
20498
+ - State your confidence (0.0\u20131.0) explicitly. Be honest \u2014 overconfident answers hurt the council.
20499
+ - Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.
20500
+ - Do NOT coordinate with other members. You will not see their responses until Round 2.
20501
+ - Do NOT pad. Be concise. Substance over volume.
20502
+
20503
+ ROUND 2 \u2014 Targeted Deliberation (ONLY when this round is invoked for you)
20504
+ - The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.
20505
+ - Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.
20506
+ - Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:
20507
+ MAINTAIN \u2014 your Round 1 position holds; cite the evidence supporting it
20508
+ CONCEDE \u2014 the opposing position is correct; state specifically what you got wrong
20509
+ NUANCE \u2014 both positions are partially right; state the boundary condition that distinguishes them
20510
+ - Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).
20511
+ - Never MAINTAIN without engaging the opposing argument on its merits.`;
20512
+ var RESPONSE_FORMAT = `================================================================
20513
+ RESPONSE FORMAT (always \u2014 both rounds)
20514
+ ================================================================
20515
+
20516
+ Reply with a single fenced JSON block. No prose outside the block.
20517
+
20518
+ \`\`\`json
20519
+ {
20520
+ "memberId": "<your hardcoded memberId>",
20521
+ "role": "<your hardcoded role>",
20522
+ "round": 1,
20523
+ "response": "Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.",
20524
+ "searchQueries": [],
20525
+ "sources": [
20526
+ { "title": "...", "url": "...", "snippet": "...", "query": "..." }
20527
+ ],
20528
+ "confidence": 0.85,
20529
+ "areasOfUncertainty": [
20530
+ "What I'm not sure about, in plain language."
20531
+ ],
20532
+ "disagreementTopics": []
20533
+ }
20534
+ \`\`\`
20535
+
20536
+ Notes:
20537
+ - \`searchQueries\` is optional \u2014 list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.
20538
+ - \`sources\` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.
20539
+ - For Round 1: leave \`disagreementTopics\` as []. For Round 2: list the specific disagreement topics this response addresses.`;
20540
+ var HARD_RULES = `================================================================
20541
+ HARD RULES
20542
+ ================================================================
20543
+ - You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.
20544
+ - Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in \`areasOfUncertainty\`.
20545
+ - Never echo other members' responses verbatim. Paraphrase or quote with attribution.
20546
+ - Stay within your role and persona. The architect chose you for a specific perspective.`;
20547
+ var GENERALIST_COUNCIL_PROMPT = `You are the GENERALIST voice on a multi-model General Council.
20548
+
20549
+ You are the GENERALIST voice on this council. Your perspective is broad and synthesizing:
20550
+ - You reason from first principles and across disciplines.
20551
+ - You weigh competing considerations without domain bias.
20552
+ - You surface tensions between different valid approaches.
20553
+ - You are the integrating voice \u2014 you see what the specialists might miss by being too deep in their domain.
20554
+ Member ID: "council_generalist" | Role: "generalist"
20555
+
20556
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.
20557
+
20558
+ ${ROUND_PROTOCOL}
20559
+
20560
+ ${RESPONSE_FORMAT}
20561
+
20562
+ ${HARD_RULES}
20563
+ `;
20564
+ var SKEPTIC_COUNCIL_PROMPT = `You are the SKEPTIC voice on a multi-model General Council.
20565
+
20566
+ You are the SKEPTIC voice on this council. Your job is rigorous stress-testing:
20567
+ - You challenge assumptions the other members take for granted.
20568
+ - You look for weak points, edge cases, and unstated dependencies.
20569
+ - You are NOT contrarian for its own sake \u2014 your pushback must be evidence-grounded.
20570
+ - You make the council's final answer more robust by finding what could go wrong before the user does.
20571
+ Member ID: "council_skeptic" | Role: "skeptic"
20572
+
20573
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.
20574
+
20575
+ ${ROUND_PROTOCOL}
20576
+
20577
+ ${RESPONSE_FORMAT}
20578
+
20579
+ ${HARD_RULES}
20580
+ `;
20581
+ var DOMAIN_EXPERT_COUNCIL_PROMPT = `You are the DOMAIN EXPERT voice on a multi-model General Council.
20582
+
20583
+ You are the DOMAIN EXPERT voice on this council. Your perspective is technically precise:
20584
+ - You go deep where others stay broad.
20585
+ - You cite specific mechanisms, constraints, and implementation-level detail.
20586
+ - You surface edge cases and gotchas that only emerge at depth.
20587
+ - Your answers are concrete \u2014 no hand-waving, no vague recommendations.
20588
+ Member ID: "council_domain_expert" | Role: "domain_expert"
20589
+
20590
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.
20591
+
20592
+ ${ROUND_PROTOCOL}
20593
+
20594
+ ${RESPONSE_FORMAT}
20595
+
20596
+ ${HARD_RULES}
20597
+ `;
20598
+
20487
20599
  // src/agents/index.ts
20488
20600
  var warnedAgents = new Set;
20489
20601
 
@@ -35341,7 +35453,7 @@ async function handleCloseCommand(directory, args) {
35341
35453
  try {
35342
35454
  const evidenceDir = path12.join(swarmDir, "evidence");
35343
35455
  const evidenceEntries = await fs7.readdir(evidenceDir);
35344
- const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-"));
35456
+ const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
35345
35457
  for (const retroDir of retroDirs) {
35346
35458
  const evidencePath = path12.join(evidenceDir, retroDir, "evidence.json");
35347
35459
  try {
@@ -40556,6 +40668,13 @@ function parseGitRemoteUrl2(remoteUrl) {
40556
40668
  repo: sshMatch[2].replace(/\.git$/, "")
40557
40669
  };
40558
40670
  }
40671
+ const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
40672
+ if (pathMatch) {
40673
+ return {
40674
+ owner: pathMatch[1],
40675
+ repo: pathMatch[2].replace(/\.git$/, "")
40676
+ };
40677
+ }
40559
40678
  return null;
40560
40679
  }
40561
40680
  function handlePrReviewCommand(_directory, args) {
@@ -45946,9 +46065,9 @@ var COMMAND_REGISTRY = {
45946
46065
  },
45947
46066
  council: {
45948
46067
  handler: (ctx) => handleCouncilCommand(ctx.directory, ctx.args),
45949
- description: "Enter architect MODE: COUNCIL \u2014 multi-model deliberation [question] [--preset <name>] [--spec-review]",
45950
- args: "<question> [--preset <name>] [--spec-review]",
45951
- details: "Triggers the architect to convene a configurable General Council: each member independently web-searches, answers, and engages in one structured deliberation round on disagreements; an optional moderator pass synthesizes the final answer. --preset <name> selects a member group from council.general.presets. --spec-review switches to single-pass advisory mode for spec review. Requires council.general.enabled: true and a search API key in opencode-swarm.json."
46068
+ description: "Enter architect MODE: COUNCIL \u2014 multi-model deliberation [question] [--spec-review]",
46069
+ args: "<question> [--spec-review]",
46070
+ details: "Triggers the architect to convene a three-agent General Council: " + "Generalist (reviewer model), Skeptic (critic model), and Domain Expert (SME model). " + "The architect first runs 1\u20133 targeted web searches and passes a compiled RESEARCH CONTEXT " + "to all three agents before dispatching them in parallel. " + "Agents deliberate using the NSED peer-review protocol (Round 1 independent analysis, " + "Round 2 MAINTAIN/CONCEDE/NUANCE for disagreements). " + "The architect synthesizes the final answer directly from convene_general_council output. " + "--spec-review switches to single-pass advisory mode for spec review. " + "Requires council.general.enabled: true and a search API key in opencode-swarm.json."
45952
46071
  },
45953
46072
  "pr-review": {
45954
46073
  handler: async (ctx) => handlePrReviewCommand(ctx.directory, ctx.args),
@@ -46273,14 +46392,6 @@ async function install() {
46273
46392
  curator_phase: {
46274
46393
  model: "opencode/gpt-5-nano",
46275
46394
  fallback_models: ["opencode/big-pickle"]
46276
- },
46277
- council_member: {
46278
- model: "opencode/gpt-5-nano",
46279
- fallback_models: ["opencode/big-pickle"]
46280
- },
46281
- council_moderator: {
46282
- model: "opencode/gpt-5-nano",
46283
- fallback_models: ["opencode/big-pickle"]
46284
46395
  }
46285
46396
  },
46286
46397
  max_iterations: 5
@@ -162,9 +162,9 @@ export declare const COMMAND_REGISTRY: {
162
162
  };
163
163
  readonly council: {
164
164
  readonly handler: (ctx: CommandContext) => Promise<string>;
165
- readonly description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--preset <name>] [--spec-review]";
166
- readonly args: "<question> [--preset <name>] [--spec-review]";
167
- readonly details: "Triggers the architect to convene a configurable General Council: each member independently web-searches, answers, and engages in one structured deliberation round on disagreements; an optional moderator pass synthesizes the final answer. --preset <name> selects a member group from council.general.presets. --spec-review switches to single-pass advisory mode for spec review. Requires council.general.enabled: true and a search API key in opencode-swarm.json.";
165
+ readonly description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--spec-review]";
166
+ readonly args: "<question> [--spec-review]";
167
+ readonly details: string;
168
168
  };
169
169
  readonly 'pr-review': {
170
170
  readonly handler: (ctx: CommandContext) => Promise<string>;
@@ -2,8 +2,8 @@ import type { ToolName } from '../tools/tool-names';
2
2
  export declare const QA_AGENTS: readonly ["reviewer", "critic", "critic_oversight"];
3
3
  export declare const PIPELINE_AGENTS: readonly ["explorer", "coder", "test_engineer"];
4
4
  export declare const ORCHESTRATOR_NAME: "architect";
5
- export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "curator_init", "curator_phase", "council_member", "council_moderator", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
6
- export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "curator_init", "curator_phase", "council_member", "council_moderator", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
5
+ export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "curator_init", "curator_phase", "council_generalist", "council_skeptic", "council_domain_expert", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
6
+ export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "designer", "critic_sounding_board", "critic_drift_verifier", "critic_hallucination_verifier", "curator_init", "curator_phase", "council_generalist", "council_skeptic", "council_domain_expert", "reviewer", "critic", "critic_oversight", "explorer", "coder", "test_engineer"];
7
7
  export declare const OPENCODE_NATIVE_AGENTS: Set<"compaction" | "title" | "build" | "general" | "plan" | "explore" | "summary">;
8
8
  export type QAAgentName = (typeof QA_AGENTS)[number];
9
9
  export type PipelineAgentName = (typeof PIPELINE_AGENTS)[number];
@@ -20,7 +20,7 @@ import type { AgentSessionState } from '../state';
20
20
  import type { GeneralCouncilResult } from './general-council-types.js';
21
21
  /**
22
22
  * Push a GeneralCouncilResult into the architect's advisory queue. The body
23
- * is the synthesis markdown plus the moderator output when present.
23
+ * is the structural synthesis markdown returned by `convene_general_council`.
24
24
  *
25
25
  * Safe to call: missing session or empty advisory body silently skips.
26
26
  * Always idempotent at the architect-prompt level (no duplicate-suppression
@@ -2,10 +2,12 @@
2
2
  * General Council Mode — data contracts.
3
3
  *
4
4
  * Distinct from the Work Complete Council (`./types.ts`). The general council
5
- * is an advisory deliberation system: user-selected models each independently
6
- * web-search and answer a question, then optionally engage in a single
7
- * disagreement-targeted reconciliation round. A moderator agent synthesizes
8
- * the final user-facing answer.
5
+ * is an advisory deliberation system: a fixed three-agent council
6
+ * (council_generalist / council_skeptic / council_domain_expert) reviews a
7
+ * question using an architect-supplied RESEARCH CONTEXT block and a
8
+ * disagreement-targeted reconciliation round. The architect synthesizes the
9
+ * final user-facing answer directly using inline output rules — the
10
+ * dedicated council_moderator agent has been removed.
9
11
  *
10
12
  * No business logic, no I/O. Only types, interfaces, and defaults.
11
13
  */
@@ -59,10 +61,12 @@ export interface GeneralCouncilResult {
59
61
  persistingDisagreements: string[];
60
62
  allSources: WebSearchResult[];
61
63
  /**
62
- * Final moderator output (when council.general.moderator: true and a moderator
63
- * model is configured). Populated by `convene-general-council.ts` after the
64
- * architect delegates the moderator prompt to `council_moderator`. Undefined
65
- * when no moderator pass is configured.
64
+ * @deprecated The dedicated council_moderator agent has been removed; the
65
+ * architect now synthesizes the final answer directly using inline output
66
+ * rules. This field is never populated post-refactor and the consumer in
67
+ * `general-council-advisory.ts` guards on its presence so omitting it is
68
+ * safe. Field kept on the type for backward compatibility with persisted
69
+ * evidence files.
66
70
  */
67
71
  moderatorOutput?: string;
68
72
  timestamp: string;
@@ -70,10 +74,17 @@ export interface GeneralCouncilResult {
70
74
  /**
71
75
  * Config shape — matched in schema.ts via GeneralCouncilConfigSchema.
72
76
  *
73
- * `enabled` defaults to false (feature gate). The moderator pass requires
74
- * a configured `moderatorModel`; when set, the architect delegates the
75
- * moderator prompt produced by `convene_general_council` to the dedicated
76
- * `council_moderator` agent (no `web_search` access — synthesis only).
77
+ * `enabled` defaults to false (feature gate). The council is now a fixed
78
+ * three-agent set (generalist / skeptic / domain_expert) registered when
79
+ * `enabled` is true; their models come from the reviewer / critic / sme
80
+ * swarm config entries respectively.
81
+ *
82
+ * Several fields are retained for backward compatibility with existing
83
+ * `opencode-swarm.json` files but are NO LONGER USED at runtime. See the
84
+ * per-field deprecation notes below. The schema in `schema.ts` is `.strict()`
85
+ * so removing these fields would break validation for users with stale
86
+ * configs; instead, they are accepted and ignored, and a deferred warning
87
+ * is surfaced when the legacy moderator fields are set.
77
88
  */
78
89
  export interface GeneralCouncilConfig {
79
90
  enabled: boolean;
@@ -83,16 +94,32 @@ export interface GeneralCouncilConfig {
83
94
  * `BRAVE_SEARCH_API_KEY` env vars depending on `searchProvider`.
84
95
  */
85
96
  searchApiKey?: string;
97
+ /**
98
+ * @deprecated Member selection is hardcoded to the three council agents
99
+ * (generalist / skeptic / domain_expert). This field is retained for
100
+ * backward compatibility but is ignored at runtime.
101
+ */
86
102
  members: GeneralCouncilMemberConfig[];
87
- /** Named groups of members for `/swarm council --preset <name>`. */
103
+ /**
104
+ * @deprecated Preset-based member selection is no longer supported.
105
+ * Retained for backward compatibility; ignored at runtime.
106
+ */
88
107
  presets: Record<string, GeneralCouncilMemberConfig[]>;
89
- /** When true, after Round 1 the architect routes disagreements back to disputing members. */
108
+ /** When true, after Round 1 the architect routes disagreements back to disputing agents. */
90
109
  deliberate: boolean;
91
- /** When true, the architect delegates a moderator pass to `council_moderator` after synthesis. */
110
+ /**
111
+ * @deprecated The dedicated council_moderator agent has been removed; the
112
+ * architect synthesizes the final answer directly. Retained for backward
113
+ * compatibility; ignored at runtime. A deferred warning is surfaced when
114
+ * this field is set to silence the deprecation explicitly.
115
+ */
92
116
  moderator: boolean;
93
- /** Required when `moderator: true` — model identifier for the council_moderator delegation. */
117
+ /**
118
+ * @deprecated See `moderator` — no longer used. Retained for backward
119
+ * compatibility; ignored at runtime.
120
+ */
94
121
  moderatorModel?: string;
95
- /** Hard cap on results returned per member per search call (1–20). Defaults to 5. */
122
+ /** Hard cap on results returned per architect web_search call (1–20). Defaults to 5. */
96
123
  maxSourcesPerMember: number;
97
124
  }
98
125
  export declare const GENERAL_COUNCIL_DEFAULTS: GeneralCouncilConfig;
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.0.1",
36
+ version: "7.0.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",
@@ -245,8 +245,9 @@ var init_constants = __esm(() => {
245
245
  "critic_hallucination_verifier",
246
246
  "curator_init",
247
247
  "curator_phase",
248
- "council_member",
249
- "council_moderator",
248
+ "council_generalist",
249
+ "council_skeptic",
250
+ "council_domain_expert",
250
251
  ...QA_AGENTS,
251
252
  ...PIPELINE_AGENTS
252
253
  ];
@@ -319,7 +320,8 @@ var init_constants = __esm(() => {
319
320
  "repo_map",
320
321
  "get_qa_gate_profile",
321
322
  "set_qa_gates",
322
- "convene_general_council"
323
+ "convene_general_council",
324
+ "web_search"
323
325
  ],
324
326
  explorer: [
325
327
  "complexity_hotspots",
@@ -470,8 +472,9 @@ var init_constants = __esm(() => {
470
472
  ],
471
473
  curator_init: ["knowledge_recall"],
472
474
  curator_phase: ["knowledge_recall"],
473
- council_member: ["web_search"],
474
- council_moderator: []
475
+ council_generalist: [],
476
+ council_skeptic: [],
477
+ council_domain_expert: []
475
478
  };
476
479
  WRITE_TOOL_NAMES = [
477
480
  "write",
@@ -533,8 +536,8 @@ var init_constants = __esm(() => {
533
536
  gitingest: "fetch a GitHub repository full content via gitingest.com",
534
537
  retrieve_summary: "retrieve the full content of a stored tool output summary",
535
538
  search: "Workspace-scoped ripgrep-style text search with structured JSON output. Supports literal and regex modes, glob filtering, and result limits. NOTE: This is text search, not structural AST search — use symbols and imports tools for structural queries.",
536
- web_search: "External web search (Tavily or Brave) for General Council member agents. Returns titled results with snippets and URLs. Restricted to council_member agents via AGENT_TOOL_MAP. Config-gated on council.general.enabled; requires a search API key.",
537
- convene_general_council: "Synthesize responses from a multi-model General Council. Accepts parallel member responses (Round 1, optionally Round 2), detects disagreements, and returns consensus points, persisting disagreements, a structured synthesis, and an optional moderator prompt. Architect-only. Config-gated on council.general.enabled.",
539
+ web_search: "External web search (Tavily or Brave) for architect-driven council research. Returns titled results with snippets and URLs. Config-gated on council.general.enabled; requires a search API key. Used by the architect in MODE: COUNCIL to gather a RESEARCH CONTEXT before dispatching council agents.",
540
+ convene_general_council: "Synthesize responses from a multi-model General Council. Accepts parallel member responses (Round 1, optionally Round 2), detects disagreements, and returns consensus points, persisting disagreements, and a structured synthesis. Architect-only. Config-gated on council.general.enabled.",
538
541
  batch_symbols: "Batched symbol extraction across multiple files. Returns per-file symbol summaries with isolated error handling.",
539
542
  suggest_patch: "Reviewer-safe structured patch suggestion tool. Produces context-anchored patch artifacts without file modification. Returns structured diagnostics on context mismatch.",
540
543
  lint_spec: "validate .swarm/spec.md format and required fields",
@@ -565,8 +568,6 @@ var init_constants = __esm(() => {
565
568
  designer: "opencode/big-pickle",
566
569
  curator_init: "opencode/big-pickle",
567
570
  curator_phase: "opencode/big-pickle",
568
- council_member: "opencode/big-pickle",
569
- council_moderator: "opencode/big-pickle",
570
571
  default: "opencode/big-pickle"
571
572
  };
572
573
  DEFAULT_SCORING_CONFIG = {
@@ -41966,7 +41967,7 @@ async function handleCloseCommand(directory, args2) {
41966
41967
  try {
41967
41968
  const evidenceDir = path18.join(swarmDir, "evidence");
41968
41969
  const evidenceEntries = await fs12.readdir(evidenceDir);
41969
- const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-"));
41970
+ const retroDirs = evidenceEntries.filter((e) => e.startsWith("retro-")).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
41970
41971
  for (const retroDir of retroDirs) {
41971
41972
  const evidencePath = path18.join(evidenceDir, retroDir, "evidence.json");
41972
41973
  try {
@@ -49263,6 +49264,13 @@ function parseGitRemoteUrl2(remoteUrl) {
49263
49264
  repo: sshMatch[2].replace(/\.git$/, "")
49264
49265
  };
49265
49266
  }
49267
+ const pathMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/);
49268
+ if (pathMatch) {
49269
+ return {
49270
+ owner: pathMatch[1],
49271
+ repo: pathMatch[2].replace(/\.git$/, "")
49272
+ };
49273
+ }
49266
49274
  return null;
49267
49275
  }
49268
49276
  function handlePrReviewCommand(_directory, args2) {
@@ -55117,9 +55125,9 @@ var init_registry = __esm(() => {
55117
55125
  },
55118
55126
  council: {
55119
55127
  handler: (ctx) => handleCouncilCommand(ctx.directory, ctx.args),
55120
- description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--preset <name>] [--spec-review]",
55121
- args: "<question> [--preset <name>] [--spec-review]",
55122
- details: "Triggers the architect to convene a configurable General Council: each member independently web-searches, answers, and engages in one structured deliberation round on disagreements; an optional moderator pass synthesizes the final answer. --preset <name> selects a member group from council.general.presets. --spec-review switches to single-pass advisory mode for spec review. Requires council.general.enabled: true and a search API key in opencode-swarm.json."
55128
+ description: "Enter architect MODE: COUNCIL — multi-model deliberation [question] [--spec-review]",
55129
+ args: "<question> [--spec-review]",
55130
+ details: "Triggers the architect to convene a three-agent General Council: " + "Generalist (reviewer model), Skeptic (critic model), and Domain Expert (SME model). " + "The architect first runs 1–3 targeted web searches and passes a compiled RESEARCH CONTEXT " + "to all three agents before dispatching them in parallel. " + "Agents deliberate using the NSED peer-review protocol (Round 1 independent analysis, " + "Round 2 MAINTAIN/CONCEDE/NUANCE for disagreements). " + "The architect synthesizes the final answer directly from convene_general_council output. " + "--spec-review switches to single-pass advisory mode for spec review. " + "Requires council.general.enabled: true and a search API key in opencode-swarm.json."
55123
55131
  },
55124
55132
  "pr-review": {
55125
55133
  handler: async (ctx) => handlePrReviewCommand(ctx.directory, ctx.args),
@@ -55362,7 +55370,7 @@ Present the ten gates with their defaults (DEFAULT_QA_GATES) as a single user-fa
55362
55370
  - council_mode (default: OFF) — multi-member council gate (recommended for high-impact architecture, public APIs, schema/data mutation, security-sensitive code)
55363
55371
  - hallucination_guard (default: OFF) — when enabled, mandatory per-phase API/signature/claim/citation verification via critic_hallucination_verifier at PHASE-WRAP; phase_complete will REJECT phase completion unless .swarm/evidence/{phase}/hallucination-guard.json exists with an APPROVED verdict (recommended for claim-heavy or research-heavy work)
55364
55372
  - 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)
55365
- - council_general_review (default: OFF) — when enabled, MODE: SPECIFY runs convene_general_council on the draft spec before the critic-gate; multiple models each independently search the web, deliberate on disagreements, and a moderator synthesizes a final answer that the architect folds 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.
55373
+ - 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.
55366
55374
  - 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)
55367
55375
 
55368
55376
  One question, one message, defaults pre-stated. Wait for the user's answer.`;
@@ -56197,13 +56205,13 @@ Read the elected QA gates (parse the \`## Pending QA Gate Selection\` section fr
56197
56205
 
56198
56206
  If \`council_general_review\` is true:
56199
56207
  1. Read \`council.general\` config. If \`council.general.enabled\` is not true OR no search API key is configured, surface to the user: "council_general_review gate is enabled but the General Council is not configured. Set council.general.enabled: true and configure a search API key in opencode-swarm.json, or unset council_general_review and re-run." Then stop.
56200
- 2. Determine the council members from \`council.general.members\` (or \`council.general.presets[<name>]\` if you were invoked via \`/swarm council --preset <name>\` originally).
56201
- 3. Delegate to each council member in PARALLEL — one message per member, then STOP and wait. Pass: the spec text as the question, the member's role/persona, round number 1. Do NOT share other members' perspectives at this stage.
56202
- 4. Collect all member JSON responses.
56208
+ 2. Run the Research Phase: formulate 1–3 targeted \`web_search\` queries grounded in the spec's domain, then compile a RESEARCH CONTEXT block (same format as MODE: COUNCIL step 2). If web_search fails, proceed without a context block.
56209
+ 3. Dispatch \`{{AGENT_PREFIX}}council_generalist\`, \`{{AGENT_PREFIX}}council_skeptic\`, and \`{{AGENT_PREFIX}}council_domain_expert\` in PARALLEL — one message per agent, then STOP and wait. Pass: the spec text as the question, round number 1, the RESEARCH CONTEXT block, and the instruction "Cite from the RESEARCH CONTEXT for external evidence. Your memberId and role are hardcoded in your system prompt." Do NOT share other agents' perspectives at this stage.
56210
+ 4. Collect all three JSON responses.
56203
56211
  5. Call \`convene_general_council\` with mode: 'spec_review', the spec as question, and the collected \`round1Responses\`. Omit \`round2Responses\` — spec review is a single-pass advisory, not a full deliberation.
56204
56212
  6. Read \`consensusPoints\` — incorporate unambiguous consensus directly into the spec.
56205
56213
  7. Read \`disagreements\` — for each: (a) accept one position with rationale, (b) mark as \`[NEEDS CLARIFICATION]\` in the spec, or (c) schedule an SME consultation.
56206
- 8. If \`council.general.moderator\` is true, the tool returned a \`moderatorPrompt\` field. Delegate this prompt to \`{{AGENT_PREFIX}}council_moderator\`. Use the moderator's output to refine the spec further.
56214
+ 8. Synthesize the final spec-review answer directly from the \`synthesis\` returned by \`convene_general_council\`. Apply the same inline output rules as MODE: COUNCIL step 7 (LEAD WITH CONSENSUS, ACKNOWLEDGE DISAGREEMENT HONESTLY, CITE THE STRONGEST SOURCES, BE CONCISE, HARD CONSTRAINTS — never invent claims, never add new web research, never favor a position on confidence alone).
56207
56215
  9. Revise \`.swarm/spec.md\` to reflect the council input.
56208
56216
 
56209
56217
  <!-- BEHAVIORAL_GUIDANCE_START -->
@@ -56214,8 +56222,8 @@ SPECIFY-COUNCIL-REVIEW RULES:
56214
56222
  → WRONG when gate is true: the user enabled this gate for a reason. Run it regardless.
56215
56223
  ✗ "I'll include round2Responses for spec_review — more is better"
56216
56224
  → WRONG: spec review is a single advisory pass. Omit \`round2Responses\` for spec_review mode.
56217
- ✗ "I'll skip the moderator pass to save time"
56218
- → WRONG when council.general.moderator is true: invoke \`{{AGENT_PREFIX}}council_moderator\` with the moderatorPrompt the tool returns.
56225
+ ✗ "I'll skip the Research Phase to save time"
56226
+ → WRONG: the council agents have no tools and depend on the architect-supplied RESEARCH CONTEXT for external evidence. Skipping the pre-search degrades every downstream agent's grounding.
56219
56227
  <!-- BEHAVIORAL_GUIDANCE_END -->
56220
56228
 
56221
56229
  7. Report a summary to the user (MUST count, SHALL count, scenario count, clarification markers, elected QA gates) and suggest the next step: \`CLARIFY-SPEC\` (if markers exist) or \`PLAN\`.
@@ -56388,36 +56396,54 @@ GREENFIELD EXEMPTION: If the work is purely greenfield (new project, no existing
56388
56396
 
56389
56397
  ### MODE: COUNCIL
56390
56398
 
56391
- Activates when: user invokes \`/swarm council <question>\` (optionally with \`--preset <name>\` or \`--spec-review\`).
56399
+ Activates when: user invokes \`/swarm council <question>\` (optionally with \`--spec-review\`).
56392
56400
 
56393
- Purpose: convene a configurable multi-model General Council for an advisory deliberation. Each member independently web-searches and answers; the architect routes any disagreements back for one targeted reconciliation round; an optional moderator pass synthesizes the final user-facing answer.
56401
+ Purpose: convene a fixed three-agent multi-model General Council (generalist / skeptic / domain expert) for an advisory deliberation. The architect runs a curated web research pass upfront, dispatches the three agents in parallel with the gathered RESEARCH CONTEXT, routes any disagreements back for one targeted reconciliation round, and synthesizes the final user-facing answer directly.
56394
56402
 
56395
56403
  This mode is ADVISORY — it does NOT block any other workflow and does NOT modify code, plans, or specs. The output is for the user (general mode) or for the spec being drafted in MODE: SPECIFY (spec_review mode, gated by \`council_general_review\`).
56396
56404
 
56397
56405
  #### Pre-flight (always run first)
56398
56406
  1. Read \`council.general\` config. If \`council.general.enabled\` is not true OR no search API key is configured (neither \`council.general.searchApiKey\` nor the corresponding env var \`TAVILY_API_KEY\` / \`BRAVE_SEARCH_API_KEY\`), surface to the user: "General Council is not enabled. Set council.general.enabled: true and configure a search API key in opencode-swarm.json." Then STOP.
56399
56407
 
56400
- #### Round 1Parallel Independent Search
56401
- 2. Determine council members. Default: \`council.general.members\`. If invoked with \`--preset <name>\`: \`council.general.presets[<name>]\`. If a named preset is missing, surface a clear error and stop.
56402
- 3. Delegate to each council member in PARALLEL — one message per member, then STOP and wait for all responses to come back. Pass: the question, the member's role/persona, round number 1. Do NOT share other members' responses at this stage.
56403
- 4. Collect all member JSON responses (each member returns a fenced JSON block per the council_member prompt).
56408
+ #### Research Phase (always run before dispatching council agents)
56409
+ 2. Formulate 1–3 targeted \`web_search\` queries that best capture the information needed to answer the question. Prefer specific, keyword-focused queries over broad ones. Call \`web_search\` for each query. Compile all results into a RESEARCH CONTEXT block in this format:
56410
+ \`\`\`
56411
+ RESEARCH CONTEXT
56412
+ ================
56413
+ [1] <title> — <url>
56414
+ <snippet>
56415
+
56416
+ [2] <title> — <url>
56417
+ <snippet>
56418
+ ...
56419
+ \`\`\`
56420
+ If \`web_search\` returns no results or an error (check \`result.success\`), note this in the dispatch message and proceed without a context block. Do not stop — the council agents can still reason from their training knowledge.
56421
+
56422
+ #### Round 1 — Parallel Independent Analysis
56423
+ 3. Dispatch \`{{AGENT_PREFIX}}council_generalist\`, \`{{AGENT_PREFIX}}council_skeptic\`, and \`{{AGENT_PREFIX}}council_domain_expert\` in PARALLEL — one message per agent, then STOP and wait for all responses. Each dispatch message must include:
56424
+ - The question
56425
+ - Round number: 1
56426
+ - The full RESEARCH CONTEXT block from step 2
56427
+ - Instruction: "Cite from the RESEARCH CONTEXT for external evidence. Your memberId and role are hardcoded in your system prompt."
56428
+ Do NOT share other agents' responses at this stage.
56429
+ 4. Collect all three JSON responses. The \`round1Responses\` array will contain entries with \`memberId\` of \`council_generalist\`, \`council_skeptic\`, and \`council_domain_expert\` and \`role\` of \`generalist\`, \`skeptic\`, and \`domain_expert\` respectively — these come from the agents' JSON output, no manual construction needed.
56404
56430
 
56405
56431
  #### Synthesis and Deliberation (when council.general.deliberate is true; default true)
56406
56432
  5. Call \`convene_general_council\` with mode set from the command (\`general\` or \`spec_review\`), \`question\`, and the collected \`round1Responses\` only (omit \`round2Responses\`). Inspect the returned \`disagreementsCount\`.
56407
56433
  6. If \`disagreementsCount > 0\`:
56408
- a. For each disagreement in the tool's response, identify the disputing members (the members listed in the disagreement's positions).
56409
- b. Re-delegate ONLY to the disputing members — one message per member — passing: their Round 1 response, the disagreement topic, the opposing position(s), round number 2.
56434
+ a. For each disagreement in the tool's response, identify the disputing agents (the agents listed in the disagreement's positions, identified by memberId: \`council_generalist\`, \`council_skeptic\`, or \`council_domain_expert\`).
56435
+ b. Re-delegate ONLY to the disputing agents — one message per agent — passing: their Round 1 response, the disagreement topic, the opposing position(s), round number 2, and the same RESEARCH CONTEXT block.
56410
56436
  c. Collect the Round 2 responses.
56411
56437
  d. Call \`convene_general_council\` AGAIN with both \`round1Responses\` AND \`round2Responses\` populated.
56412
56438
 
56413
- #### Moderator Pass (when council.general.moderator is true; default true)
56414
- 7. The most recent \`convene_general_council\` call returned a \`moderatorPrompt\` field. Delegate this prompt to \`{{AGENT_PREFIX}}council_moderator\`. The moderator agent has no tools and no web access — it synthesizes a final user-facing answer from the council output you give it. Collect the moderator's markdown output.
56415
-
56416
56439
  #### Output
56417
- 8. Present the final answer to the user:
56418
- - If the moderator pass ran: present the moderator's output verbatim, prefaced with the participating models (one line).
56419
- - If no moderator: present the structural \`synthesis\` markdown from the tool's return.
56420
- In either case, do NOT present the raw per-member JSON. Do NOT silently pick a winner among persisting disagreements surface them honestly.
56440
+ 7. Present the final answer to the user from the \`synthesis\` returned by \`convene_general_council\`. Apply these output rules directly:
56441
+ - LEAD WITH CONSENSUS: open with the strongest consensus position. Confidence-weighted: higher-confidence claims from multiple agents rank first, but evidence quality outranks raw confidence. Never elevate a single confident voice over a well-evidenced contrary majority.
56442
+ - ACKNOWLEDGE DISAGREEMENT HONESTLY: for each persisting disagreement, write "experts disagree on X because…" and present the strongest version of each side. Do NOT pretend disagreements are resolved. Do NOT silently pick a winner.
56443
+ - CITE THE STRONGEST SOURCES: link key claims with [title](url) format from the source list in the synthesis. Pick the most reputable source per claim; do not cite duplicates.
56444
+ - BE CONCISE: a few short paragraphs plus a bulleted summary. Expand only when the question genuinely requires it.
56445
+ - HARD CONSTRAINTS: You MUST NOT invent claims not present in the council's responses. You MUST NOT add new web research. You MUST NOT favor a position based on confidence alone.
56446
+ Preface the answer with one line listing the participating models (reviewer model as generalist, critic model as skeptic, SME model as domain expert). Do NOT present raw per-member JSON.
56421
56447
 
56422
56448
  ### MODE: ISSUE_INGEST
56423
56449
  Activates when: user invokes \`/swarm issue <url>\`; OR architect receives \`[MODE: ISSUE_INGEST issue="<url>"]\` signal.
@@ -57152,60 +57178,28 @@ META.SUMMARY CONVENTION — When reporting task completion, include:
57152
57178
 
57153
57179
  `;
57154
57180
 
57155
- // src/agents/council-member.ts
57156
- function createCouncilMemberAgent(model, customPrompt, customAppendPrompt) {
57157
- let prompt = COUNCIL_MEMBER_PROMPT;
57158
- if (customPrompt) {
57159
- prompt = customPrompt;
57160
- } else if (customAppendPrompt) {
57161
- prompt = `${COUNCIL_MEMBER_PROMPT}
57162
-
57163
- ${customAppendPrompt}`;
57164
- }
57165
- return {
57166
- name: "council_member",
57167
- description: "General Council deliberation member. Independently web-searches and answers in Round 1; " + "targeted MAINTAIN/CONCEDE/NUANCE deliberation in Round 2. Tool-restricted to web_search only.",
57168
- config: {
57169
- model,
57170
- temperature: 0.4,
57171
- prompt,
57172
- tools: {
57173
- write: false,
57174
- edit: false,
57175
- patch: false
57176
- }
57177
- }
57178
- };
57179
- }
57180
- var COUNCIL_MEMBER_PROMPT = `You are Council Member {{MEMBER_ID}} ({{ROLE}}) on a multi-model General Council.
57181
-
57182
- {{PERSONA_BLOCK}}
57183
-
57184
- You are participating in Round {{ROUND}} of a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57185
-
57186
- ================================================================
57187
- ROUND {{ROUND}} PROTOCOL
57181
+ // src/agents/council-prompts.ts
57182
+ var ROUND_PROTOCOL = `================================================================
57183
+ ROUND PROTOCOL
57188
57184
  ================================================================
57189
57185
 
57190
- ROUND 1 — Independent Research and Answer
57191
- - Issue 1–3 targeted web_search calls to gather evidence relevant to the question.
57192
- - Cite EVERY factual claim with a source URL from your search results.
57186
+ ROUND 1 — Independent Analysis and Answer
57187
+ - Use the RESEARCH CONTEXT block provided by the architect in your dispatch message as your external evidence source. The architect has already gathered the relevant web search results.
57188
+ - Cite EVERY factual claim that depends on external evidence with a source from the RESEARCH CONTEXT (use the title and URL exactly as given).
57193
57189
  - State your confidence (0.0–1.0) explicitly. Be honest — overconfident answers hurt the council.
57194
57190
  - Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.
57195
57191
  - Do NOT coordinate with other members. You will not see their responses until Round 2.
57196
57192
  - Do NOT pad. Be concise. Substance over volume.
57197
57193
 
57198
57194
  ROUND 2 — Targeted Deliberation (ONLY when this round is invoked for you)
57199
- - {{DISAGREEMENT_BLOCK}}
57200
- - Issue at most 1 additional web_search call.
57195
+ - The architect will pass you the disagreement topic and the opposing position(s) in the dispatch message.
57196
+ - Re-read the RESEARCH CONTEXT for any evidence relevant to the disagreement.
57201
57197
  - Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:
57202
- MAINTAIN — your Round 1 position holds; cite the new evidence supporting it
57198
+ MAINTAIN — your Round 1 position holds; cite the evidence supporting it
57203
57199
  CONCEDE — the opposing position is correct; state specifically what you got wrong
57204
57200
  NUANCE — both positions are partially right; state the boundary condition that distinguishes them
57205
57201
  - Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).
57206
- - Never MAINTAIN without engaging the opposing argument on its merits.
57207
-
57208
- ================================================================
57202
+ - Never MAINTAIN without engaging the opposing argument on its merits.`, RESPONSE_FORMAT = `================================================================
57209
57203
  RESPONSE FORMAT (always — both rounds)
57210
57204
  ================================================================
57211
57205
 
@@ -57213,11 +57207,11 @@ Reply with a single fenced JSON block. No prose outside the block.
57213
57207
 
57214
57208
  \`\`\`json
57215
57209
  {
57216
- "memberId": "{{MEMBER_ID}}",
57217
- "role": "{{ROLE}}",
57218
- "round": {{ROUND}},
57210
+ "memberId": "<your hardcoded memberId>",
57211
+ "role": "<your hardcoded role>",
57212
+ "round": 1,
57219
57213
  "response": "Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.",
57220
- "searchQueries": ["query 1", "query 2"],
57214
+ "searchQueries": [],
57221
57215
  "sources": [
57222
57216
  { "title": "...", "url": "...", "snippet": "...", "query": "..." }
57223
57217
  ],
@@ -57229,111 +57223,69 @@ Reply with a single fenced JSON block. No prose outside the block.
57229
57223
  }
57230
57224
  \`\`\`
57231
57225
 
57232
- For Round 1: leave \`disagreementTopics\` as []. For Round 2: list the specific disagreement topics this response addresses.
57233
-
57234
- ================================================================
57226
+ Notes:
57227
+ - \`searchQueries\` is optional — list queries you would have run if you had web access (the architect uses these for audit), or omit / leave empty if none.
57228
+ - \`sources\` MUST come from the RESEARCH CONTEXT only. Copy title/url/snippet/query verbatim. Never invent sources.
57229
+ - For Round 1: leave \`disagreementTopics\` as []. For Round 2: list the specific disagreement topics this response addresses.`, HARD_RULES = `================================================================
57235
57230
  HARD RULES
57236
57231
  ================================================================
57237
- - web_search is your ONLY tool. You cannot read or write files, run commands, or delegate.
57238
- - Never invent sources. If a search returns nothing useful, say so in \`areasOfUncertainty\`.
57232
+ - You have no tools. Reason from the provided RESEARCH CONTEXT and your training knowledge.
57233
+ - Never invent sources. If the RESEARCH CONTEXT does not cover a needed claim, say so in \`areasOfUncertainty\`.
57239
57234
  - Never echo other members' responses verbatim. Paraphrase or quote with attribution.
57240
- - Stay within your role and persona. The architect chose you for a specific perspective.
57241
- `;
57235
+ - Stay within your role and persona. The architect chose you for a specific perspective.`, GENERALIST_COUNCIL_PROMPT, SKEPTIC_COUNCIL_PROMPT, DOMAIN_EXPERT_COUNCIL_PROMPT;
57236
+ var init_council_prompts = __esm(() => {
57237
+ GENERALIST_COUNCIL_PROMPT = `You are the GENERALIST voice on a multi-model General Council.
57242
57238
 
57243
- // src/agents/council-moderator.ts
57244
- function createCouncilModeratorAgent(model, customPrompt, customAppendPrompt) {
57245
- let prompt = COUNCIL_MODERATOR_PROMPT;
57246
- if (customPrompt) {
57247
- prompt = customPrompt;
57248
- } else if (customAppendPrompt) {
57249
- prompt = `${COUNCIL_MODERATOR_PROMPT}
57239
+ You are the GENERALIST voice on this council. Your perspective is broad and synthesizing:
57240
+ - You reason from first principles and across disciplines.
57241
+ - You weigh competing considerations without domain bias.
57242
+ - You surface tensions between different valid approaches.
57243
+ - You are the integrating voice — you see what the specialists might miss by being too deep in their domain.
57244
+ Member ID: "council_generalist" | Role: "generalist"
57250
57245
 
57251
- ${customAppendPrompt}`;
57252
- }
57253
- return {
57254
- name: "council_moderator",
57255
- description: "General Council moderator. Synthesizes a coherent final answer from member " + "responses; no web search (works on already-gathered content).",
57256
- config: {
57257
- model,
57258
- temperature: 0.3,
57259
- prompt,
57260
- tools: {
57261
- write: false,
57262
- edit: false,
57263
- patch: false
57264
- }
57265
- }
57266
- };
57267
- }
57268
- var COUNCIL_MODERATOR_PROMPT = `You are the General Council Moderator.
57269
-
57270
- You are receiving the structural synthesis from a multi-model council deliberation:
57271
- - Question (and mode: general or spec_review)
57272
- - All member Round 1 responses with sources
57273
- - Detected disagreements
57274
- - Round 2 deliberation responses (if any)
57275
- - Confidence-weighted consensus claims
57276
- - Persisting disagreements after deliberation
57277
-
57278
- Your job: produce a coherent, well-structured final answer for the user.
57279
-
57280
- ================================================================
57281
- RULES
57282
- ================================================================
57283
-
57284
- 1. LEAD WITH CONSENSUS — open with the strongest consensus position. Use the
57285
- confidence-weighted ordering (Quadratic Voting): higher-confidence claims
57286
- from multiple members rank higher, but evidence quality outranks raw
57287
- confidence. Never elevate a single confident voice over a well-evidenced
57288
- contrary majority.
57289
-
57290
- 2. ACKNOWLEDGE DISAGREEMENT HONESTLY — for each persisting disagreement, write
57291
- "experts disagree on X because…" and present the strongest version of each
57292
- side. Do NOT pretend disagreements are resolved when they are not. Do NOT
57293
- silently pick a winner.
57246
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57294
57247
 
57295
- 3. CITE THE STRONGEST SOURCES — link key claims with [title](url) format from
57296
- the deduplicated source list. Pick the most reputable source for each claim;
57297
- do not cite duplicates.
57248
+ ${ROUND_PROTOCOL}
57298
57249
 
57299
- 4. BE CONCISE — the user wants an answer, not a committee report. Default
57300
- length: a few short paragraphs plus a bulleted summary. Expand only when
57301
- the question genuinely requires it.
57302
-
57303
- ================================================================
57304
- HARD CONSTRAINTS
57305
- ================================================================
57250
+ ${RESPONSE_FORMAT}
57306
57251
 
57307
- - You MUST NOT invent claims that are not present in the council's responses.
57308
- - You MUST NOT add new web research. If something was missed, say so.
57309
- - You MUST NOT favor a position based on member confidence alone — evidence
57310
- quality is the tie-breaker.
57311
- - You have NO tools. You write the final synthesis from the input given.
57252
+ ${HARD_RULES}
57253
+ `;
57254
+ SKEPTIC_COUNCIL_PROMPT = `You are the SKEPTIC voice on a multi-model General Council.
57312
57255
 
57313
- ================================================================
57314
- OUTPUT FORMAT
57315
- ================================================================
57256
+ You are the SKEPTIC voice on this council. Your job is rigorous stress-testing:
57257
+ - You challenge assumptions the other members take for granted.
57258
+ - You look for weak points, edge cases, and unstated dependencies.
57259
+ - You are NOT contrarian for its own sake — your pushback must be evidence-grounded.
57260
+ - You make the council's final answer more robust by finding what could go wrong before the user does.
57261
+ Member ID: "council_skeptic" | Role: "skeptic"
57316
57262
 
57317
- Plain markdown. No code fences. No JSON. Suggested structure:
57263
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57318
57264
 
57319
- # Answer
57265
+ ${ROUND_PROTOCOL}
57320
57266
 
57321
- <lead consensus position with citation(s)>
57267
+ ${RESPONSE_FORMAT}
57322
57268
 
57323
- <remaining consensus / context paragraphs as needed>
57269
+ ${HARD_RULES}
57270
+ `;
57271
+ DOMAIN_EXPERT_COUNCIL_PROMPT = `You are the DOMAIN EXPERT voice on a multi-model General Council.
57324
57272
 
57325
- ## Where Experts Disagree
57273
+ You are the DOMAIN EXPERT voice on this council. Your perspective is technically precise:
57274
+ - You go deep where others stay broad.
57275
+ - You cite specific mechanisms, constraints, and implementation-level detail.
57276
+ - You surface edge cases and gotchas that only emerge at depth.
57277
+ - Your answers are concrete — no hand-waving, no vague recommendations.
57278
+ Member ID: "council_domain_expert" | Role: "domain_expert"
57326
57279
 
57327
- - <topic 1>: <position A> vs <position B>, with sources for each
57328
- - <topic 2>: ...
57280
+ You are participating in a structured deliberation. Your job is to give your independent, evidence-grounded perspective — not to agree with the group.
57329
57281
 
57330
- ## Sources
57282
+ ${ROUND_PROTOCOL}
57331
57283
 
57332
- - [title](url)
57333
- - ...
57284
+ ${RESPONSE_FORMAT}
57334
57285
 
57335
- (Omit any section that is empty.)
57286
+ ${HARD_RULES}
57336
57287
  `;
57288
+ });
57337
57289
 
57338
57290
  // src/agents/critic.ts
57339
57291
  function parseSoundingBoardResponse(raw) {
@@ -59008,18 +58960,25 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
59008
58960
  testEngineer.name = prefixName("test_engineer");
59009
58961
  agents.push(applyOverrides(testEngineer, swarmAgents, swarmPrefix, quiet));
59010
58962
  }
59011
- if (pluginConfig?.council?.general?.enabled === true && !isAgentDisabled("council_member", swarmAgents, swarmPrefix)) {
59012
- const councilMemberPrompts = getPrompts("council_member");
59013
- const councilMember = createCouncilMemberAgent(getModel("council_member"), councilMemberPrompts.prompt, councilMemberPrompts.appendPrompt);
59014
- councilMember.name = prefixName("council_member");
59015
- agents.push(applyOverrides(councilMember, swarmAgents, swarmPrefix, quiet));
59016
- }
59017
- if (pluginConfig?.council?.general?.enabled === true && pluginConfig?.council?.general?.moderator === true && !isAgentDisabled("council_moderator", swarmAgents, swarmPrefix)) {
59018
- const moderatorPrompts = getPrompts("council_moderator");
59019
- const moderatorModel = pluginConfig?.council?.general?.moderatorModel ?? getModel("council_moderator");
59020
- const councilModerator = createCouncilModeratorAgent(moderatorModel, moderatorPrompts.prompt, moderatorPrompts.appendPrompt);
59021
- councilModerator.name = prefixName("council_moderator");
59022
- agents.push(applyOverrides(councilModerator, swarmAgents, swarmPrefix, quiet));
58963
+ if (pluginConfig?.council?.general?.enabled === true) {
58964
+ if (!isAgentDisabled("reviewer", swarmAgents, swarmPrefix)) {
58965
+ const councilGeneralist = createReviewerAgent(getModel("reviewer"), GENERALIST_COUNCIL_PROMPT);
58966
+ councilGeneralist.name = prefixName("council_generalist");
58967
+ agents.push(applyOverrides(councilGeneralist, swarmAgents, swarmPrefix, quiet));
58968
+ }
58969
+ if (!isAgentDisabled("critic", swarmAgents, swarmPrefix)) {
58970
+ const councilSkeptic = createCriticAgent(getModel("critic"), SKEPTIC_COUNCIL_PROMPT);
58971
+ councilSkeptic.name = prefixName("council_skeptic");
58972
+ agents.push(applyOverrides(councilSkeptic, swarmAgents, swarmPrefix, quiet));
58973
+ }
58974
+ if (!isAgentDisabled("sme", swarmAgents, swarmPrefix)) {
58975
+ const councilDomainExpert = createSMEAgent(getModel("sme"), DOMAIN_EXPERT_COUNCIL_PROMPT);
58976
+ councilDomainExpert.name = prefixName("council_domain_expert");
58977
+ agents.push(applyOverrides(councilDomainExpert, swarmAgents, swarmPrefix, quiet));
58978
+ }
58979
+ if (pluginConfig?.council?.general?.moderatorModel !== undefined) {
58980
+ addDeferredWarning("[opencode-swarm] council.general.moderatorModel is deprecated and ignored. The architect now synthesizes the final answer directly using inline output rules. Remove this field (and council.general.moderator if set) from opencode-swarm.json to silence this warning.");
58981
+ }
59023
58982
  }
59024
58983
  if (!isAgentDisabled("docs", swarmAgents, swarmPrefix)) {
59025
58984
  const docsPrompts = getPrompts("docs");
@@ -59155,9 +59114,11 @@ var init_agents2 = __esm(() => {
59155
59114
  init_schema();
59156
59115
  init_warning_buffer();
59157
59116
  init_architect();
59117
+ init_council_prompts();
59158
59118
  init_curator_agent();
59159
59119
  init_reviewer();
59160
59120
  init_architect();
59121
+ init_council_prompts();
59161
59122
  init_curator_agent();
59162
59123
  init_reviewer();
59163
59124
  warnedAgents = new Set;
@@ -75771,12 +75732,7 @@ function pushGeneralCouncilAdvisory(session, result) {
75771
75732
  ${body2}`);
75772
75733
  }
75773
75734
  function renderAdvisoryBody(result) {
75774
- const parts2 = [result.synthesis];
75775
- if (result.moderatorOutput && result.moderatorOutput.trim().length > 0) {
75776
- parts2.push("", "### Moderator Output", result.moderatorOutput);
75777
- }
75778
- return parts2.join(`
75779
- `).trim();
75735
+ return result.synthesis.trim();
75780
75736
  }
75781
75737
 
75782
75738
  // src/council/disagreement-detector.ts
@@ -76102,23 +76058,8 @@ var ArgsSchema2 = exports_external.object({
76102
76058
  round2Responses: exports_external.array(Round2ResponseSchema).optional(),
76103
76059
  working_directory: exports_external.string().optional()
76104
76060
  });
76105
- function buildModeratorPrompt(question, synthesis) {
76106
- return [
76107
- "A multi-model council has deliberated on the following question. Your job is to synthesize",
76108
- "the council output into a single coherent answer for the user, following the rules in your",
76109
- "system prompt (lead with consensus, acknowledge persisting disagreement honestly, cite the",
76110
- "strongest sources, be concise, do not invent claims, do not run new searches).",
76111
- "",
76112
- `QUESTION:
76113
- ${question}`,
76114
- "",
76115
- "COUNCIL OUTPUT:",
76116
- synthesis
76117
- ].join(`
76118
- `);
76119
- }
76120
76061
  var convene_general_council = createSwarmTool({
76121
- description: "Synthesize responses from a multi-model General Council. Accepts parallel member " + "responses (Round 1, optionally Round 2), detects disagreements, and returns " + "consensus points, persisting disagreements, a structured synthesis, and an optional " + "moderator prompt. Architect-only. Config-gated on council.general.enabled.",
76062
+ description: "Synthesize responses from a multi-model General Council. Accepts parallel member " + "responses (Round 1, optionally Round 2), detects disagreements, and returns " + "consensus points, persisting disagreements, and a structured synthesis. " + "Architect-only. Config-gated on council.general.enabled.",
76122
76063
  args: {
76123
76064
  question: exports_external.string().min(1).max(8000).describe("The question put to the council, or the spec text to review."),
76124
76065
  mode: exports_external.enum(["general", "spec_review"]).optional().describe('"general" for /swarm council; "spec_review" for SPECIFY-COUNCIL-REVIEW gate.'),
@@ -76224,7 +76165,6 @@ var convene_general_council = createSwarmTool({
76224
76165
  }
76225
76166
  }
76226
76167
  } catch {}
76227
- const moderatorPrompt = generalConfig.moderator === true ? buildModeratorPrompt(input.question, result.synthesis) : undefined;
76228
76168
  const ok = {
76229
76169
  success: true,
76230
76170
  question: input.question,
@@ -76235,7 +76175,6 @@ var convene_general_council = createSwarmTool({
76235
76175
  persistingDisagreements: result.persistingDisagreements,
76236
76176
  allSourcesCount: result.allSources.length,
76237
76177
  synthesis: result.synthesis,
76238
- ...moderatorPrompt !== undefined && { moderatorPrompt },
76239
76178
  evidencePath
76240
76179
  };
76241
76180
  return JSON.stringify(ok, null, 2);
@@ -89154,7 +89093,7 @@ var ArgsSchema4 = exports_external.object({
89154
89093
  working_directory: exports_external.string().optional()
89155
89094
  });
89156
89095
  var web_search = createSwarmTool({
89157
- description: "External web search for council member agents. Returns titled results with snippets and URLs. " + "Restricted to council_member agents via AGENT_TOOL_MAP. Requires council.general.enabled and a " + "configured search API key (Tavily or Brave). max_results is capped at 10 with default from council.general.maxSourcesPerMember.",
89096
+ description: "External web search for architect-driven council research. Returns titled results with snippets and URLs. " + "Used by the architect in MODE: COUNCIL to gather a RESEARCH CONTEXT before dispatching council agents. " + "Requires council.general.enabled and a configured search API key (Tavily or Brave). max_results is capped at 10 with default from council.general.maxSourcesPerMember.",
89158
89097
  args: {
89159
89098
  query: exports_external.string().min(1).max(500).describe("Search query string (1–500 characters)."),
89160
89099
  max_results: exports_external.number().int().min(1).max(20).optional().describe(`Number of results to request (1–20). Hard-capped at ${MAX_RESULTS_HARD_CAP}. Defaults to council.general.maxSourcesPerMember.`),
@@ -90123,7 +90062,7 @@ async function initializeOpenCodeSwarm(ctx) {
90123
90062
  ...opencodeConfig.command || {},
90124
90063
  swarm: {
90125
90064
  template: "/swarm $ARGUMENTS",
90126
- description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|diagnosis|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|brainstorm|qa-gates|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|acknowledge-spec-drift|doctor-tools|close]"
90065
+ description: "Swarm management commands: /swarm [status|plan|agents|history|config|evidence|handoff|archive|diagnose|diagnosis|preflight|sync-plan|benchmark|export|reset|rollback|retrieve|clarify|analyze|specify|brainstorm|council|qa-gates|dark-matter|knowledge|curate|turbo|full-auto|write-retro|reset-session|simulate|promote|checkpoint|acknowledge-spec-drift|doctor-tools|close]"
90127
90066
  },
90128
90067
  "swarm-status": {
90129
90068
  template: "/swarm status",
@@ -90209,6 +90148,10 @@ async function initializeOpenCodeSwarm(ctx) {
90209
90148
  template: "/swarm brainstorm $ARGUMENTS",
90210
90149
  description: "Use /swarm brainstorm to enter the architect MODE: BRAINSTORM planning workflow"
90211
90150
  },
90151
+ "swarm-council": {
90152
+ template: "/swarm council $ARGUMENTS",
90153
+ description: "Use /swarm council <question> to convene a multi-model General Council deliberation (generalist / skeptic / domain expert) [--spec-review]"
90154
+ },
90212
90155
  "swarm-qa-gates": {
90213
90156
  template: "/swarm qa-gates $ARGUMENTS",
90214
90157
  description: "Use /swarm qa-gates to view or modify QA gate profile for the current plan"
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * General Council Mode — architect-only synthesis tool.
3
3
  *
4
- * The architect spawns council_member subagents in parallel for Round 1,
5
- * collects their JSON responses, and calls this tool to synthesize results.
6
- * If the tool detects disagreements and Round 2 deliberation is configured,
7
- * the architect re-delegates to disputing members and calls this tool again
4
+ * The architect spawns council_generalist / council_skeptic /
5
+ * council_domain_expert subagents in parallel for Round 1, collects their
6
+ * JSON responses, and calls this tool to synthesize results. If the tool
7
+ * detects disagreements and Round 2 deliberation is configured, the
8
+ * architect re-delegates to disputing members and calls this tool again
8
9
  * with both round1Responses and round2Responses populated.
9
10
  *
10
11
  * Mirrors the convene-council.ts skeleton but explicitly does NOT inherit
@@ -1,5 +1,5 @@
1
1
  /**
2
- * web_search tool — restricted to council_member agents.
2
+ * web_search tool — owned by the architect for MODE: COUNCIL pre-search.
3
3
  *
4
4
  * Thin wrapper around `src/council/web-search-provider.ts`. Returns structured
5
5
  * results on success and structured errors on failure (never throws). Config-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "7.0.1",
3
+ "version": "7.0.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",
@@ -1,30 +0,0 @@
1
- /**
2
- * General Council member agent.
3
- *
4
- * Implements the NSED peer-review protocol (arXiv:2601.16863):
5
- * - Round 1: independent search + answer with self-reported confidence
6
- * - Round 2: targeted deliberation on disagreements with explicit MAINTAIN /
7
- * CONCEDE / NUANCE stance (ConfMAD)
8
- *
9
- * Tools: web_search ONLY. No write tools, no orchestration tools. The architect
10
- * spawns members in parallel via the OpenCode subagent task system, collects
11
- * structured JSON responses, and synthesizes via convene_general_council.
12
- *
13
- * Prompt template variables (substituted by the architect at delegation time):
14
- * {{MEMBER_ID}} — the council member identifier
15
- * {{ROLE}} — generalist | skeptic | domain_expert | devil_advocate | synthesizer
16
- * {{PERSONA_BLOCK}} — optional persona instructions (omitted if undefined)
17
- * {{ROUND}} — "1" or "2"
18
- * {{DISAGREEMENT_BLOCK}} — Round 2 only: opposing position(s) to address
19
- */
20
- import type { AgentDefinition } from './architect';
21
- export declare const COUNCIL_MEMBER_PROMPT = "You are Council Member {{MEMBER_ID}} ({{ROLE}}) on a multi-model General Council.\n\n{{PERSONA_BLOCK}}\n\nYou are participating in Round {{ROUND}} of a structured deliberation. Your job is to give your independent, evidence-grounded perspective \u2014 not to agree with the group.\n\n================================================================\nROUND {{ROUND}} PROTOCOL\n================================================================\n\nROUND 1 \u2014 Independent Research and Answer\n- Issue 1\u20133 targeted web_search calls to gather evidence relevant to the question.\n- Cite EVERY factual claim with a source URL from your search results.\n- State your confidence (0.0\u20131.0) explicitly. Be honest \u2014 overconfident answers hurt the council.\n- Enumerate areas of uncertainty so the architect knows where you're guessing vs. where you're sure.\n- Do NOT coordinate with other members. You will not see their responses until Round 2.\n- Do NOT pad. Be concise. Substance over volume.\n\nROUND 2 \u2014 Targeted Deliberation (ONLY when this round is invoked for you)\n- {{DISAGREEMENT_BLOCK}}\n- Issue at most 1 additional web_search call.\n- Declare your stance explicitly using one of these keywords as the FIRST word of a paragraph:\n MAINTAIN \u2014 your Round 1 position holds; cite the new evidence supporting it\n CONCEDE \u2014 the opposing position is correct; state specifically what you got wrong\n NUANCE \u2014 both positions are partially right; state the boundary condition that distinguishes them\n- Never CONCEDE without evidence. Sycophantic capitulation degrades the council below an individual member's baseline (NSED arXiv:2601.16863).\n- Never MAINTAIN without engaging the opposing argument on its merits.\n\n================================================================\nRESPONSE FORMAT (always \u2014 both rounds)\n================================================================\n\nReply with a single fenced JSON block. No prose outside the block.\n\n```json\n{\n \"memberId\": \"{{MEMBER_ID}}\",\n \"role\": \"{{ROLE}}\",\n \"round\": {{ROUND}},\n \"response\": \"Your full answer (Round 1) or stance + reasoning (Round 2). Markdown OK inside the string.\",\n \"searchQueries\": [\"query 1\", \"query 2\"],\n \"sources\": [\n { \"title\": \"...\", \"url\": \"...\", \"snippet\": \"...\", \"query\": \"...\" }\n ],\n \"confidence\": 0.85,\n \"areasOfUncertainty\": [\n \"What I'm not sure about, in plain language.\"\n ],\n \"disagreementTopics\": []\n}\n```\n\nFor Round 1: leave `disagreementTopics` as []. For Round 2: list the specific disagreement topics this response addresses.\n\n================================================================\nHARD RULES\n================================================================\n- web_search is your ONLY tool. You cannot read or write files, run commands, or delegate.\n- Never invent sources. If a search returns nothing useful, say so in `areasOfUncertainty`.\n- Never echo other members' responses verbatim. Paraphrase or quote with attribution.\n- Stay within your role and persona. The architect chose you for a specific perspective.\n";
22
- /**
23
- * Factory for the council_member agent definition. The factory mirrors other
24
- * agent factories (createSMEAgent, createReviewerAgent) for consistency.
25
- *
26
- * Per-member context (memberId, role, persona, round, disagreement) is supplied
27
- * by the architect at delegation time via prompt-string substitution; the
28
- * factory itself produces the unparameterized template.
29
- */
30
- export declare function createCouncilMemberAgent(model: string, customPrompt?: string, customAppendPrompt?: string): AgentDefinition;
@@ -1,8 +0,0 @@
1
- /**
2
- * Tests for src/agents/council-member.ts and src/agents/council-moderator.ts.
3
- *
4
- * Covers prompt template content (NSED protocol markers), AGENT_TOOL_MAP
5
- * enforcement (web_search-only for member, empty for moderator), and the
6
- * persona-block insertion path.
7
- */
8
- export {};
@@ -1,20 +0,0 @@
1
- /**
2
- * General Council moderator agent.
3
- *
4
- * Receives the structural synthesis output from convene_general_council
5
- * (consensus / disagreements / sources) and produces a coherent, well-structured
6
- * final answer for the user. Empty tool list — moderation is synthesis-only;
7
- * it does NOT need web_search because every claim it works with has already
8
- * been searched and cited by council members.
9
- *
10
- * Confidence-weighted (Quadratic Voting from NSED arXiv:2601.16863): higher-
11
- * confidence members carry more weight, but evidence quality matters more
12
- * than confidence alone. The moderator must NOT favor a position purely
13
- * because its proponent was confident.
14
- */
15
- import type { AgentDefinition } from './architect';
16
- export declare const COUNCIL_MODERATOR_PROMPT = "You are the General Council Moderator.\n\nYou are receiving the structural synthesis from a multi-model council deliberation:\n- Question (and mode: general or spec_review)\n- All member Round 1 responses with sources\n- Detected disagreements\n- Round 2 deliberation responses (if any)\n- Confidence-weighted consensus claims\n- Persisting disagreements after deliberation\n\nYour job: produce a coherent, well-structured final answer for the user.\n\n================================================================\nRULES\n================================================================\n\n1. LEAD WITH CONSENSUS \u2014 open with the strongest consensus position. Use the\n confidence-weighted ordering (Quadratic Voting): higher-confidence claims\n from multiple members rank higher, but evidence quality outranks raw\n confidence. Never elevate a single confident voice over a well-evidenced\n contrary majority.\n\n2. ACKNOWLEDGE DISAGREEMENT HONESTLY \u2014 for each persisting disagreement, write\n \"experts disagree on X because\u2026\" and present the strongest version of each\n side. Do NOT pretend disagreements are resolved when they are not. Do NOT\n silently pick a winner.\n\n3. CITE THE STRONGEST SOURCES \u2014 link key claims with [title](url) format from\n the deduplicated source list. Pick the most reputable source for each claim;\n do not cite duplicates.\n\n4. BE CONCISE \u2014 the user wants an answer, not a committee report. Default\n length: a few short paragraphs plus a bulleted summary. Expand only when\n the question genuinely requires it.\n\n================================================================\nHARD CONSTRAINTS\n================================================================\n\n- You MUST NOT invent claims that are not present in the council's responses.\n- You MUST NOT add new web research. If something was missed, say so.\n- You MUST NOT favor a position based on member confidence alone \u2014 evidence\n quality is the tie-breaker.\n- You have NO tools. You write the final synthesis from the input given.\n\n================================================================\nOUTPUT FORMAT\n================================================================\n\nPlain markdown. No code fences. No JSON. Suggested structure:\n\n# Answer\n\n<lead consensus position with citation(s)>\n\n<remaining consensus / context paragraphs as needed>\n\n## Where Experts Disagree\n\n- <topic 1>: <position A> vs <position B>, with sources for each\n- <topic 2>: ...\n\n## Sources\n\n- [title](url)\n- ...\n\n(Omit any section that is empty.)\n";
17
- /**
18
- * Factory for the council_moderator agent definition. No tools — synthesis only.
19
- */
20
- export declare function createCouncilModeratorAgent(model: string, customPrompt?: string, customAppendPrompt?: string): AgentDefinition;