@heretek-ai/epistemic-swarm 0.7.25 → 1.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.
Files changed (350) hide show
  1. package/index.ts +1 -0
  2. package/package.json +63 -139
  3. package/rpc.ts +1 -0
  4. package/server.ts +1 -0
  5. package/src/agents.ts +69 -0
  6. package/src/continue.ts +40 -0
  7. package/src/hooks.ts +283 -0
  8. package/src/panels.tsx +220 -0
  9. package/src/policy.ts +221 -0
  10. package/src/pr.ts +37 -0
  11. package/src/research.ts +63 -0
  12. package/src/rpc-def.ts +136 -0
  13. package/src/rpc.ts +331 -0
  14. package/src/runtime.ts +112 -0
  15. package/src/server.ts +311 -0
  16. package/src/session.ts +61 -0
  17. package/src/tools.ts +61 -0
  18. package/src/tui.tsx +206 -0
  19. package/tui.tsx +1 -0
  20. package/.agents/skills/brainstorming/SKILL.md +0 -19
  21. package/.agents/skills/code_audit/SKILL.md +0 -19
  22. package/.agents/skills/darkharvest/SKILL.md +0 -19
  23. package/.agents/skills/epistemic_search/SKILL.md +0 -19
  24. package/.agents/skills/factory/SKILL.md +0 -19
  25. package/.agents/skills/grilling/SKILL.md +0 -19
  26. package/.agents/skills/oss_scout/SKILL.md +0 -19
  27. package/.agents/skills/research_cache/SKILL.md +0 -19
  28. package/.agents/skills/swarm_config/SKILL.md +0 -19
  29. package/.claude-plugin/marketplace.json +0 -61
  30. package/.claude-plugin/plugin.json +0 -76
  31. package/.omp/README.md +0 -39
  32. package/.omp/SYSTEM.md +0 -17
  33. package/.omp/commands/audit.md +0 -10
  34. package/.omp/commands/brainstorming.md +0 -12
  35. package/.omp/commands/darkharvest.md +0 -11
  36. package/.omp/commands/domainexpansion.md +0 -9
  37. package/.omp/commands/factory.md +0 -9
  38. package/.omp/commands/grill.md +0 -9
  39. package/.omp/commands/scout.md +0 -10
  40. package/.omp/commands/swarm-config.md +0 -10
  41. package/.omp/commands/swarm.md +0 -9
  42. package/.omp/hooks/post/epistemic-audit.ts +0 -16
  43. package/.omp/hooks/pre/epistemic-redirect.ts +0 -21
  44. package/.omp/prompts/brainstorming.md +0 -8
  45. package/.omp/prompts/swarm.md +0 -8
  46. package/LICENSE +0 -126
  47. package/MARKETPLACE.md +0 -130
  48. package/README.md +0 -403
  49. package/bin/cli.js +0 -297
  50. package/config/claude-settings-patch.json +0 -10
  51. package/config/docker-compose.infra.yml +0 -23
  52. package/config/domain_packs/biopharma.json +0 -23
  53. package/config/domain_packs/legal.json +0 -19
  54. package/config/domain_packs/quant.json +0 -19
  55. package/config/mcp-research-servers.json +0 -33
  56. package/config/mcp_launcher.py +0 -153
  57. package/config/opencode-snippet.json +0 -156
  58. package/config/searxng_mcp.py +0 -92
  59. package/extensions/pi/index.js +0 -357
  60. package/hooks/hooks.json +0 -45
  61. package/install.sh +0 -473
  62. package/plugins/antigravity/README.md +0 -72
  63. package/plugins/antigravity/agents/alpha-thesis.md +0 -36
  64. package/plugins/antigravity/agents/beta-antithesis.md +0 -35
  65. package/plugins/antigravity/agents/brainstormer.md +0 -27
  66. package/plugins/antigravity/agents/code-auditor.md +0 -28
  67. package/plugins/antigravity/agents/darkharvester.md +0 -30
  68. package/plugins/antigravity/agents/epistemic-auditor.md +0 -28
  69. package/plugins/antigravity/agents/manager.md +0 -26
  70. package/plugins/antigravity/agents/oss-scout.md +0 -27
  71. package/plugins/antigravity/agents/programmer.md +0 -25
  72. package/plugins/antigravity/agents/qa-a.md +0 -22
  73. package/plugins/antigravity/agents/qa-b.md +0 -22
  74. package/plugins/antigravity/antigravity_hooks.py +0 -265
  75. package/plugins/antigravity/commands/audit.md +0 -11
  76. package/plugins/antigravity/commands/brainstorm.md +0 -9
  77. package/plugins/antigravity/commands/brainstorming.md +0 -10
  78. package/plugins/antigravity/commands/darkharvest.md +0 -12
  79. package/plugins/antigravity/commands/domainexpansion.md +0 -12
  80. package/plugins/antigravity/commands/factory.md +0 -14
  81. package/plugins/antigravity/commands/grill.md +0 -10
  82. package/plugins/antigravity/commands/scout.md +0 -11
  83. package/plugins/antigravity/commands/swarm-config.md +0 -9
  84. package/plugins/antigravity/commands/swarm.md +0 -12
  85. package/plugins/antigravity/hook_bus_bridge.js +0 -352
  86. package/plugins/antigravity/hooks.json +0 -44
  87. package/plugins/antigravity/mcp_config.json +0 -33
  88. package/plugins/antigravity/mcp_launcher.py +0 -58
  89. package/plugins/antigravity/plugin.json +0 -13
  90. package/plugins/antigravity/rules/AGENTS.md +0 -127
  91. package/plugins/antigravity/rules/epistemic-integrity.md +0 -16
  92. package/plugins/antigravity/skills/brainstorming/SKILL.md +0 -19
  93. package/plugins/antigravity/skills/code_audit/SKILL.md +0 -19
  94. package/plugins/antigravity/skills/darkharvest/SKILL.md +0 -19
  95. package/plugins/antigravity/skills/epistemic_search/SKILL.md +0 -19
  96. package/plugins/antigravity/skills/factory/SKILL.md +0 -19
  97. package/plugins/antigravity/skills/grilling/SKILL.md +0 -19
  98. package/plugins/antigravity/skills/oss_scout/SKILL.md +0 -19
  99. package/plugins/antigravity/skills/research_cache/SKILL.md +0 -19
  100. package/plugins/antigravity/skills/swarm_config/SKILL.md +0 -19
  101. package/plugins/codex/AGENTS.md.snippet +0 -10
  102. package/plugins/codex/README.md +0 -37
  103. package/plugins/codex/config.toml.snippet +0 -28
  104. package/plugins/codex/openai.yaml +0 -24
  105. package/plugins/codex/skills/brainstorming/SKILL.md +0 -19
  106. package/plugins/codex/skills/code_audit/SKILL.md +0 -19
  107. package/plugins/codex/skills/darkharvest/SKILL.md +0 -19
  108. package/plugins/codex/skills/epistemic_search/SKILL.md +0 -19
  109. package/plugins/codex/skills/factory/SKILL.md +0 -19
  110. package/plugins/codex/skills/grilling/SKILL.md +0 -19
  111. package/plugins/codex/skills/oss_scout/SKILL.md +0 -19
  112. package/plugins/codex/skills/research_cache/SKILL.md +0 -19
  113. package/plugins/codex/skills/swarm_config/SKILL.md +0 -19
  114. package/plugins/darkharvest/.claude-plugin/plugin.json +0 -15
  115. package/plugins/darkharvest/agents/harvest-proponent.md +0 -22
  116. package/plugins/darkharvest/agents/harvest-redteam.md +0 -20
  117. package/plugins/darkharvest/evals/teardown-verdict/graders/license-line.md +0 -6
  118. package/plugins/darkharvest/evals/teardown-verdict/graders/skill-fired.md +0 -5
  119. package/plugins/darkharvest/evals/teardown-verdict/prompt.md +0 -6
  120. package/plugins/darkharvest/skills/darkharvest/SKILL.md +0 -70
  121. package/plugins/darkharvest/skills/darkharvest/scripts/harvest.py +0 -289
  122. package/plugins/factory/.claude-plugin/plugin.json +0 -15
  123. package/plugins/factory/agents/factory-manager.md +0 -22
  124. package/plugins/factory/agents/programmer.md +0 -16
  125. package/plugins/factory/agents/qa-adversarial.md +0 -17
  126. package/plugins/factory/agents/qa-functional.md +0 -17
  127. package/plugins/factory/evals/gate-halt/graders/gates-first.md +0 -6
  128. package/plugins/factory/evals/gate-halt/graders/skill-fired.md +0 -5
  129. package/plugins/factory/evals/gate-halt/prompt.md +0 -6
  130. package/plugins/factory/skills/factory/SKILL.md +0 -61
  131. package/plugins/factory/skills/factory/scripts/factory.py +0 -300
  132. package/plugins/gemini/GEMINI.md +0 -16
  133. package/plugins/gemini/README.md +0 -20
  134. package/plugins/gemini/commands/audit.toml +0 -6
  135. package/plugins/gemini/commands/brainstorming.toml +0 -10
  136. package/plugins/gemini/commands/darkharvest.toml +0 -8
  137. package/plugins/gemini/commands/domainexpansion.toml +0 -7
  138. package/plugins/gemini/commands/factory.toml +0 -8
  139. package/plugins/gemini/commands/grill.toml +0 -6
  140. package/plugins/gemini/commands/scout.toml +0 -7
  141. package/plugins/gemini/commands/swarm-config.toml +0 -7
  142. package/plugins/gemini/commands/swarm.toml +0 -8
  143. package/plugins/gemini/gemini-extension.json +0 -38
  144. package/plugins/gemini/hooks/hooks.json +0 -11
  145. package/plugins/gemini/skills/brainstorming/SKILL.md +0 -19
  146. package/plugins/gemini/skills/code_audit/SKILL.md +0 -19
  147. package/plugins/gemini/skills/darkharvest/SKILL.md +0 -19
  148. package/plugins/gemini/skills/epistemic_search/SKILL.md +0 -19
  149. package/plugins/gemini/skills/factory/SKILL.md +0 -19
  150. package/plugins/gemini/skills/grilling/SKILL.md +0 -19
  151. package/plugins/gemini/skills/oss_scout/SKILL.md +0 -19
  152. package/plugins/gemini/skills/research_cache/SKILL.md +0 -19
  153. package/plugins/gemini/skills/swarm_config/SKILL.md +0 -19
  154. package/plugins/opencode/analysis-bridge.js +0 -620
  155. package/plugins/opencode/config-io.js +0 -1651
  156. package/plugins/opencode/dep-health.js +0 -1039
  157. package/plugins/opencode/hook-bus.js +0 -496
  158. package/plugins/opencode/index.js +0 -2992
  159. package/plugins/opencode/tui.js +0 -1551
  160. package/plugins/opencode/weight-baseline.json +0 -16
  161. package/plugins/opencode/weight-signals.js +0 -769
  162. package/plugins/research-cache/.claude-plugin/plugin.json +0 -15
  163. package/plugins/research-cache/skills/research_cache/SKILL.md +0 -36
  164. package/plugins/research-cache/skills/research_cache/__init__.py +0 -0
  165. package/plugins/research-cache/skills/research_cache/hasher.py +0 -302
  166. package/plugins/socratic-grilling/.claude-plugin/plugin.json +0 -15
  167. package/plugins/socratic-grilling/skills/grilling/SKILL.md +0 -61
  168. package/plugins/socratic-grilling/skills/grilling/__init__.py +0 -0
  169. package/plugins/socratic-grilling/skills/grilling/socratic_tree.py +0 -363
  170. package/prompts/agent_alpha_thesis.md +0 -70
  171. package/prompts/agent_beta_antithesis.md +0 -78
  172. package/prompts/agent_brainstormer.md +0 -143
  173. package/prompts/agent_code_auditor.md +0 -88
  174. package/prompts/agent_darkharvest.md +0 -140
  175. package/prompts/agent_oss_scout.md +0 -78
  176. package/prompts/antigravity_repo_factcheck.md +0 -143
  177. package/prompts/base_epistemic_system.md +0 -50
  178. package/prompts/epistemic_auditor.md +0 -74
  179. package/prompts/orchestrator.md +0 -70
  180. package/runner/__init__.py +0 -0
  181. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  182. package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
  183. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  184. package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
  185. package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
  186. package/runner/__pycache__/darkharvest_claims.cpython-311.pyc +0 -0
  187. package/runner/__pycache__/errors.cpython-311.pyc +0 -0
  188. package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
  189. package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
  190. package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
  191. package/runner/__pycache__/path_safety.cpython-311.pyc +0 -0
  192. package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
  193. package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
  194. package/runner/__pycache__/preflight.cpython-311.pyc +0 -0
  195. package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
  196. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  197. package/runner/__pycache__/retrieval_log.cpython-311.pyc +0 -0
  198. package/runner/__pycache__/schema_validate.cpython-311.pyc +0 -0
  199. package/runner/__pycache__/schemas.cpython-311.pyc +0 -0
  200. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  201. package/runner/antigravity_hooks.py +0 -265
  202. package/runner/auctioneer.py +0 -169
  203. package/runner/auditor_engine.py +0 -711
  204. package/runner/claim_store.py +0 -417
  205. package/runner/claim_witness.py +0 -412
  206. package/runner/darkharvest_claims.py +0 -358
  207. package/runner/errors.py +0 -51
  208. package/runner/living_dossiers.py +0 -395
  209. package/runner/mcp_protocol.py +0 -188
  210. package/runner/mcp_server.py +0 -1447
  211. package/runner/path_safety.py +0 -62
  212. package/runner/pcrb.py +0 -290
  213. package/runner/pcrb_verify.py +0 -267
  214. package/runner/preflight.py +0 -1011
  215. package/runner/refinement.py +0 -580
  216. package/runner/research_swarm.py +0 -1848
  217. package/runner/retrieval_log.py +0 -90
  218. package/runner/schema_validate.py +0 -193
  219. package/runner/schemas.py +0 -630
  220. package/runner/state_machine.py +0 -463
  221. package/runner/tests/__pycache__/test_analysis_bridge.cpython-311.pyc +0 -0
  222. package/runner/tests/__pycache__/test_antigravity_plugin.cpython-311.pyc +0 -0
  223. package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
  224. package/runner/tests/__pycache__/test_backends.cpython-311.pyc +0 -0
  225. package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
  226. package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
  227. package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
  228. package/runner/tests/__pycache__/test_claude_plugin.cpython-311.pyc +0 -0
  229. package/runner/tests/__pycache__/test_config_schema.cpython-311.pyc +0 -0
  230. package/runner/tests/__pycache__/test_contract_tooling.cpython-311.pyc +0 -0
  231. package/runner/tests/__pycache__/test_darkharvest_audit.cpython-311.pyc +0 -0
  232. package/runner/tests/__pycache__/test_dep_health.cpython-311.pyc +0 -0
  233. package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
  234. package/runner/tests/__pycache__/test_dossier_contract.cpython-311.pyc +0 -0
  235. package/runner/tests/__pycache__/test_factory.cpython-311.pyc +0 -0
  236. package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
  237. package/runner/tests/__pycache__/test_frontier_concurrency.cpython-311.pyc +0 -0
  238. package/runner/tests/__pycache__/test_frontier_mirror_standalone.cpython-311.pyc +0 -0
  239. package/runner/tests/__pycache__/test_grounding.cpython-311.pyc +0 -0
  240. package/runner/tests/__pycache__/test_hardening_contract.cpython-311.pyc +0 -0
  241. package/runner/tests/__pycache__/test_hook_bus.cpython-311.pyc +0 -0
  242. package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
  243. package/runner/tests/__pycache__/test_manifest_concurrency.cpython-311.pyc +0 -0
  244. package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
  245. package/runner/tests/__pycache__/test_nk_hardening.cpython-311.pyc +0 -0
  246. package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
  247. package/runner/tests/__pycache__/test_opencode_v2_registrars.cpython-311.pyc +0 -0
  248. package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
  249. package/runner/tests/__pycache__/test_phase08_waiver_reopens.cpython-311.pyc +0 -0
  250. package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
  251. package/runner/tests/__pycache__/test_search_anomaly.cpython-311.pyc +0 -0
  252. package/runner/tests/__pycache__/test_search_provider.cpython-311.pyc +0 -0
  253. package/runner/tests/__pycache__/test_search_provider_plugin.cpython-311.pyc +0 -0
  254. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  255. package/runner/tests/__pycache__/test_swarm_config.cpython-311.pyc +0 -0
  256. package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
  257. package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
  258. package/runner/tests/__pycache__/test_weight_signals.cpython-311.pyc +0 -0
  259. package/runner/tests/fixtures/auction_objective.json +0 -35
  260. package/runner/tests/fixtures/borderline_claims.json +0 -27
  261. package/runner/tests/fixtures/config_validation_cases.json +0 -276
  262. package/runner/tests/fixtures/ddg_anomaly_page.html +0 -176
  263. package/runner/tests/fixtures/ddg_results_page.html +0 -28
  264. package/runner/tests/fixtures/divergence_objectives.json +0 -31
  265. package/runner/tests/test_analysis_bridge.py +0 -450
  266. package/runner/tests/test_antigravity_plugin.py +0 -361
  267. package/runner/tests/test_auction_order.py +0 -173
  268. package/runner/tests/test_backends.py +0 -457
  269. package/runner/tests/test_bet1_spike.py +0 -95
  270. package/runner/tests/test_claim_store.py +0 -162
  271. package/runner/tests/test_claim_witness.py +0 -186
  272. package/runner/tests/test_claude_plugin.py +0 -170
  273. package/runner/tests/test_config_schema.py +0 -879
  274. package/runner/tests/test_contract_tooling.py +0 -312
  275. package/runner/tests/test_darkharvest_audit.py +0 -395
  276. package/runner/tests/test_dep_health.py +0 -595
  277. package/runner/tests/test_domain_packs.py +0 -217
  278. package/runner/tests/test_dossier_contract.py +0 -196
  279. package/runner/tests/test_factory.py +0 -258
  280. package/runner/tests/test_fleet_seam.py +0 -113
  281. package/runner/tests/test_frontier_concurrency.py +0 -288
  282. package/runner/tests/test_frontier_mirror_standalone.py +0 -156
  283. package/runner/tests/test_grounding.py +0 -183
  284. package/runner/tests/test_hardening_contract.py +0 -391
  285. package/runner/tests/test_hook_bus.py +0 -956
  286. package/runner/tests/test_living_dossiers.py +0 -204
  287. package/runner/tests/test_manifest_concurrency.py +0 -187
  288. package/runner/tests/test_mcp_server.py +0 -257
  289. package/runner/tests/test_nk_hardening.py +0 -1046
  290. package/runner/tests/test_opencode_ux.py +0 -2438
  291. package/runner/tests/test_opencode_v2_registrars.py +0 -2157
  292. package/runner/tests/test_pcrb.py +0 -240
  293. package/runner/tests/test_phase08_waiver_reopens.py +0 -539
  294. package/runner/tests/test_refinement.py +0 -255
  295. package/runner/tests/test_search_anomaly.py +0 -342
  296. package/runner/tests/test_search_provider.py +0 -1534
  297. package/runner/tests/test_search_provider_plugin.py +0 -401
  298. package/runner/tests/test_swarm.py +0 -444
  299. package/runner/tests/test_swarm_config.py +0 -520
  300. package/runner/tests/test_sweep_regressions.py +0 -453
  301. package/runner/tests/test_webcache.py +0 -271
  302. package/runner/tests/test_weight_signals.py +0 -401
  303. package/schemas/alpha_dossier.schema.json +0 -204
  304. package/schemas/audit_report.schema.json +0 -79
  305. package/schemas/beta_dossier.schema.json +0 -214
  306. package/schemas/config.schema.json +0 -311
  307. package/schemas/frontier.schema.json +0 -61
  308. package/schemas/manifest.schema.json +0 -49
  309. package/schemas/scope_manifest.schema.json +0 -47
  310. package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
  311. package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
  312. package/scripts/__pycache__/check_docs.cpython-311.pyc +0 -0
  313. package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
  314. package/scripts/__pycache__/gen_schemas.cpython-311.pyc +0 -0
  315. package/scripts/auction_experiment.py +0 -188
  316. package/scripts/bet1_advisory_spike.py +0 -442
  317. package/scripts/build_adapters.py +0 -417
  318. package/scripts/check_docs.py +0 -66
  319. package/scripts/divergence_experiment.py +0 -214
  320. package/scripts/gen_schemas.py +0 -69
  321. package/skills/brainstorming/SKILL.md +0 -106
  322. package/skills/brainstorming/__init__.py +0 -1
  323. package/skills/brainstorming/scripts/brainstorm.py +0 -201
  324. package/skills/code_audit/SKILL.md +0 -45
  325. package/skills/darkharvest/SKILL.md +0 -70
  326. package/skills/darkharvest/scripts/harvest.py +0 -289
  327. package/skills/epistemic_search/SKILL.md +0 -67
  328. package/skills/epistemic_search/__init__.py +0 -1
  329. package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
  330. package/skills/epistemic_search/scripts/fetch.py +0 -156
  331. package/skills/epistemic_search/scripts/search.py +0 -551
  332. package/skills/epistemic_search/scripts/webcache.py +0 -339
  333. package/skills/factory/SKILL.md +0 -61
  334. package/skills/factory/scripts/factory.py +0 -300
  335. package/skills/grilling/SKILL.md +0 -61
  336. package/skills/grilling/__init__.py +0 -0
  337. package/skills/grilling/__pycache__/__init__.cpython-311.pyc +0 -0
  338. package/skills/grilling/__pycache__/socratic_tree.cpython-311.pyc +0 -0
  339. package/skills/grilling/socratic_tree.py +0 -363
  340. package/skills/oss_scout/SKILL.md +0 -49
  341. package/skills/research_cache/SKILL.md +0 -36
  342. package/skills/research_cache/__init__.py +0 -0
  343. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  344. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  345. package/skills/research_cache/hasher.py +0 -302
  346. package/skills/swarm_config/SKILL.md +0 -178
  347. package/skills/swarm_config/__init__.py +0 -35
  348. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  349. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
  350. package/skills/swarm_config/configure.py +0 -1002
