@heretek-ai/epistemic-swarm 0.7.19 → 0.7.21

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 (81) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +23 -2
  3. package/package.json +1 -1
  4. package/plugins/antigravity/plugin.json +1 -1
  5. package/plugins/antigravity/rules/AGENTS.md +5 -3
  6. package/plugins/opencode/config-io.js +55 -12
  7. package/plugins/opencode/index.js +611 -0
  8. package/plugins/opencode/tui.js +278 -6
  9. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  10. package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
  11. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  12. package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
  13. package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
  14. package/runner/__pycache__/darkharvest_claims.cpython-311.pyc +0 -0
  15. package/runner/__pycache__/errors.cpython-311.pyc +0 -0
  16. package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
  17. package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
  18. package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
  19. package/runner/__pycache__/path_safety.cpython-311.pyc +0 -0
  20. package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
  21. package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
  22. package/runner/__pycache__/preflight.cpython-311.pyc +0 -0
  23. package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
  24. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  25. package/runner/__pycache__/retrieval_log.cpython-311.pyc +0 -0
  26. package/runner/__pycache__/schema_validate.cpython-311.pyc +0 -0
  27. package/runner/__pycache__/schemas.cpython-311.pyc +0 -0
  28. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  29. package/runner/mcp_server.py +81 -10
  30. package/runner/research_swarm.py +12 -2
  31. package/runner/schemas.py +18 -0
  32. package/runner/tests/__pycache__/test_antigravity_plugin.cpython-311.pyc +0 -0
  33. package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
  34. package/runner/tests/__pycache__/test_backends.cpython-311.pyc +0 -0
  35. package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
  36. package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
  37. package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
  38. package/runner/tests/__pycache__/test_claude_plugin.cpython-311.pyc +0 -0
  39. package/runner/tests/__pycache__/test_config_schema.cpython-311.pyc +0 -0
  40. package/runner/tests/__pycache__/test_contract_tooling.cpython-311.pyc +0 -0
  41. package/runner/tests/__pycache__/test_darkharvest_audit.cpython-311.pyc +0 -0
  42. package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
  43. package/runner/tests/__pycache__/test_dossier_contract.cpython-311.pyc +0 -0
  44. package/runner/tests/__pycache__/test_factory.cpython-311.pyc +0 -0
  45. package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
  46. package/runner/tests/__pycache__/test_frontier_concurrency.cpython-311.pyc +0 -0
  47. package/runner/tests/__pycache__/test_frontier_mirror_standalone.cpython-311.pyc +0 -0
  48. package/runner/tests/__pycache__/test_grounding.cpython-311.pyc +0 -0
  49. package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
  50. package/runner/tests/__pycache__/test_manifest_concurrency.cpython-311.pyc +0 -0
  51. package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
  52. package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
  53. package/runner/tests/__pycache__/test_opencode_v2_registrars.cpython-311.pyc +0 -0
  54. package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
  55. package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
  56. package/runner/tests/__pycache__/test_search_anomaly.cpython-311.pyc +0 -0
  57. package/runner/tests/__pycache__/test_search_provider.cpython-311.pyc +0 -0
  58. package/runner/tests/__pycache__/test_search_provider_plugin.cpython-311.pyc +0 -0
  59. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  60. package/runner/tests/__pycache__/test_swarm_config.cpython-311.pyc +0 -0
  61. package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
  62. package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
  63. package/runner/tests/test_config_schema.py +106 -0
  64. package/runner/tests/test_opencode_ux.py +462 -7
  65. package/runner/tests/test_opencode_v2_registrars.py +1112 -3
  66. package/runner/tests/test_swarm_config.py +21 -0
  67. package/schemas/config.schema.json +11 -0
  68. package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
  69. package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
  70. package/scripts/__pycache__/check_docs.cpython-311.pyc +0 -0
  71. package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
  72. package/scripts/__pycache__/gen_schemas.cpython-311.pyc +0 -0
  73. package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
  74. package/skills/grilling/__pycache__/__init__.cpython-311.pyc +0 -0
  75. package/skills/grilling/__pycache__/socratic_tree.cpython-311.pyc +0 -0
  76. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  77. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  78. package/skills/swarm_config/SKILL.md +26 -0
  79. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  80. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
  81. package/skills/swarm_config/configure.py +40 -6
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epistemic-swarm",
3
- "version": "0.7.19",
3
+ "version": "0.7.21",
4
4
  "description": "High-Integrity Dialectic Research Agent Harness for Claude Code enforcing empirical evidence over parametric hallucination.",
