@heretek-ai/epistemic-swarm 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/.agents/skills/brainstorming/SKILL.md +13 -0
  2. package/.agents/skills/code_audit/SKILL.md +13 -0
  3. package/.agents/skills/epistemic_search/SKILL.md +13 -0
  4. package/.agents/skills/grilling/SKILL.md +13 -0
  5. package/.agents/skills/oss_scout/SKILL.md +13 -0
  6. package/.agents/skills/research_cache/SKILL.md +13 -0
  7. package/.agents/skills/swarm_config/SKILL.md +13 -0
  8. package/.claude-plugin/plugin.json +15 -5
  9. package/.omp/README.md +39 -0
  10. package/.omp/SYSTEM.md +12 -0
  11. package/.omp/commands/audit.md +10 -0
  12. package/.omp/commands/brainstorming.md +12 -0
  13. package/.omp/commands/grill.md +9 -0
  14. package/.omp/commands/scout.md +10 -0
  15. package/.omp/commands/swarm-config.md +10 -0
  16. package/.omp/commands/swarm.md +9 -0
  17. package/.omp/hooks/post/epistemic-audit.ts +16 -0
  18. package/.omp/hooks/pre/epistemic-redirect.ts +21 -0
  19. package/.omp/prompts/brainstorming.md +8 -0
  20. package/.omp/prompts/swarm.md +8 -0
  21. package/MARKETPLACE.md +8 -0
  22. package/README.md +53 -8
  23. package/bin/cli.js +27 -3
  24. package/config/domain_packs/biopharma.json +23 -0
  25. package/config/domain_packs/legal.json +19 -0
  26. package/config/domain_packs/quant.json +19 -0
  27. package/config/mcp-research-servers.json +7 -0
  28. package/config/mcp_launcher.py +48 -137
  29. package/config/opencode-snippet.json +58 -3
  30. package/config/searxng_mcp.py +42 -83
  31. package/extensions/pi/index.js +196 -28
  32. package/install.sh +20 -4
  33. package/package.json +40 -5
  34. package/plugins/antigravity/README.md +28 -0
  35. package/plugins/antigravity/agents/alpha-thesis.md +6 -0
  36. package/plugins/antigravity/agents/beta-antithesis.md +7 -0
  37. package/plugins/antigravity/agents/brainstormer.md +7 -0
  38. package/plugins/antigravity/agents/epistemic-auditor.md +5 -0
  39. package/plugins/antigravity/hooks.json +23 -0
  40. package/plugins/antigravity/mcp_config.json +33 -0
  41. package/plugins/antigravity/plugin.json +21 -0
  42. package/plugins/antigravity/rules/epistemic-integrity.md +6 -0
  43. package/plugins/antigravity/skills/brainstorming/SKILL.md +13 -0
  44. package/plugins/antigravity/skills/code_audit/SKILL.md +13 -0
  45. package/plugins/antigravity/skills/epistemic_search/SKILL.md +13 -0
  46. package/plugins/antigravity/skills/grilling/SKILL.md +13 -0
  47. package/plugins/antigravity/skills/oss_scout/SKILL.md +13 -0
  48. package/plugins/antigravity/skills/research_cache/SKILL.md +13 -0
  49. package/plugins/antigravity/skills/swarm_config/SKILL.md +13 -0
  50. package/plugins/codex/AGENTS.md.snippet +10 -0
  51. package/plugins/codex/README.md +37 -0
  52. package/plugins/codex/config.toml.snippet +28 -0
  53. package/plugins/codex/openai.yaml +24 -0
  54. package/plugins/codex/skills/brainstorming/SKILL.md +13 -0
  55. package/plugins/codex/skills/code_audit/SKILL.md +13 -0
  56. package/plugins/codex/skills/epistemic_search/SKILL.md +13 -0
  57. package/plugins/codex/skills/grilling/SKILL.md +13 -0
  58. package/plugins/codex/skills/oss_scout/SKILL.md +13 -0
  59. package/plugins/codex/skills/research_cache/SKILL.md +13 -0
  60. package/plugins/codex/skills/swarm_config/SKILL.md +13 -0
  61. package/plugins/gemini/GEMINI.md +15 -0
  62. package/plugins/gemini/README.md +19 -0
  63. package/plugins/gemini/commands/audit.toml +6 -0
  64. package/plugins/gemini/commands/brainstorming.toml +10 -0
  65. package/plugins/gemini/commands/grill.toml +6 -0
  66. package/plugins/gemini/commands/scout.toml +7 -0
  67. package/plugins/gemini/commands/swarm-config.toml +7 -0
  68. package/plugins/gemini/commands/swarm.toml +8 -0
  69. package/plugins/gemini/gemini-extension.json +38 -0
  70. package/plugins/gemini/hooks/hooks.json +11 -0
  71. package/plugins/gemini/skills/brainstorming/SKILL.md +13 -0
  72. package/plugins/gemini/skills/code_audit/SKILL.md +13 -0
  73. package/plugins/gemini/skills/epistemic_search/SKILL.md +13 -0
  74. package/plugins/gemini/skills/grilling/SKILL.md +13 -0
  75. package/plugins/gemini/skills/oss_scout/SKILL.md +13 -0
  76. package/plugins/gemini/skills/research_cache/SKILL.md +13 -0
  77. package/plugins/gemini/skills/swarm_config/SKILL.md +13 -0
  78. package/plugins/opencode/index.js +335 -118
  79. package/prompts/agent_brainstormer.md +97 -0
  80. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  81. package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
  82. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  83. package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
  84. package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
  85. package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
  86. package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
  87. package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
  88. package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
  89. package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
  90. package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
  91. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  92. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  93. package/runner/auctioneer.py +169 -0
  94. package/runner/auditor_engine.py +127 -12
  95. package/runner/claim_store.py +361 -0
  96. package/runner/claim_witness.py +183 -0
  97. package/runner/living_dossiers.py +355 -0
  98. package/runner/mcp_protocol.py +188 -0
  99. package/runner/mcp_server.py +567 -0
  100. package/runner/pcrb.py +212 -0
  101. package/runner/pcrb_verify.py +237 -0
  102. package/runner/refinement.py +335 -0
  103. package/runner/research_swarm.py +378 -94
  104. package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
  105. package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
  106. package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
  107. package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
  108. package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
  109. package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
  110. package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
  111. package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
  112. package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
  113. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  114. package/runner/tests/fixtures/auction_objective.json +35 -0
  115. package/runner/tests/fixtures/borderline_claims.json +27 -0
  116. package/runner/tests/fixtures/divergence_objectives.json +31 -0
  117. package/runner/tests/test_auction_order.py +173 -0
  118. package/runner/tests/test_claim_store.py +162 -0
  119. package/runner/tests/test_claim_witness.py +186 -0
  120. package/runner/tests/test_domain_packs.py +217 -0
  121. package/runner/tests/test_fleet_seam.py +113 -0
  122. package/runner/tests/test_living_dossiers.py +204 -0
  123. package/runner/tests/test_mcp_server.py +212 -0
  124. package/runner/tests/test_pcrb.py +240 -0
  125. package/runner/tests/test_refinement.py +255 -0
  126. package/runner/tests/test_swarm.py +173 -15
  127. package/scripts/auction_experiment.py +180 -0
  128. package/scripts/build_adapters.py +183 -0
  129. package/scripts/divergence_experiment.py +184 -0
  130. package/skills/brainstorming/SKILL.md +106 -0
  131. package/skills/brainstorming/__init__.py +1 -0
  132. package/skills/brainstorming/scripts/brainstorm.py +200 -0
  133. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  134. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  135. package/skills/swarm_config/SKILL.md +1 -1
  136. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  137. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
  138. package/skills/swarm_config/configure.py +45 -11