@@ -1,551 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Zero-API-Key Web Search Engine for IUMBTEMS.
4
- DuckDuckGo HTML/lite parser with domain filtering and Claude Code XML formatting.
5
- """
6
-
7
- import json
8
- import os
9
- import re
10
- import sys
11
- import urllib.parse
12
- import urllib.request
13
- from html.parser import HTMLParser
14
- from typing import Any, Dict, List, Optional, Tuple
15
- from xml.sax.saxutils import escape as xml_escape
16
-
17
- USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
18
-
19
- # ---------------------------------------------------------------------------
20
- # DuckDuckGo anti-bot ("anomaly") detection
21
- # ---------------------------------------------------------------------------
22
- #
23
- # DuckDuckGo Lite answers suspected automation with an HTTP-2xx image-selection
24
- # CAPTCHA instead of results. The page is *not* empty, but it carries no
25
- # ``result-link`` rows, so the parser yielded ``[]`` and callers could not tell
26
- # a block apart from a legitimate no-hits query (they were swallowed as
27
- # ``results: 0`` in ``.research/retrieval.jsonl``).
28
- #
29
- # We deliberately do NOT key off the bare word "anomaly": a normal result
30
- # snippet may legitimately contain it — that is the false positive this phase
31
- # guards against. Detection is anchored to page chrome that only the
32
- # interstitial emits, captured verbatim in the recorded fixture
33
- # ``runner/tests/fixtures/ddg_anomaly_page.html`` (14,213 bytes, HTTP 202):
34
- #
35
- # * ``id="challenge-form"`` — CAPTCHA submit form
36
- # * ``id="img-form"`` — challenge image form
37
- # * ``assets/anomaly/images/challenge/`` — challenge image asset path
38
- # * ``anomaly-modal`` — challenge modal CSS class
39
- #
40
- # Detection additionally requires the page to be free of real ``result-link``
41
- # anchors, so even an (implausible) results page whose snippet embeds one of the
42
- # markers is still treated as a results page.
43
- DDG_ANOMALY_MARKERS: Tuple[str, ...] = (
44
- 'id="challenge-form"',
45
- 'id="img-form"',
46
- "assets/anomaly/images/challenge/",
47
- "anomaly-modal",
48
- )
49
-
50
- DDG_BLOCKED_HINT = (
51
- "DuckDuckGo served its anti-bot challenge page (BLOCKED), not results. "
52
- "Back off and retry later, or fall back to another engine: set "
53
- "search_engine to firecrawl or searxng in .research/config.json, or "
54
- "export a BYO key (EXA_API_KEY / FIRECRAWL_API_KEY / PARALLEL_API_KEY / "
55
- "TAVILY_API_KEY / TINYFISH_API_KEY)."
56
- )
57
-
58
- DDG_ERROR_HINT = (
59
- "DuckDuckGo request failed (network/HTTP error). Retry with backoff or "
60
- "fall back to firecrawl or searxng."
61
- )
62
-
63
-
64
- class SearchBlocked(RuntimeError):
65
- """DuckDuckGo served its anti-bot interstitial instead of results.
66
-
67
- Exposed for callers that want the block to be an error rather than an empty
68
- list — see :func:`require_search_duckduckgo`. ``hint`` carries the
69
- actionable fallback/backoff advice.
70
- """
71
-
72
- def __init__(
73
- self, message: str = DDG_BLOCKED_HINT, hint: str = DDG_BLOCKED_HINT
74
- ) -> None:
75
- super().__init__(message)
76
- self.hint = hint
77
-
78
-
79
- def _is_ddg_anomaly_page(html: str) -> bool:
80
- """True iff *html* is DDG's anti-bot interstitial rather than results.
81
-
82
- Anchored to structural page chrome (see the module comment above), never to
83
- the bare word "anomaly". An empty body or a page already carrying
84
- ``result-link`` anchors is a results page and is never flagged.
85
- """
86
- if not html:
87
- return False
88
- if "result-link" in html:
89
- return False
90
- return any(marker in html for marker in DDG_ANOMALY_MARKERS)
91
-
92
-
93
- def _log_retrieval(kind: str, **fields) -> None:
94
- """Best-effort retrieval telemetry (see runner/retrieval_log.py)."""
95
- try:
96
- from pathlib import Path as _Path
97
-
98
- root = _Path(__file__).resolve().parents[3]
99
- if str(root) not in sys.path:
100
- sys.path.insert(0, str(root))
101
- from runner.retrieval_log import default_base_dir, log_event
102
-
103
- log_event(default_base_dir(), kind, **fields)
104
- except Exception:
105
- pass
106
-
107
-
108
- def _log_ddg_query(
109
- status: str, effective_query: str, results: int, log: bool = True
110
- ) -> None:
111
- """Record a query event whose ``status`` distinguishes block from empty.
112
-
113
- The plain ``results`` count is kept so ``retrieval_log.summarize`` still
114
- works, but a block additionally carries ``status: "blocked"`` and
115
- ``blocked: true`` so the anomaly can no longer masquerade as a normal
116
- zero-hit query.
117
-
118
- ``log=False`` suppresses telemetry entirely. The preflight/reachability
119
- probe uses it so its own request is never counted as an agent search and
120
- never leaks an event into whichever workspace happens to be the process
121
- cwd (see ``runner/preflight.py:_engine_probe``).
122
- """
123
- if not log:
124
- return
125
- fields: Dict[str, Any] = {
126
- "query": effective_query,
127
- "results": results,
128
- "status": status,
129
- "provider": "duckduckgo",
130
- }
131
- if status == "blocked":
132
- fields["blocked"] = True
133
- _log_retrieval("query", **fields)
134
-
135
-
136
- class DDGLiteParser(HTMLParser):
137
- """Linear, non-backtracking HTML parser for DuckDuckGo Lite search results.
138
-
139
- DuckDuckGo Lite emits one table row per result: an ``<a class="result-link">``
140
- holding the title and a ``<td class="result-snippet">`` holding the summary.
141
- Results are assembled **per row** so that:
142
-
143
- * a missing link or snippet cannot desynchronise every later result (the old
144
- ``zip(links, snippets)`` pairing shifted all subsequent rows), and
145
- * nested markup inside a snippet — including a nested
146
- ``<a class="result-link">`` — never hijacks capture state (the old parser
147
- reset its buffer mid-snippet and then never recorded the snippet at all).
148
-
149
- Captured text is stored decoded (``convert_charrefs`` default); escaping is
150
- done at emit time by :func:`format_xml`.
151
- """
152
-
153
- def __init__(self):
154
- super().__init__()
155
- self.results: List[Dict[str, str]] = []
156
- self._row_href: Optional[str] = None
157
- self._row_title: List[str] = []
158
- self._row_snippet: List[str] = []
159
- self._capture: Optional[str] = None # "title" | "snippet" | None
160
- self._depth = 0 # nesting depth of the tag that opened the capture
161
-
162
- def handle_starttag(self, tag: str, attrs: List[Tuple[str, Optional[str]]]):
163
- attr_dict = {k: v or "" for k, v in attrs}
164
- classes = attr_dict.get("class", "").split()
165
-
166
- if tag == "tr":
167
- self.flush()
168
- return
169
-
170
- # While capturing a snippet, nested markup (including a nested
171
- # result-link) stays inside the snippet. Only track nested <td> so the
172
- # capture still ends at the *outer* </td>.
173
- if self._capture == "snippet":
174
- if tag == "td":
175
- self._depth += 1
176
- return
177
-
178
- # Likewise for a title: a nested <a> must not close the capture early.
179
- if self._capture == "title":
180
- if tag == "a":
181
- self._depth += 1
182
- return
183
-
184
- if tag == "a" and "result-link" in classes:
185
- # Row-less fallback: a new result-link implies the previous result
186
- # is complete even if no <tr> ever bounded it.
187
- if self._row_href is not None:
188
- self.flush()
189
- self._capture = "title"
190
- self._depth = 1
191
- self._row_href = attr_dict.get("href", "")
192
- self._row_title = []
193
- elif tag == "td" and "result-snippet" in classes:
194
- self._capture = "snippet"
195
- self._depth = 1
196
- self._row_snippet = []
197
-
198
- def handle_endtag(self, tag: str):
199
- if (self._capture == "title" and tag == "a") or (
200
- self._capture == "snippet" and tag == "td"
201
- ):
202
- self._depth -= 1
203
- if self._depth <= 0:
204
- self._capture = None
205
- elif tag == "tr":
206
- self.flush()
207
-
208
- def handle_data(self, data: str):
209
- if self._capture == "title":
210
- self._row_title.append(data)
211
- elif self._capture == "snippet":
212
- self._row_snippet.append(data)
213
-
214
- def flush(self):
215
- """Emit the in-progress row, if any. Call after ``feed()`` completes."""
216
- if self._row_href is not None:
217
- self.results.append(
218
- {
219
- "href": self._row_href,
220
- "title": "".join(self._row_title).strip(),
221
- "snippet": "".join(self._row_snippet).strip(),
222
- }
223
- )
224
- self._row_href = None
225
- self._row_title = []
226
- self._row_snippet = []
227
- self._capture = None
228
- self._depth = 0
229
-
230
-
231
- def _build_search_query(
232
- query: str,
233
- allowed_domains: Optional[List[str]] = None,
234
- blocked_domains: Optional[List[str]] = None,
235
- ) -> str:
236
- effective_query = query.strip()
237
- if allowed_domains:
238
- effective_query += " " + " OR ".join(f"site:{d}" for d in allowed_domains)
239
- if blocked_domains:
240
- effective_query += " " + " ".join(f"-site:{d}" for d in blocked_domains)
241
- return effective_query
242
-
243
-
244
- def _env_search_timeout(default: float = 15.0) -> float:
245
- """Search HTTP timeout: `IUMBTEMS_SEARCH_TIMEOUT_S` env > built-in default.
246
-
247
- The runner exports the env var from config `search_timeout_s` to spawned
248
- children; unset/invalid keeps the historical 15s default.
249
- """
250
- raw = os.environ.get("IUMBTEMS_SEARCH_TIMEOUT_S", "").strip()
251
- if not raw:
252
- return default
253
- try:
254
- value = float(raw)
255
- except ValueError:
256
- return default
257
- return value if value > 0 else default
258
-
259
-
260
- def _fetch_ddg_html(effective_query: str, timeout: float = 15.0) -> str:
261
- url = "https://lite.duckduckgo.com/lite/"
262
- data = urllib.parse.urlencode({"q": effective_query}).encode("utf-8")
263
- headers = {
264
- "User-Agent": USER_AGENT,
265
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
266
- "Accept-Language": "en-US,en;q=0.5",
267
- "Referer": "https://lite.duckduckgo.com/",
268
- }
269
- req = urllib.request.Request(url, data=data, headers=headers)
270
- try:
271
- with urllib.request.urlopen(req, timeout=timeout) as resp:
272
- return resp.read().decode("utf-8", errors="replace")
273
- except Exception as e:
274
- sys.stderr.write(
275
- f"[epistemic-search] Network error fetching search results: {e}\n"
276
- )
277
- return ""
278
-
279
-
280
- def _parse_clean_url(href: str) -> str:
281
- m = re.search(r"uddg=([^&]+)", href)
282
- if m:
283
- return urllib.parse.unquote(m.group(1))
284
- return href
285
-
286
-
287
- def _is_domain_allowed(
288
- hostname: str,
289
- allowed_domains: Optional[List[str]] = None,
290
- blocked_domains: Optional[List[str]] = None,
291
- ) -> bool:
292
- if allowed_domains and not any(
293
- hostname == d or hostname.endswith("." + d) for d in allowed_domains
294
- ):
295
- return False
296
- if blocked_domains and any(
297
- hostname == d or hostname.endswith("." + d) for d in blocked_domains
298
- ):
299
- return False
300
- return True
301
-
302
-
303
- def _parse_ddg_results(
304
- html: str,
305
- allowed_domains: Optional[List[str]] = None,
306
- blocked_domains: Optional[List[str]] = None,
307
- max_results: int = 10,
308
- ) -> List[Dict[str, str]]:
309
- parser = DDGLiteParser()
310
- parser.feed(html)
311
- parser.flush()
312
-
313
- results = []
314
- for row in parser.results:
315
- clean_url = _parse_clean_url(row["href"])
316
- if not clean_url.startswith("http"):
317
- continue
318
-
319
- parsed_url = urllib.parse.urlparse(clean_url)
320
- hostname = parsed_url.hostname or ""
321
- if not _is_domain_allowed(hostname, allowed_domains, blocked_domains):
322
- continue
323
-
324
- results.append(
325
- {
326
- "title": row["title"],
327
- "url": clean_url,
328
- "snippet": row["snippet"],
329
- }
330
- )
331
- if len(results) >= max_results:
332
- break
333
-
334
- return results
335
-
336
-
337
- def search_duckduckgo_detailed(
338
- query: str,
339
- allowed_domains: Optional[List[str]] = None,
340
- blocked_domains: Optional[List[str]] = None,
341
- max_results: int = 10,
342
- log: bool = True,
343
- timeout: Optional[float] = None,
344
- ) -> Dict[str, Any]:
345
- """Search DuckDuckGo Lite and report *why* a run produced no results.
346
-
347
- Returns a typed dict::
348
-
349
- {"status": "ok" | "empty" | "blocked" | "error",
350
- "results": [...], "hint": "...", "query": "<effective query>"}
351
-
352
- ``"blocked"`` is DDG's anti-bot interstitial (an actionable hint, not an
353
- empty success). ``"empty"`` is a genuine no-hits query. ``"error"`` is a
354
- network/HTTP failure. Only this function preserves the distinction;
355
- :func:`search_duckduckgo` keeps the legacy list contract for callers that
356
- do not need it. ``timeout`` bounds the HTTP fetch; when omitted (None) the
357
- `IUMBTEMS_SEARCH_TIMEOUT_S` env override (config `search_timeout_s`) is
358
- honored, else the historical 15s default. An explicit value always wins —
359
- the preflight probe wires its own bound through here.
360
- """
361
- if timeout is None:
362
- timeout = _env_search_timeout()
363
- effective_query = _build_search_query(query, allowed_domains, blocked_domains)
364
- try:
365
- html = _fetch_ddg_html(effective_query, timeout=timeout)
366
- except TypeError:
367
- # An injected/older ``_fetch_ddg_html`` that takes only the query.
368
- html = _fetch_ddg_html(effective_query)
369
-
370
- if not html:
371
- results: List[Dict[str, str]] = []
372
- status = "error"
373
- hint = DDG_ERROR_HINT
374
- elif _is_ddg_anomaly_page(html):
375
- results = []
376
- status = "blocked"
377
- hint = DDG_BLOCKED_HINT
378
- else:
379
- results = _parse_ddg_results(
380
- html, allowed_domains, blocked_domains, max_results
381
- )
382
- status = "ok" if results else "empty"
383
- hint = "" if results else "No results parsed for this query."
384
-
385
- _log_ddg_query(status, effective_query, len(results), log=log)
386
- return {
387
- "status": status,
388
- "results": results,
389
- "hint": hint,
390
- "query": effective_query,
391
- }
392
-
393
-
394
- def search_duckduckgo(
395
- query: str,
396
- allowed_domains: Optional[List[str]] = None,
397
- blocked_domains: Optional[List[str]] = None,
398
- max_results: int = 10,
399
- log: bool = True,
400
- timeout: Optional[float] = None,
401
- ) -> List[Dict[str, str]]:
402
- """Execute search query against DuckDuckGo Lite without API keys.
403
-
404
- Preserves the original list contract for callers that only want results.
405
- When DDG serves its anti-bot interstitial this returns ``[]`` *and* warns on
406
- stderr; callers that must act on the block should use
407
- :func:`search_duckduckgo_detailed` or :func:`require_search_duckduckgo`.
408
- """
409
- outcome = search_duckduckgo_detailed(
410
- query, allowed_domains, blocked_domains, max_results, log=log, timeout=timeout
411
- )
412
- if outcome["status"] == "blocked":
413
- sys.stderr.write(f"[epistemic-search] BLOCKED: {outcome['hint']}\n")
414
- return outcome["results"]
415
-
416
-
417
- def require_search_duckduckgo(
418
- query: str,
419
- allowed_domains: Optional[List[str]] = None,
420
- blocked_domains: Optional[List[str]] = None,
421
- max_results: int = 10,
422
- log: bool = True,
423
- timeout: Optional[float] = None,
424
- ) -> List[Dict[str, str]]:
425
- """Like :func:`search_duckduckgo` but raises :class:`SearchBlocked`.
426
-
427
- Use when an anti-bot page is an error condition (fall back to another
428
- engine, then retry) rather than an empty success.
429
- """
430
- outcome = search_duckduckgo_detailed(
431
- query, allowed_domains, blocked_domains, max_results, log=log, timeout=timeout
432
- )
433
- if outcome["status"] == "blocked":
434
- raise SearchBlocked(outcome["hint"], hint=outcome["hint"])
435
- return outcome["results"]
436
-
437
-
438
- def format_xml(results: List[Dict[str, str]]) -> str:
439
- """Format results as Claude Code <search_results> XML.
440
-
441
- Every field is XML-escaped at emit time. The parser stores decoded text, so
442
- without this a snippet like ``A &amp; B`` becomes ``A & B`` and produces
443
- malformed markup (bare ``&``); URLs containing query strings have the same
444
- problem.
445
- """
446
- lines = ["<search_results>"]
447
- for r in results:
448
- lines.append(" <result>")
449
- lines.append(f" <title>{xml_escape(r['title'])}</title>")
450
- lines.append(f" <url>{xml_escape(r['url'])}</url>")
451
- lines.append(f" <snippet>{xml_escape(r['snippet'])}</snippet>")
452
- lines.append(" </result>")
453
- lines.append("</search_results>")
454
- return "\n".join(lines)
455
-
456
-
457
- def _read_stdin_payload() -> Tuple[str, Optional[List[str]], Optional[List[str]]]:
458
- if sys.stdin.isatty():
459
- return "", None, None
460
- try:
461
- data = sys.stdin.read().strip()
462
- if not data:
463
- return "", None, None
464
- try:
465
- payload = json.loads(data)
466
- return (
467
- payload.get("query", ""),
468
- payload.get("allowed_domains"),
469
- payload.get("blocked_domains"),
470
- )
471
- except json.JSONDecodeError:
472
- return data, None, None
473
- except Exception:
474
- return "", None, None
475
-
476
-
477
- def _parse_cli_args(
478
- args: List[str],
479
- default_query: str = "",
480
- default_allowed: Optional[List[str]] = None,
481
- default_blocked: Optional[List[str]] = None,
482
- ) -> Tuple[str, Optional[List[str]], Optional[List[str]], bool, bool]:
483
- query = default_query
484
- allowed = default_allowed
485
- blocked = default_blocked
486
- output_json = False
487
- output_status = False
488
- idx = 0
489
- while idx < len(args):
490
- arg = args[idx]
491
- if arg == "--json":
492
- output_json = True
493
- elif arg == "--status":
494
- output_status = True
495
- elif arg == "--allowed-domain" and idx + 1 < len(args):
496
- allowed = (allowed or []) + [args[idx + 1]]
497
- idx += 1
498
- elif arg == "--blocked-domain" and idx + 1 < len(args):
499
- blocked = (blocked or []) + [args[idx + 1]]
500
- idx += 1
501
- elif not query and not arg.startswith("--"):
502
- query = arg
503
- idx += 1
504
- return query, allowed, blocked, output_json, output_status
505
-
506
-
507
- def main():
508
- std_query, std_allowed, std_blocked = _read_stdin_payload()
509
- query, allowed, blocked, output_json, output_status = _parse_cli_args(
510
- sys.argv[1:], std_query, std_allowed, std_blocked
511
- )
512
-
513
- if not query:
514
- sys.stderr.write(
515
- 'Usage: search.py [options] <query>\nOr pipe JSON: echo \'{"query":"..."}\' | search.py\n'
516
- "Options: --json (results as JSON), --status (typed status + hint),\n"
517
- " --allowed-domain D, --blocked-domain D\n"
518
- )
519
- sys.exit(1)
520
-
521
- outcome = search_duckduckgo_detailed(
522
- query=query,
523
- allowed_domains=allowed,
524
- blocked_domains=blocked,
525
- )
526
-
527
- if outcome["status"] in ("blocked", "error"):
528
- sys.stderr.write(
529
- f"[epistemic-search] {outcome['status'].upper()}: {outcome['hint']}\n"
530
- )
531
-
532
- if output_status:
533
- print(
534
- json.dumps(
535
- {
536
- "status": outcome["status"],
537
- "query": outcome["query"],
538
- "results": len(outcome["results"]),
539
- "hint": outcome["hint"],
540
- },
541
- indent=2,
542
- )
543
- )
544
- elif output_json:
545
- print(json.dumps(outcome["results"], indent=2))
546
- else:
547
- print(format_xml(outcome["results"]))
548
-
549
-
550
- if __name__ == "__main__":
551
- main()