5
5
  "author": {
6
6
  "name": "Heretek AI",
package/README.md CHANGED
@@ -157,7 +157,7 @@ Related environment variables:
157
157
 
158
158
  Spawned agents inherit `PWD` set to their working directory, so harnesses that resolve their project root from `$PWD` (OpenCode) land in the same tree the runner reads from.
159
159
 
160
- > **Legacy config migration:** configs written before 0.7.6 could pin `agents.<role>.backend = ["claude", "-p"]`, which silently overrode the host-native default. That pin is now migrated to `null` on load (only when the top-level `backend` is not an explicit `"claude"`), and the migration is persisted by `iumbtems config`. To deliberately keep Claude on an OpenCode host, set `"backend": "claude"`.
160
+ > **Legacy config migration:** configs written before 0.7.6 could pin `agents.<role>.backend = ["claude", "-p"]`, which silently overrode the host-native default. That pin is now migrated to `null` on load (only when the top-level `backend` is not an explicit `"claude"`), and the migration is persisted on the next config **write** (a read / `--show` never heals). To deliberately keep Claude on an OpenCode host, set `"backend": "claude"`.
161
161
 
162
162
  ---
163
163
 
@@ -265,6 +265,27 @@ Nine canonical skills live in `skills/*/SKILL.md`. Each maps to a slash command
265
265
 
266
266
  ---
267
267
 
268
+ ## ⚙️ Settings Surface (`/swarm-config`)
269
+
270
+ The settings surface is the recommended way to tune the harness from inside OpenCode V2. It has two parts:
271
+
272
+ - **`/swarm-config` wizard** — a native dialog wizard (`ui.dialog.select/prompt/confirm`) showing every canonical key with a provenance badge (`default` / `file` / `env-override`), one editable row at a time, validated and written atomically on save.
273
+ - **Status panel** — a read-only panel opened from the command palette / keymap (`iumbtems.swarm-settings`) rendering the effective rows with a **next-run** badge. It never writes.
274
+
275
+ The canonical config it edits is **`.research/config.json`** — the TUI resolves `.research` from the OpenCode host root; `IUMBTEMS_PROJECT_DIR` / `--dir` / `base_dir` select the workspace for the CLI, MCP, and runner surfaces. Configurable values include the search engine, depth (`max_iterations`), operating `mode`, `backend`, per-role (`agents.*`) backend/model, `license_whitelist`, the cache / TTL / search-timeout knobs, `searxng_url`, `verify.min_fuzzy_confidence`, the persistence `mcp_servers` toggle map, `allocation`, `domain_pack`, and the deprecated display-only `output_dir`. (`divergence_threshold` is advisory: recorded, never gating a verdict.)
276
+
277
+ **Honest boundaries**
278
+
279
+ - **Values apply to the NEXT swarm run.** The Python runner reads `.research/config.json` once at run start, so a save never hot-swaps a running swarm — the wizard and panel both say "applies to the next swarm run".
280
+ - **Per-role temperature does not govern spawned agents.** The OpenCode session `context` hook honors `IUMBTEMS_TEMPERATURE[_<ROLE>]` for sessions running inside OpenCode, but there is **no verified path** that sets per-role temperature for spawned swarm agents — that leg is recorded `[NEGATIVE_KNOWLEDGE]` (S3, dated 2026-09-29; waiver W3). The UI deliberately makes no temperature claim.
281
+ - **No secrets in config.** API keys stay in the environment (`FIRECRAWL_API_KEY`, …). The surface only reports whether an env override is set for the keys it models; it never renders an env value, and it does not probe engine availability.
282
+
283
+ Writes prefer the plugin's `iumbtems.settings` RPC (server-side validation + an `expectedHash` lost-update guard; the RPC surfaces a `conflict` error type, and Python's code is `CONFLICTING_EXPECTED_HASH`) and degrade to a direct-fs write with a visible toast when `client.rpc` is absent; `.research/config.json` stays the single source of truth. See [docs/SYSTEM_ARCHITECTURE.md](docs/SYSTEM_ARCHITECTURE.md) §6.7 for the full data flow, the atomic-write + lock protocol, and the `ctx.storage` mirror.
284
+
285
+ *Reviewed settings-surface patterns from Gemini CLI, Codex, Goose, Aider, Cline, OpenCode, and Crush (docs-only) informed this design; no code was copied.*
286
+
287
+ ---
288
+
268
289
  ## 🗂️ Workspace & Evidence Model