@@ -3,7 +3,7 @@
3
3
  Epistemic Swarm: Dialectic Multi-Agent Research Runner.
4
4
  Executes parallel Claude Code sub-processes (claude -p) for Proponent and Adversary agents,
5
5
  monitors filesystem IPC scratchpads, and invokes the Epistemic Auditor.
6
- Supports multiple modes: research, audit (codebase), scout (OSS), and hybrid.
6
+ Supports multiple modes: research, audit (codebase), scout (OSS), hybrid, and brainstorm (lateral ideation).
7
7
  """
8
8
 
9
9
  import os
@@ -15,7 +15,7 @@ import subprocess
15
15
  from pathlib import Path
16
16
  from datetime import datetime, timezone
17
17
  from concurrent.futures import ThreadPoolExecutor, as_completed
18
- from typing import Dict, Any, List, Optional
18
+ from typing import Dict, Any, List, Optional, Tuple
19
19
 
20
20
  # Ensure project root is in sys.path
21
21
  PROJECT_ROOT = Path(__file__).resolve().parent.parent
@@ -26,38 +26,97 @@ from runner.auditor_engine import EpistemicAuditorEngine
26
26
  from skills.research_cache.hasher import SourceHasher
27
27
  from skills.swarm_config.configure import load_config
28
28
 
29
+
29
30
  class SwarmRunner:
30
- def __init__(self, base_dir: Optional[Path] = None, mock_mode: bool = False,
31
- mode: Optional[str] = None, engine: Optional[str] = None,
32
- depth: Optional[int] = None):
31
+ def __init__(
32
+ self,
33
+ base_dir: Optional[Path] = None,
34
+ mock_mode: bool = False,
35
+ mode: Optional[str] = None,
36
+ engine: Optional[str] = None,
37
+ depth: Optional[int] = None,
38
+ agent_overrides: Optional[Dict[str, Dict[str, Any]]] = None,
39
+ allocation: Optional[str] = None,
40
+ domain_pack: Optional[str] = None,
41
+ ):
33
42
  self.base_dir = base_dir or Path(".research")
34
43
  self.mock_mode = mock_mode
35
44
  self.config = load_config(str(self.base_dir))
36
45
  self.mode = mode or self.config.get("mode", "research")
37
46
  self.engine = engine or self.config.get("search_engine", "duckduckgo")
38
47
  self.depth = depth or self.config.get("max_iterations", 2)
48
+ # Stream F: "dag" (legacy default) or "auction" (Frontier Markets).
49
+ self.allocation = allocation or self.config.get("allocation", "dag")
50
+ # Stream G: optional Domain Pack (constitution) for the auditor.
51
+ self.domain_pack = domain_pack if domain_pack is not None else self.config.get("domain_pack")
52
+ # Per-agent backend/model overrides (CLI > env > config > default).
53
+ # Keys are role names ("alpha", "beta"); values are {"backend": [...],
54
+ # "model": str|None}. Empty dict means "fall through to next source".
55
+ self.agent_overrides: Dict[str, Dict[str, Any]] = agent_overrides or {}
39
56
  self.state_machine = ResearchStateMachine(base_dir=self.base_dir)
40
57
  self.auditor = EpistemicAuditorEngine(base_dir=self.base_dir)
41
58
  self.hasher = SourceHasher(base_dir=self.base_dir)
42
59
  self.prompts_dir = PROJECT_ROOT / "prompts"
43
60
 
44
- def run_claude_process(self, prompt: str, system_prompt_file: Optional[Path] = None,
45
- tools: str = "default") -> str:
46
- """Executes a headless Claude Code session via `claude -p`."""
61
+ def _resolve_agent_backend(self, role: str) -> Tuple[List[str], Optional[str]]:
62
+ """Resolve (backend_cmd, model) for an agent role.
63
+
64
+ Precedence (Stream E): explicit override (set by CLI flags) > env >
65
+ config > default. Returns (["claude", "-p"], None) when nothing is
66
+ configured, preserving prior behavior byte-for-byte.
67
+ """
68
+ override = self.agent_overrides.get(role) or {}
69
+ env_backend = os.environ.get(f"IUMBTEMS_BACKEND_{role.upper()}")
70
+ env_model = os.environ.get(f"IUMBTEMS_MODEL_{role.upper()}")
71
+
72
+ agents_cfg = self.config.get("agents") or {}
73
+ role_cfg = agents_cfg.get(role) or {}
74
+
75
+ backend = (
76
+ override.get("backend")
77
+ or (env_backend.split() if env_backend else None)
78
+ or role_cfg.get("backend")
79
+ or ["claude", "-p"]
80
+ )
81
+ model = override.get("model") or env_model or role_cfg.get("model")
82
+ return list(backend), model
83
+
84
+ def build_agent_cmd(
85
+ self,
86
+ prompt: str,
87
+ system_prompt_file: Optional[Path] = None,
88
+ tools: str = "default",
89
+ role: str = "alpha",
90
+ ) -> List[str]:
91
+ """Construct the backend argv for an agent. Pure — no subprocess.
92
+
93
+ Exposed separately so tests can assert argv shape (e.g. `--model`
94
+ present when configured, absent in mock/default) without spawning.
95
+ """
96
+ backend, model = self._resolve_agent_backend(role)
97
+ cmd = list(backend) + [prompt, "--tools", tools]
98
+ if model:
99
+ cmd.extend(["--model", str(model)])
100
+ if system_prompt_file and system_prompt_file.exists():
101
+ cmd.extend(["--system-prompt", str(system_prompt_file)])
102
+ return cmd
103
+
104
+ def run_claude_process(
105
+ self,
106
+ prompt: str,
107
+ system_prompt_file: Optional[Path] = None,
108
+ tools: str = "default",
109
+ role: str = "alpha",
110
+ ) -> str:
111
+ """Executes a headless agent session on the configured backend."""
47
112
  if self.mock_mode:
48
113
  return self._mock_claude_response(prompt)
49
114
 
50
- cmd = ["claude", "-p", prompt, "--tools", tools]
51
- if system_prompt_file and system_prompt_file.exists():
52
- cmd.extend(["--system-prompt", str(system_prompt_file)])
115
+ cmd = self.build_agent_cmd(prompt, system_prompt_file, tools, role=role)
53
116
 
54
117
  try:
55
118
  res = subprocess.run(
56
- cmd,
57
- capture_output=True,
58
- text=True,
59
- check=True,
60
- cwd=str(PROJECT_ROOT)
119
+ cmd, capture_output=True, text=True, check=True, cwd=str(PROJECT_ROOT)
61
120
  )
62
121
  return res.stdout.strip()
63
122
  except subprocess.CalledProcessError as e:
@@ -67,25 +126,35 @@ class SwarmRunner:
67
126
  def _mock_claude_response(self, prompt: str) -> str:
68
127
  """Mock response generator for unit testing without live API keys."""
69
128
  if "Orchestrator" in prompt or "manifest.json" in prompt:
70
- return json.dumps({
71
- "session_id": "mock-session-001",
72
- "objective": "Evaluate ZK prover latency",
73
- "scopes": [
74
- {
75
- "scope_id": "scope_01_latency",
76
- "title": "Hardware Prover Latency Bounds",
77
- "objective": "Evaluate Poseidon hash witness generation latency on FPGAs vs GPUs",
78
- "dependencies": [],
79
- "affirmative_targets": ["Sub-200ms witness generation on 2^20 constraints"],
80
- "adversarial_targets": ["PCIe bus bottlenecks during batch streaming"]
81
- }
82
- ]
83
- })
129
+ return json.dumps(
130
+ {
131
+ "session_id": "mock-session-001",
132
+ "objective": "Evaluate ZK prover latency",
133
+ "scopes": [
134
+ {
135
+ "scope_id": "scope_01_latency",
136
+ "title": "Hardware Prover Latency Bounds",
137
+ "objective": "Evaluate Poseidon hash witness generation latency on FPGAs vs GPUs",
138
+ "dependencies": [],
139
+ "affirmative_targets": [
140
+ "Sub-200ms witness generation on 2^20 constraints"
141
+ ],
142
+ "adversarial_targets": [
143
+ "PCIe bus bottlenecks during batch streaming"
144
+ ],
145
+ }
146
+ ],
147
+ }
148
+ )
84
149
  return "MOCK_RESPONSE"
85
150
 
86
- def orchestrate_objective(self, objective: str, frontier_file: Optional[Path] = None) -> List[Dict[str, Any]]:
151
+ def orchestrate_objective(
152
+ self, objective: str, frontier_file: Optional[Path] = None
153
+ ) -> List[Dict[str, Any]]:
87
154
  """Phase 1: Run Swarm Orchestrator to decompose the research/audit question."""
88
- print(f"\n🧠 [Phase 1: Orchestration] Decomposing objective ({self.mode.upper()} mode): '{objective}'...")
155
+ print(
156
+ f"\n🧠 [Phase 1: Orchestration] Decomposing objective ({self.mode.upper()} mode): '{objective}'..."
157
+ )
89
158
  self.state_machine.init_session(objective)
90
159
  self.state_machine.update_session_status(SessionStatus.ORCHESTRATING)
91
160
 
@@ -105,8 +174,10 @@ Max Depth: {self.depth}
105
174
  Output ONLY valid JSON representing the scope decomposition conforming to prompts/orchestrator.md.
106
175
  """