269
290
 
270
291
  All state lives in a local `.research/` directory (resolved from `IUMBTEMS_PROJECT_DIR` when set, else the process cwd):
@@ -366,7 +387,7 @@ The package is distributed on npm as `@heretek-ai/epistemic-swarm` using [npm Tr
366
387
 
367
388
  **Release flow** (see [AGENTS.md](AGENTS.md#releases)):
368
389
 
369
- 1. Bump `package.json` **and** `.claude-plugin/plugin.json` **and** the lockfile in lockstep.
390
+ 1. Bump the **four enforced manifests** in lockstep: `package.json`, `package-lock.json`, `.claude-plugin/plugin.json`, and `plugins/antigravity/plugin.json` (`python3 scripts/build_adapters.py --check` guards the AntiGravity one).
370
391
  2. Commit and push to `main`.
371
392
  3. `gh release create vX.Y.Z` — the `Publish to npm` workflow
372
393
  (`.github/workflows/publish.yml`) runs on `release: published`, executes the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heretek-ai/epistemic-swarm",
3
- "version": "0.7.19",
3
+ "version": "0.7.21",
4
4
  "description": "IUMBTEMS (Epistemic Swarm) \u2014 High-Integrity Dialectic Research Agent Harness for Claude Code, OpenCode V2, Pi (pi.dev), OMP (oh-my-pi), Gemini CLI, Codex CLI, and AntiGravity",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://antigravity.google/schemas/v1/plugin.json",
3
3
  "name": "epistemic-swarm",
4
- "version": "0.7.19",
4
+ "version": "0.7.21",
5
5
  "description": "IUMBTEMS (Epistemic Swarm) \u2014 High-Integrity Dialectic Research, Architecture Auditing, Competitor Teardowns, and Socratic Planning for Antigravity.",
6
6
  "author": {
7
7
  "name": "Heretek AI",
@@ -109,9 +109,11 @@ or `[NEGATIVE_KNOWLEDGE: <query>]`. Never present parametric recall as verified.
109
109
  effect only after release + reinstall.
110
110
 
111
111
  ## Releases
112
- - Bump `package.json` (+ lockfile sync) **and `.claude-plugin/plugin.json`**
113
- (0.7.5 shipped with a stale plugin manifest — keep all three in lockstep),
114
- commit, push to `main`, then
112
+ - Bump the **four enforced manifests** in lockstep — `package.json`,
113
+ `package-lock.json`, `.claude-plugin/plugin.json`, and
114
+ `plugins/antigravity/plugin.json` (0.7.5 shipped a stale plugin manifest and
115
+ v0.7.18 missed the AntiGravity manifest, failing `Validate All Harnesses` +
116
+ `Publish to npm` twice). Then commit, push to `main`, then
115
117
  `gh release create vX.Y.Z`. The `Publish to npm` workflow (OIDC trusted
116
118
  publishing) runs on release-published. Verify with
117
119
  `curl https://registry.npmjs.org/@heretek-ai%2Fepistemic-swarm`
@@ -429,6 +429,7 @@ export const FALLBACK_DEFAULTS = {
429
429
  },
430
430
  opencode_auto: null,
431
431
  opencode_agent: null,
432
+ mcp_servers: {},
432
433
  };
433
434
 
434
435
  /** Effective defaults, derived from the schema `default` keywords. */
@@ -589,6 +590,58 @@ export function isConfigMalformed(baseDir = DEFAULT_RESEARCH_DIR) {
589
590
  }
590
591
  }
591
592
 
593
+ /**
594
+ * Single-read snapshot of the config file (phase-03 R5): the raw bytes are
595
+ * read EXACTLY ONCE; `hash` is computed over those bytes and `raw` is parsed
596
+ * from those bytes, so a config parsed from one era can never pair with a
597
+ * hash of another. `malformed` is true when bytes exist but are not a JSON
598
+ * object. Never throws, never writes, never creates directories.
599
+ */
600
+ export function readConfigSnapshot(baseDir = DEFAULT_RESEARCH_DIR) {
601
+ try {
602
+ const bytes = readBytes(getConfigPath(baseDir));
603
+ if (!bytes) return { hash: null, raw: null, malformed: false };
604
+ let hash = null;
605
+ try {
606
+ hash = createHash('sha256').update(bytes).digest('hex');
607
+ } catch {
608
+ hash = null;
609
+ }
610
+ const raw = parseJsonObject(bytes);
611
+ return { hash, raw, malformed: raw === null };
612
+ } catch {
613
+ return { hash: null, raw: null, malformed: false };
614
+ }
615
+ }
616
+
617
+ /**
618
+ * Effective config merged over defaults from an already-parsed raw object —
619
+ * the no-reread half of `loadConfig` (identical merge, migration, and
620
+ * fallback semantics; the caller owns the bytes). Never throws.
621
+ */
622
+ export function loadConfigFromRaw(raw) {
623
+ try {
624
+ const base = configDefaults();
625
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return base;
626
+ const merged = { ...base, ...deepCopy(raw) };
627
+ merged.agents = mergeAgents(base.agents, raw.agents);
628
+ if (
629
+ raw.verify && typeof raw.verify === 'object' && !Array.isArray(raw.verify) &&
630
+ base.verify && typeof base.verify === 'object' && !Array.isArray(base.verify)
631
+ ) {
632
+ merged.verify = { ...base.verify, ...deepCopy(raw.verify) };
633
+ }
634
+ migrateLegacyAgentBackends(merged);
635
+ return merged;
636
+ } catch {
637
+ try {
638
+ return deepCopy(FALLBACK_DEFAULTS);
639
+ } catch {
640
+ return {};
641
+ }
642
+ }
643
+ }
644
+
592
645
  /**
593
646
  * Load config merged over defaults. Pure — never writes. Applies the
594
647
  * legacy-pin migration in memory; unknown keys are preserved verbatim.
@@ -596,7 +649,6 @@ export function isConfigMalformed(baseDir = DEFAULT_RESEARCH_DIR) {
596
649
  */
597
650
  export function loadConfig(baseDir = DEFAULT_RESEARCH_DIR) {
598
651
  try {
599
- const base = configDefaults();
600
652
  const raw = readRawConfig(baseDir);
601
653
  if (!raw) {
602
654
  if (isConfigMalformed(baseDir)) {
@@ -609,18 +661,9 @@ export function loadConfig(baseDir = DEFAULT_RESEARCH_DIR) {
609
661
  /* logging must never throw */
610
662
  }
611
663
  }
612
- return base;
664
+ return configDefaults();
613
665
  }
614
- const merged = { ...base, ...deepCopy(raw) };
615
- merged.agents = mergeAgents(base.agents, raw.agents);
616
- if (
617
- raw.verify && typeof raw.verify === 'object' && !Array.isArray(raw.verify) &&
618
- base.verify && typeof base.verify === 'object' && !Array.isArray(base.verify)
619
- ) {
620
- merged.verify = { ...base.verify, ...deepCopy(raw.verify) };
621
- }
622
- migrateLegacyAgentBackends(merged);
623
- return merged;
666
+ return loadConfigFromRaw(raw);
624
667
  } catch {
625
668
  try {
626
669
  return deepCopy(FALLBACK_DEFAULTS);