107
176
  system_prompt = self.prompts_dir / "orchestrator.md"
108
- raw_output = self.run_claude_process(orchestrator_prompt, system_prompt_file=system_prompt)
109
-
177
+ raw_output = self.run_claude_process(
178
+ orchestrator_prompt, system_prompt_file=system_prompt
179
+ )
180
+
110
181
  # Parse JSON
111
182
  try:
112
183
  # Handle potential markdown fence blocks
@@ -118,7 +189,9 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
118
189
  manifest_data = json.loads(clean_json.strip())
119
190
  scopes = manifest_data.get("scopes", [])
120
191
  except Exception as e:
121
- print(f"[WARN] Failed to parse JSON from orchestrator output: {e}. Using fallback decomposition.")
192
+ print(
193
+ f"[WARN] Failed to parse JSON from orchestrator output: {e}. Using fallback decomposition."
194
+ )
122
195
  scopes = [
123
196
  {
124
197
  "scope_id": "scope_01_primary_investigation",
@@ -126,7 +199,9 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
126
199
  "objective": objective,
127
200
  "dependencies": [],
128
201
  "affirmative_targets": ["Find corroborating empirical data"],
129
- "adversarial_targets": ["Probe counter-arguments and failure modes"]
202
+ "adversarial_targets": [
203
+ "Probe counter-arguments and failure modes"
204
+ ],
130
205
  }
131
206
  ]
132
207
 
@@ -137,12 +212,16 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
137
212
  def run_agent_alpha(self, scope: Dict[str, Any]):
138
213
  """Executes Agent Alpha (Thesis / Proponent / Structural Auditor) for a scope."""
139
214
  scope_id = scope["scope_id"]
140
- print(f" [Alpha] šŸ›ļø Starting Agent Alpha ({self.mode.upper()} Thesis) on [{scope_id}]...")
215
+ print(
216
+ f" [Alpha] šŸ›ļø Starting Agent Alpha ({self.mode.upper()} Thesis) on [{scope_id}]..."
217
+ )
141
218
 
142
219
  if self.mock_mode:
143
220
  if self.mode == "audit":
144
221
  sample_content = "# System State Machine Architecture\nAtomic state transitions enforce ACID consistency via write-then-rename."
145
- shash = self.hasher.store_source("file:///runner/state_machine.py", sample_content, "State Machine")
222
+ shash = self.hasher.store_source(
223
+ "file:///runner/state_machine.py", sample_content, "State Machine"
224
+ )
146
225
  dossier = {
147
226
  "agent": "Agent Alpha (Code Architect)",
148
227
  "mode": "code_audit",
@@ -155,15 +234,17 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
155
234
  "statement": "State machine transitions enforce atomic ACID guarantees across scratchpad files.",
156
235
  "source_hash": shash,
157
236
  "source_url": "file:///runner/state_machine.py#L45-L65",
158
- "verbatim_quote": "Atomic state transitions enforce ACID consistency via write-then-rename."
237
+ "verbatim_quote": "Atomic state transitions enforce ACID consistency via write-then-rename.",
159
238
  }
160
239
  ],
161
240
  "inferred_implications": [],
162
- "negative_knowledge": []
241
+ "negative_knowledge": [],
163
242
  }
164
243
  elif self.mode == "scout":
165
244
  sample_content = "# High Performance Raft in Rust\nZero-dependency Raft implementation with 150k ops/sec throughput under Apache-2.0."
166
- shash = self.hasher.store_source("https://github.com/example/rust-raft", sample_content, "Rust Raft")
245
+ shash = self.hasher.store_source(
246
+ "https://github.com/example/rust-raft", sample_content, "Rust Raft"
247
+ )
167
248
  dossier = {
168
249
  "agent": "Agent Alpha (OSS Scout)",
169
250
  "mode": "oss_scout",
@@ -176,15 +257,51 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
176
257
  "statement": "Rust-Raft achieves 150k ops/sec with zero external dependencies.",
177
258
  "source_hash": shash,
178
259
  "source_url": "https://github.com/example/rust-raft",
179
- "verbatim_quote": "Zero-dependency Raft implementation with 150k ops/sec throughput under Apache-2.0."
260
+ "verbatim_quote": "Zero-dependency Raft implementation with 150k ops/sec throughput under Apache-2.0.",
180
261
  }
181
262
  ],
182
263
  "inferred_implications": [],
183
- "negative_knowledge": []
264
+ "negative_knowledge": [],
265
+ }
266
+ elif self.mode == "brainstorm":
267
+ sample_content = "# Workspace Domain Snapshot\nEntities: swarm runner, dialectic dossiers, content-addressed cache. Constraint: evidence primacy."
268
+ shash = self.hasher.store_source(
269
+ "file:///.research/domain_model.json",
270
+ sample_content,
271
+ "Domain Snapshot",
272
+ )
273
+ dossier = {
274
+ "agent": "Agent Alpha (Wild Proponent)",
275
+ "mode": "brainstorm",
276
+ "scope_id": scope_id,
277
+ "timestamp": datetime.now(timezone.utc).isoformat(),
278
+ "affirmative_claims": [
279
+ {
280
+ "claim_id": "ALPHA-B01",
281
+ "tag": "VERIFIED",
282
+ "statement": "Workspace entities center on swarm runner, dialectic dossiers, and content-addressed cache.",
283
+ "source_hash": shash,
284
+ "source_url": "file:///.research/domain_model.json",
285
+ "verbatim_quote": "Entities: swarm runner, dialectic dossiers, content-addressed cache.",
286
+ }
287
+ ],
288
+ "inferred_implications": [
289
+ {
290
+ "inference_id": "ALPHA-BI01",
291
+ "tag": "HYPOTHESIS",
292
+ "statement": "What-if: divergence-rewarded synthesis produces higher-upside feature vectors than evidence-gated synthesis.",
293
+ "parent_claims": ["ALPHA-B01"],
294
+ "deductive_logic": "Falsified if blind A/B of brainstorm vs research briefs shows no novelty gain per reviewer vote.",
295
+ "falsification": "Blind reviewer novelty vote shows no gain within 2 review rounds.",
296
+ }
297
+ ],
298
+ "negative_knowledge": [],
184
299
  }
185
300
  else:
186
301
  sample_content = "# FPGA Prover Benchmark\nOur FPGA pipeline executes the Poseidon round constraints in 184ms with a peak memory bandwidth of 45 GB/s."
187
- shash = self.hasher.store_source("https://arxiv.org/abs/2405.0001", sample_content, "FPGA Benchmark")
302
+ shash = self.hasher.store_source(
303
+ "https://arxiv.org/abs/2405.0001", sample_content, "FPGA Benchmark"
304
+ )
188
305
  dossier = {
189
306
  "agent": "Agent Alpha (Thesis)",
190
307
  "scope_id": scope_id,
@@ -196,7 +313,7 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
196
313
  "statement": "FPGA-accelerated Poseidon provers achieve sub-200ms latency on 2^20 constraints.",
197
314
  "source_hash": shash,
198
315
  "source_url": "https://arxiv.org/abs/2405.0001",
199
- "verbatim_quote": "Our FPGA pipeline executes the Poseidon round constraints in 184ms with a peak memory bandwidth of 45 GB/s."
316
+ "verbatim_quote": "Our FPGA pipeline executes the Poseidon round constraints in 184ms with a peak memory bandwidth of 45 GB/s.",
200
317
  }
201
318
  ],
202
319
  "inferred_implications": [
@@ -205,10 +322,10 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
205
322
  "tag": "INFERRED",
206
323
  "statement": "Hardware provers satisfy 1-second block finality bounds.",
207
324
  "parent_claims": ["ALPHA-C01"],
208
- "deductive_logic": "184ms << 1000ms target."
325
+ "deductive_logic": "184ms << 1000ms target.",
209
326
  }
210
327
  ],
211
- "negative_knowledge": []
328
+ "negative_knowledge": [],
212
329
  }
213
330
  else:
214
331
  prompt = f"Run Agent Alpha ({self.mode} mode) for scope: {json.dumps(scope)}. Engine: {self.engine}. Depth: {self.depth}. Save findings to {scope_id} scratchpad."
@@ -216,10 +333,14 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
216
333
  system_prompt = self.prompts_dir / "agent_code_auditor.md"
217
334
  elif self.mode == "scout":
218
335
  system_prompt = self.prompts_dir / "agent_oss_scout.md"
336
+ elif self.mode == "brainstorm":
337
+ system_prompt = self.prompts_dir / "agent_brainstormer.md"
219
338
  else:
220
339
  system_prompt = self.prompts_dir / "agent_alpha_thesis.md"
221
- self.run_claude_process(prompt, system_prompt_file=system_prompt)
222
- dossier_path = self.state_machine.get_scope_dir(scope_id) / "alpha_dossier.json"
340
+ self.run_claude_process(prompt, system_prompt_file=system_prompt, role="alpha")
341
+ dossier_path = (
342
+ self.state_machine.get_scope_dir(scope_id) / "alpha_dossier.json"
343
+ )
223
344
  with open(dossier_path, "r", encoding="utf-8") as f:
224
345
  dossier = json.load(f)
225
346
 
@@ -229,12 +350,18 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
229
350
  def run_agent_beta(self, scope: Dict[str, Any]):
230
351
  """Executes Agent Beta (Antithesis / Red Team) for a scope."""
231
352
  scope_id = scope["scope_id"]
232
- print(f" [Beta] šŸŽÆ Starting Agent Beta ({self.mode.upper()} Red Team) on [{scope_id}]...")
353
+ print(
354
+ f" [Beta] šŸŽÆ Starting Agent Beta ({self.mode.upper()} Red Team) on [{scope_id}]..."
355
+ )
233
356
 
234
357
  if self.mock_mode:
235
358
  if self.mode == "audit":
236
359
  sample_content = "# Concurrency Analysis\nSubprocess writes may conflict if file descriptors are left open across parallel threads."
237
- shash = self.hasher.store_source("file:///runner/state_machine.py#race", sample_content, "Concurrency Check")
360
+ shash = self.hasher.store_source(
361
+ "file:///runner/state_machine.py#race",
362
+ sample_content,
363
+ "Concurrency Check",
364
+ )
238
365
  dossier = {
239
366
  "agent": "Agent Beta (Code Red Team)",
240
367
  "mode": "code_audit",
@@ -248,21 +375,23 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
248
375
  "source_hash": shash,
249
376
  "source_url": "file:///runner/state_machine.py#race",
250
377
  "verbatim_quote": "Subprocess writes may conflict if file descriptors are left open across parallel threads.",
251
- "severity": "MEDIUM"
378
+ "severity": "MEDIUM",
252
379
  }
253
380
  ],
254
381
  "methodological_critiques": [
255
382
  {
256
383
  "target_assertion": "State machine transitions enforce atomic ACID guarantees across scratchpad files.",
257
384
  "critique": "Unprotected open(..., 'w') creates race condition window between concurrent agents.",
258
- "evidence_hash": shash
385
+ "evidence_hash": shash,
259
386
  }
260
387
  ],
261
- "negative_knowledge": []
388
+ "negative_knowledge": [],
262
389
  }
263
390
  elif self.mode == "scout":
264
391
  sample_content = "# High Performance Raft in Rust\nZero-dependency Raft implementation with 150k ops/sec throughput under Apache-2.0."
265
- shash = self.hasher.store_source("https://github.com/example/rust-raft", sample_content, "Rust Raft")
392
+ shash = self.hasher.store_source(
393
+ "https://github.com/example/rust-raft", sample_content, "Rust Raft"
394
+ )
266
395
  dossier = {
267
396
  "agent": "Agent Beta (OSS Red Team)",
268
397
  "mode": "oss_scout",
@@ -276,21 +405,57 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
276
405
  "source_hash": shash,
277
406
  "source_url": "https://github.com/example/rust-raft",
278
407
  "verbatim_quote": "Zero-dependency Raft implementation with 150k ops/sec throughput under Apache-2.0.",
279
- "severity": "LOW"
408
+ "severity": "LOW",
280
409
  }
281
410
  ],
282
411
  "methodological_critiques": [
283
412
  {
284
413
  "target_assertion": "Rust-Raft achieves 150k ops/sec with zero external dependencies.",
285
414
  "critique": "Throughput degrades during log compaction due to unbuffered disk sync.",
286
- "evidence_hash": shash
415
+ "evidence_hash": shash,
287
416
  }
288
417
  ],
289
- "negative_knowledge": []
418
+ "negative_knowledge": [],
419
+ }
420
+ elif self.mode == "brainstorm":
421
+ sample_content = "# Inversion Probe\nWhat if the evidence gate is the bottleneck? Divergence-rewarded synthesis explores what-if mechanics first."
422
+ shash = self.hasher.store_source(
423
+ "file:///.research/domain_model.json#inversion",
424
+ sample_content,
425
+ "Inversion Probe",
426
+ )
427
+ dossier = {
428
+ "agent": "Agent Beta (Radical Inverter)",
429
+ "mode": "brainstorm",
430
+ "scope_id": scope_id,
431
+ "timestamp": datetime.now(timezone.utc).isoformat(),
432
+ "falsification_claims": [
433
+ {
434
+ "claim_id": "BETA-B01",
435
+ "tag": "VERIFIED",
436
+ "statement": "Inversion probe: evidence gating may bottleneck lateral ideation throughput.",
437
+ "source_hash": shash,
438
+ "source_url": "file:///.research/domain_model.json#inversion",
439
+ "verbatim_quote": "What if the evidence gate is the bottleneck?",
440
+ "severity": "INFO",
441
+ }
442
+ ],
443
+ "methodological_critiques": [
444
+ {
445
+ "target_assertion": "Divergence-rewarded synthesis produces higher-upside feature vectors.",
446
+ "critique": "Novelty without falsification probes is indistinguishable from hallucination; require spike tests.",
447
+ "evidence_hash": shash,
448
+ }
449
+ ],
450
+ "negative_knowledge": [],
290
451
  }
291
452
  else:
292
453
  sample_content = "# PCIe Bus Saturation Study\nIn continuous batch streaming, PCIe 4.0 transfers introduce a 650ms delay, yielding total latency > 800ms."
293
- shash = self.hasher.store_source("https://arxiv.org/abs/2406.9999", sample_content, "PCIe Bottlenecks")
454
+ shash = self.hasher.store_source(
455
+ "https://arxiv.org/abs/2406.9999",
456
+ sample_content,
457
+ "PCIe Bottlenecks",
458
+ )
294
459
 
295
460
  dossier = {
296
461
  "agent": "Agent Beta (Red Team)",
@@ -303,22 +468,22 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
303
468
  "statement": "Batch streaming incurs a 650ms PCIe transfer delay under production loads.",
304
469
  "source_hash": shash,
305
470
  "source_url": "https://arxiv.org/abs/2406.9999",
306
- "verbatim_quote": "In continuous batch streaming, PCIe 4.0 transfers introduce a 650ms delay, yielding total latency > 800ms."
471
+ "verbatim_quote": "In continuous batch streaming, PCIe 4.0 transfers introduce a 650ms delay, yielding total latency > 800ms.",
307
472
  }
308
473
  ],
309
474
  "methodological_critiques": [
310
475
  {
311
476
  "target_assertion": "FPGA-accelerated Poseidon provers achieve sub-200ms latency on 2^20 constraints.",
312
477
  "critique": "Benchmark isolates compute kernel and ignores host-to-device PCIe latency in pipelined batches.",
313
- "evidence_hash": shash
478
+ "evidence_hash": shash,
314
479
  }
315
480
  ],
316
481
  "negative_knowledge": [
317
482
  {
318
483
  "query": "Zero-latency PCIe streaming ZK provers",
319
- "finding": "No architecture eliminates bus transfer overhead without on-chip memory > 128GB."
484
+ "finding": "No architecture eliminates bus transfer overhead without on-chip memory > 128GB.",
320
485
  }
321
- ]
486
+ ],
322
487
  }
323
488
  else:
324
489
  prompt = f"Run Agent Beta ({self.mode} mode) for scope: {json.dumps(scope)}. Engine: {self.engine}. Depth: {self.depth}. Save findings to {scope_id} scratchpad."
@@ -326,42 +491,62 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
326
491
  system_prompt = self.prompts_dir / "agent_code_auditor.md"
327
492
  elif self.mode == "scout":
328
493
  system_prompt = self.prompts_dir / "agent_oss_scout.md"
494
+ elif self.mode == "brainstorm":
495
+ system_prompt = self.prompts_dir / "agent_brainstormer.md"
329
496
  else:
330
497
  system_prompt = self.prompts_dir / "agent_beta_antithesis.md"
331
- self.run_claude_process(prompt, system_prompt_file=system_prompt)
332
- dossier_path = self.state_machine.get_scope_dir(scope_id) / "beta_dossier.json"
498
+ self.run_claude_process(prompt, system_prompt_file=system_prompt, role="beta")
499
+ dossier_path = (
500
+ self.state_machine.get_scope_dir(scope_id) / "beta_dossier.json"
501
+ )
333
502
  with open(dossier_path, "r", encoding="utf-8") as f:
334
503
  dossier = json.load(f)
335
504
 
336
505
  self.state_machine.record_agent_completion(scope_id, "beta", dossier)
337
506
  print(f" [Beta] āœ… Completed Agent Beta for [{scope_id}].")
338
507
 
339
- def execute_scope_dialectic(self, scope: Dict[str, Any]):
340
- """Dispatches Agent Alpha and Agent Beta concurrently."""
508
+ def execute_scope_dialectic(self, scope: Dict[str, Any]) -> Dict[str, Any]:
509
+ """Dispatches Agent Alpha and Agent Beta concurrently. Returns the audit report."""
341
510
  scope_id = scope["scope_id"]
342
- print(f"\n⚔ [Swarm Dispatch] Launching Dialectic Pair for [{scope_id}]: '{scope.get('title')}'")
511
+ print(
512
+ f"\n⚔ [Swarm Dispatch] Launching Dialectic Pair for [{scope_id}]: '{scope.get('title')}'"
513
+ )
343
514
  self.state_machine.update_scope_status(scope_id, ScopeStatus.RUNNING_PARALLEL)
344
-
515
+
345
516
  with ThreadPoolExecutor(max_workers=2) as executor:
346
517
  future_alpha = executor.submit(self.run_agent_alpha, scope)
347
518
  future_beta = executor.submit(self.run_agent_beta, scope)
348
-
519
+
349
520
  # Wait for both
350
521
  future_alpha.result()
351
522
  future_beta.result()
352
523
 
353
524
  # Phase 4: Run Epistemic Auditor
354
- print(f"āš–ļø [Auditor] Auditing evidence & computing divergence for [{scope_id}]...")
355
- audit_report = self.auditor.audit_scope(scope_id)
525
+ print(
526
+ f"āš–ļø [Auditor] Auditing evidence & computing divergence for [{scope_id}]..."
527
+ )
528
+ constitution = None
529
+ if self.domain_pack:
530
+ from runner.refinement import load_domain_pack
531
+
532
+ constitution = load_domain_pack(self.domain_pack)
533
+ audit_report = self.auditor.audit_scope(scope_id, constitution=constitution)
356
534
  summary = audit_report["summary"]
357
- print(f" [Audit Result] Score: {summary['epistemic_score']}/1.0 | Divergence: {summary['divergence_score']} | Verified: {summary['verified_passed']} | Rejected: {summary['unverified_rejected']}")
535
+ print(
536
+ f" [Audit Result] Score: {summary['epistemic_score']}/1.0 | Divergence: {summary['divergence_score']} | Verified: {summary['verified_passed']} | Rejected: {summary['unverified_rejected']}"
537
+ )
538
+ return audit_report
358
539
 
359
540
  def run_swarm(self, objective: str, frontier_file: Optional[Path] = None):
360
541
  """Full end-to-end execution loop."""
361
542
  start_time = datetime.now(timezone.utc)
362
543
  print("=" * 70)
363
- print(f"🌟 EPISTEMIC SWARM: HIGH-INTEGRITY RESEARCH HARNESS [{self.mode.upper()} MODE]")
364
- print(f" Engine: {self.engine.upper()} | Depth: {self.depth} | Dir: {self.base_dir}")
544
+ print(
545
+ f"🌟 EPISTEMIC SWARM: HIGH-INTEGRITY RESEARCH HARNESS [{self.mode.upper()} MODE]"
546
+ )
547
+ print(
548
+ f" Engine: {self.engine.upper()} | Depth: {self.depth} | Dir: {self.base_dir}"
549
+ )
365
550
  print("=" * 70)
366
551
 
367
552
  # 1. Orchestrate
@@ -374,24 +559,61 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
374
559
  # Check if all scopes are complete
375
560
  manifest = self.state_machine.load_global_manifest()
376
561
  all_complete = all(
377
- self.state_machine.load_scope_manifest(s["scope_id"]).get("status") == ScopeStatus.COMPLETE.value
562
+ self.state_machine.load_scope_manifest(s["scope_id"]).get("status")
563
+ == ScopeStatus.COMPLETE.value
378
564
  for s in manifest["scopes"]
379
565
  )
380
566
  if all_complete:
381
567
  break
382
568
  else:
383
- print("[ERROR] Deadlock in scope dependency graph.", file=sys.stderr)
569
+ print(
570
+ "[ERROR] Deadlock in scope dependency graph.", file=sys.stderr
571
+ )
384
572
  self.state_machine.update_session_status(SessionStatus.FAILED)
385
573
  return
386
574
 
387
- for scope in ready_scopes:
388
- self.execute_scope_dialectic(scope)
575
+ # Stream F: "auction" reorders each ready batch by expected
576
+ # information gain; "dag" keeps legacy dependency order (all ready
577
+ # scopes dispatched in the batch, unchanged).
578
+ if self.allocation == "auction":
579
+ from runner.auctioneer import (
580
+ estimate_tokens,
581
+ record_scope_telemetry,
582
+ score_scopes,
583
+ )
584
+
585
+ scored = score_scopes(ready_scopes, base_dir=self.base_dir)
586
+ ordered = [s for _b, s in scored]
587
+ bids = {s.get("scope_id"): b for b, s in scored}
588
+ for ordered_scope in ordered:
589
+ audit_report = self.execute_scope_dialectic(ordered_scope)
590
+ summary = (audit_report or {}).get("summary", {})
591
+ sid = ordered_scope.get("scope_id", "")
592
+ # tokens_used is a chars/4 ESTIMATE — flagged approximation.
593
+ dossier_chars = 0
594
+ scope_dir = self.state_machine.get_scope_dir(sid)
595
+ for name in ("alpha_dossier.json", "beta_dossier.json"):
596
+ p = scope_dir / name
597
+ if p.exists():
598
+ dossier_chars += p.stat().st_size
599
+ record_scope_telemetry(
600
+ self.base_dir,
601
+ sid,
602
+ tokens_used=estimate_tokens("x" * dossier_chars),
603
+ verified_claims=summary.get("verified_passed", 0),
604
+ bid=bids.get(sid),
605
+ )
606
+ else:
607
+ for scope in ready_scopes:
608
+ self.execute_scope_dialectic(scope)
389
609
 
390
610
  # 3. Master Synthesis Compilation
391
- print(f"\nšŸ“œ [Phase 5: Master Synthesis] Aggregating {self.mode.upper()} dossiers...")
611
+ print(
612
+ f"\nšŸ“œ [Phase 5: Master Synthesis] Aggregating {self.mode.upper()} dossiers..."
613
+ )
392
614
  report_path = self._compile_master_synthesis(objective)
393
615
  self.state_machine.update_session_status(SessionStatus.COMPLETED)
394
-
616
+
395
617
  duration = (datetime.now(timezone.utc) - start_time).total_seconds()
396
618
  print(f"\nšŸŽ‰ Swarm run completed in {duration:.1f}s. Report: {report_path}")
397
619
 
@@ -401,7 +623,8 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
401
623
  "audit": "Codebase Architectural & Security Audit",
402
624
  "scout": "Open-Source Software Discovery & Clean-Room Blueprint",
403
625
  "hybrid": "Hybrid Codebase & Literature Epistemic Report",
404
- "research": "Master Epistemic Research Report"
626
+ "brainstorm": "Lateral Brainstorm & Speculative Ideation Portfolio",
627
+ "research": "Master Epistemic Research Report",
405
628
  }
406
629
  title = mode_titles.get(self.mode, "Master Epistemic Research Report")
407
630
 
@@ -410,7 +633,7 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
410
633
  f"**Session ID**: `{manifest['session_id']}` | **Mode**: `{self.mode.upper()}` | **Engine**: `{self.engine}` | **Generated**: `{manifest['updated_at']}`\n",
411
634
  "## Executive Summary",
412
635
  f"This brief was compiled using the Epistemic Swarm dialectic harness ({self.mode} mode). Every factual statement carries an empirical verification pointer backed by a content-addressed raw document cache.\n",
413
- "## Scope Findings & Dialectic Balance Sheets\n"
636
+ "## Scope Findings & Dialectic Balance Sheets\n",
414
637
  ]
415
638
 
416
639
  total_verified = 0
@@ -437,8 +660,12 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
437
660
 
438
661
  avg_div = round(sum(all_divergences) / max(1, len(all_divergences)), 2)
439
662
  synthesis_lines.append(f"\n## Swarm Epistemic Audit Totals\n")
440
- synthesis_lines.append(f"- **Total Verified Primary Citations**: `{total_verified}`")
441
- synthesis_lines.append(f"- **Total Unverified Claims Purged**: `{total_rejected}`")
663
+ synthesis_lines.append(
664
+ f"- **Total Verified Primary Citations**: `{total_verified}`"
665
+ )
666
+ synthesis_lines.append(
667
+ f"- **Total Unverified Claims Purged**: `{total_rejected}`"
668
+ )
442
669
  synthesis_lines.append(f"- **Mean Swarm Divergence Score**: `{avg_div}`")
443
670
 
444
671
  final_path = self.base_dir / "final_synthesis.md"
@@ -456,28 +683,85 @@ Output ONLY valid JSON representing the scope decomposition conforming to prompt
456
683
  with open(scout_path, "w", encoding="utf-8") as f:
457
684
  f.write("\n".join(synthesis_lines))
458
685
  return scout_path
686
+ elif self.mode == "brainstorm":
687
+ brainstorm_path = self.base_dir / "brainstorm_report.md"
688
+ with open(brainstorm_path, "w", encoding="utf-8") as f:
689
+ f.write("\n".join(synthesis_lines))
690
+ return brainstorm_path
459
691
 
460
692
  return final_path
461
693
 
462
694
 
463
695
  def main():
464
- parser = argparse.ArgumentParser(description="Epistemic Swarm Dialectic Research Runner")
465
- parser.add_argument("--objective", type=str, required=True, help="Research question or objective")
466
- parser.add_argument("--frontier", type=str, help="Path to settled frontier.json from /grilling")
467
- parser.add_argument("--mock-claude", action="store_true", help="Run with synthetic test data without invoking Claude Code")
468
- parser.add_argument("--dir", default=".research", help="Path to .research workspace")
469
- parser.add_argument("--mode", choices=["research", "audit", "scout", "hybrid"], default=None, help="Operating mode")
470
- parser.add_argument("--engine", choices=["duckduckgo", "brave", "firecrawl", "searxng"], default=None, help="Search engine")
471
- parser.add_argument("--depth", "--iterations", type=int, default=None, help="Max dialectic depth / iterations")
696
+ parser = argparse.ArgumentParser(
697
+ description="Epistemic Swarm Dialectic Research Runner"
698
+ )
699
+ parser.add_argument(
700
+ "--objective", type=str, required=True, help="Research question or objective"
701
+ )
702
+ parser.add_argument(
703
+ "--frontier", type=str, help="Path to settled frontier.json from /grilling"
704
+ )
705
+ parser.add_argument(
706
+ "--mock-claude",
707
+ action="store_true",
708
+ help="Run with synthetic test data without invoking Claude Code",
709
+ )
710
+ parser.add_argument(
711
+ "--dir", default=".research", help="Path to .research workspace"
712
+ )
713
+ parser.add_argument(
714
+ "--mode",
715
+ choices=["research", "audit", "scout", "hybrid", "brainstorm"],
716
+ default=None,
717
+ help="Operating mode",
718
+ )
719
+ parser.add_argument(
720
+ "--engine",
721
+ choices=["duckduckgo", "brave", "firecrawl", "searxng"],
722
+ default=None,
723
+ help="Search engine",
724
+ )
725
+ parser.add_argument(
726
+ "--depth",
727
+ "--iterations",
728
+ type=int,
729
+ default=None,
730
+ help="Max dialectic depth / iterations",
731
+ )
732
+
733
+ # Stream E: per-agent backend/model overrides (CLI > env > config).
734
+ parser.add_argument("--model-alpha", type=str, default=None,
735
+ help="Model id for Agent Alpha (thesis)")
736
+ parser.add_argument("--model-beta", type=str, default=None,
737
+ help="Model id for Agent Beta (antithesis)")
738
+ parser.add_argument("--beta-backend", type=str, nargs="+", default=None,
739
+ help="Backend command list for Beta, e.g. --beta-backend ollama run qwen3")
740
+ parser.add_argument("--allocation", choices=["dag", "auction"], default=None,
741
+ help="Scope allocation policy (default: config/dag; auction = Frontier Markets)")
742
+ parser.add_argument("--domain-pack", type=str, default=None,
743
+ help="Regulated Domain Pack id (config/domain_packs/<id>.json), e.g. biopharma")
472
744
 
473
745
  args = parser.parse_args()
474
746
  frontier_path = Path(args.frontier) if args.frontier else None
747
+
748
+ agent_overrides: Dict[str, Dict[str, Any]] = {}
749
+ if args.model_alpha:
750
+ agent_overrides.setdefault("alpha", {})["model"] = args.model_alpha
751
+ if args.model_beta:
752
+ agent_overrides.setdefault("beta", {})["model"] = args.model_beta
753
+ if args.beta_backend:
754
+ agent_overrides.setdefault("beta", {})["backend"] = args.beta_backend
755
+
475
756
  runner = SwarmRunner(
476
757
  base_dir=Path(args.dir),
477
758
  mock_mode=args.mock_claude,
478
759
  mode=args.mode,
479
760
  engine=args.engine,
480
- depth=args.depth
761
+ depth=args.depth,
762
+ agent_overrides=agent_overrides or None,
763
+ allocation=args.allocation,
764
+ domain_pack=args.domain_pack,
481
765
  )
482
766
  runner.run_swarm(args.objective, frontier_file=frontier_path)
483
767