@heretek-ai/epistemic-swarm 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/antigravity/plugin.json +1 -1
- package/plugins/gemini/gemini-extension.json +1 -1
- package/plugins/opencode/index.js +85 -8
- package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
- package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
- package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
- package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
- package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
- package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
- package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
- package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
- package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
- package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
- package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
- package/runner/tests/test_opencode_ux.py +124 -0
- package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
- package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heretek-ai/epistemic-swarm",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "IUMBTEMS: I Use My Brain To Express My Self — High-Integrity Dialectic Research Agent Harness for Claude Code, OpenCode V2, Pi, 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.4.
|
|
4
|
+
"version": "0.4.6",
|
|
5
5
|
"description": "IUMBTEMS Epistemic Swarm: dialectic research, code audit, OSS scout, and lateral brainstorming for AntiGravity.",
|
|
6
6
|
"author": "Heretek AI",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -563,6 +563,10 @@ export function buildCompactionContext(root) {
|
|
|
563
563
|
|
|
564
564
|
const NUDGE_MIN_INTERVAL_MS = 5 * 60 * 1000;
|
|
565
565
|
const activeNudges = new Map();
|
|
566
|
+
// Cleanup functions for the setup() disposer (a setup that returns a plain
|
|
567
|
+
// object crashes host reload: the host calls the return value as a cleanup
|
|
568
|
+
// function). All setup-owned resources register here.
|
|
569
|
+
const setupCleanups = new Set();
|
|
566
570
|
|
|
567
571
|
/**
|
|
568
572
|
* Handle one subscribed event: nudge once per ledger change when degraded
|
|
@@ -659,7 +663,8 @@ export function createOpenCodePlugin(context = {}) {
|
|
|
659
663
|
}),
|
|
660
664
|
|
|
661
665
|
setup: async (appContext) => {
|
|
662
|
-
const
|
|
666
|
+
const host = appContext || {};
|
|
667
|
+
const opts = context.options || host.options;
|
|
663
668
|
if (opts && typeof opts === 'object') {
|
|
664
669
|
const updates = {};
|
|
665
670
|
if (opts.search_engine) updates.search_engine = opts.search_engine;
|
|
@@ -669,25 +674,97 @@ export function createOpenCodePlugin(context = {}) {
|
|
|
669
674
|
updates.divergence_threshold = opts.divergence_threshold;
|
|
670
675
|
}
|
|
671
676
|
if (Object.keys(updates).length > 0) {
|
|
672
|
-
await callMcp('iumbtems_config', updates,
|
|
677
|
+
await callMcp('iumbtems_config', updates, host.cwd);
|
|
673
678
|
}
|
|
674
679
|
}
|
|
675
680
|
// Phase C: idle staleness nudge (opt out with {staleness_nudge: false}).
|
|
676
681
|
// Subscribes only when the host exposes event/session; one per root.
|
|
677
682
|
try {
|
|
678
683
|
if ((opts?.staleness_nudge ?? true)) {
|
|
679
|
-
const
|
|
680
|
-
event:
|
|
681
|
-
session:
|
|
684
|
+
const nudgeHost = {
|
|
685
|
+
event: host.event || context.event,
|
|
686
|
+
session: host.session || context.session,
|
|
682
687
|
};
|
|
683
688
|
const root =
|
|
684
|
-
|
|
685
|
-
if (root)
|
|
689
|
+
host.location?.directory || context.location?.directory;
|
|
690
|
+
if (root) {
|
|
691
|
+
const stop = startStalenessNudge(nudgeHost, root);
|
|
692
|
+
if (typeof stop === 'function') setupCleanups.add(stop);
|
|
693
|
+
}
|
|
686
694
|
}
|
|
687
695
|
} catch {
|
|
688
696
|
/* lifecycle hooks are best-effort */
|
|
689
697
|
}
|
|
690
|
-
|
|
698
|
+
// V2 host transforms (official dual-package pattern: V1 calls server(),
|
|
699
|
+
// V2 calls setup()). Registers slash commands + tools via the owning
|
|
700
|
+
// domains; each guarded so V1 hosts and partial V2 hosts keep working.
|
|
701
|
+
try {
|
|
702
|
+
if (host.command && typeof host.command.transform === 'function') {
|
|
703
|
+
let existing = new Set();
|
|
704
|
+
try {
|
|
705
|
+
const listed = await host.command.list();
|
|
706
|
+
existing = new Set(((listed && listed.data) || []).map((c) => c && c.name));
|
|
707
|
+
} catch {
|
|
708
|
+
/* treat as empty */
|
|
709
|
+
}
|
|
710
|
+
await host.command.transform((draft) => {
|
|
711
|
+
for (const cmd of OPENCODE_COMMANDS) {
|
|
712
|
+
if (!cmd || !cmd.name || existing.has(cmd.name)) continue;
|
|
713
|
+
existing.add(cmd.name);
|
|
714
|
+
draft.add({
|
|
715
|
+
name: cmd.name,
|
|
716
|
+
description: cmd.description,
|
|
717
|
+
execute: async (input) => {
|
|
718
|
+
const args = (input && input.prompt && input.prompt.text) || '';
|
|
719
|
+
const prompt = (input && input.prompt && typeof input.prompt === 'object') ? input.prompt : {};
|
|
720
|
+
await host.session.prompt({
|
|
721
|
+
...prompt,
|
|
722
|
+
sessionID: input && input.sessionID,
|
|
723
|
+
text: cmd.template.split('$ARGUMENTS').join(String(args).trim()),
|
|
724
|
+
delivery: input && input.delivery,
|
|
725
|
+
});
|
|
726
|
+
},
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
} catch {
|
|
732
|
+
/* command transform is best-effort */
|
|
733
|
+
}
|
|
734
|
+
try {
|
|
735
|
+
if (host.tool && typeof host.tool.transform === 'function') {
|
|
736
|
+
await host.tool.transform((draft) => {
|
|
737
|
+
for (const [name, spec] of Object.entries(buildToolMap())) {
|
|
738
|
+
draft.add({
|
|
739
|
+
name,
|
|
740
|
+
description: spec.description,
|
|
741
|
+
input: spec.input,
|
|
742
|
+
options: { codemode: false },
|
|
743
|
+
execute: async (args = {}, toolContext) => {
|
|
744
|
+
const r = await callMcp(name, normalizeArgs(name, args), toolContext?.cwd);
|
|
745
|
+
return { content: r.content };
|
|
746
|
+
},
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
} catch {
|
|
752
|
+
/* tool transform is best-effort */
|
|
753
|
+
}
|
|
754
|
+
// Disposer: the host calls setup's return value as a cleanup function
|
|
755
|
+
// on unload/reload. It MUST be a function (returning a plain object
|
|
756
|
+
// crashes host reload with "not a function"). Aborts nudge streams so
|
|
757
|
+
// reloads never stack duplicate subscriptions.
|
|
758
|
+
return () => {
|
|
759
|
+
for (const stop of [...setupCleanups]) {
|
|
760
|
+
setupCleanups.delete(stop);
|
|
761
|
+
try {
|
|
762
|
+
stop();
|
|
763
|
+
} catch {
|
|
764
|
+
/* cleanup is best-effort */
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
};
|
|
691
768
|
},
|
|
692
769
|
};
|
|
693
770
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -465,5 +465,129 @@ class TestOpenCodeLifecycle(unittest.TestCase):
|
|
|
465
465
|
self.assertEqual(data["sid"], "ses_1")
|
|
466
466
|
|
|
467
467
|
|
|
468
|
+
class TestOpenCodeV2Transforms(unittest.TestCase):
|
|
469
|
+
"""V2 host (setup ctx transforms): commands + tools via owning domains."""
|
|
470
|
+
|
|
471
|
+
V2_SETUP = """
|
|
472
|
+
import plugin from "./plugins/opencode/index.js";
|
|
473
|
+
const addedCommands = [];
|
|
474
|
+
const addedTools = [];
|
|
475
|
+
const prompts = [];
|
|
476
|
+
const host = {
|
|
477
|
+
options: {},
|
|
478
|
+
command: {
|
|
479
|
+
list: async () => ({data: [{name: "goal"}]}),
|
|
480
|
+
transform: async (fn) => { fn({add: (d) => addedCommands.push(d)}); return () => {}; }
|
|
481
|
+
},
|
|
482
|
+
tool: {
|
|
483
|
+
transform: async (fn) => { fn({add: (t) => addedTools.push(t)}); return () => {}; }
|
|
484
|
+
},
|
|
485
|
+
session: { prompt: async (p) => { prompts.push(p); return {}; } }
|
|
486
|
+
};
|
|
487
|
+
const ret = await plugin.setup(host);
|
|
488
|
+
%s
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
def test_v2_registers_commands_and_tools(self):
|
|
492
|
+
res = run_node(
|
|
493
|
+
self.V2_SETUP
|
|
494
|
+
% """
|
|
495
|
+
console.log(JSON.stringify({
|
|
496
|
+
cleanupFn: typeof ret === "function",
|
|
497
|
+
commands: addedCommands.map(c => c.name),
|
|
498
|
+
cmdExec: typeof addedCommands.find(c => c.name === "swarm").execute,
|
|
499
|
+
tools: addedTools.map(t => t.name),
|
|
500
|
+
toolExec: typeof addedTools.find(t => t.name === "iumbtems_config").execute
|
|
501
|
+
}));
|
|
502
|
+
"""
|
|
503
|
+
)
|
|
504
|
+
self.assertEqual(res.returncode, 0, f"v2 transform test failed: {res.stderr}")
|
|
505
|
+
data = last_json_object(res.stdout)
|
|
506
|
+
self.assertEqual(data["cleanupFn"], True)
|
|
507
|
+
for c in [
|
|
508
|
+
"swarm",
|
|
509
|
+
"grill",
|
|
510
|
+
"swarm-config",
|
|
511
|
+
"audit",
|
|
512
|
+
"scout",
|
|
513
|
+
"brainstorming",
|
|
514
|
+
"brainstorm",
|
|
515
|
+
]:
|
|
516
|
+
self.assertIn(c, data["commands"])
|
|
517
|
+
self.assertNotIn("goal", data["commands"])
|
|
518
|
+
self.assertEqual(data["cmdExec"], "function")
|
|
519
|
+
self.assertEqual(len(data["tools"]), 13)
|
|
520
|
+
self.assertIn("iumbtems_brainstorm", data["tools"])
|
|
521
|
+
self.assertEqual(data["toolExec"], "function")
|
|
522
|
+
|
|
523
|
+
def test_v2_command_execute_forwards_prompt(self):
|
|
524
|
+
res = run_node(
|
|
525
|
+
self.V2_SETUP
|
|
526
|
+
% """
|
|
527
|
+
const swarm = addedCommands.find(c => c.name === "swarm");
|
|
528
|
+
await swarm.execute({sessionID: "ses_9", prompt: {text: "probe objective"}, delivery: "steer"});
|
|
529
|
+
console.log(JSON.stringify({prompts}));
|
|
530
|
+
"""
|
|
531
|
+
)
|
|
532
|
+
self.assertEqual(res.returncode, 0, f"v2 execute test failed: {res.stderr}")
|
|
533
|
+
data = last_json_object(res.stdout)["prompts"]
|
|
534
|
+
self.assertEqual(len(data), 1)
|
|
535
|
+
self.assertEqual(data[0]["sessionID"], "ses_9")
|
|
536
|
+
self.assertIn("probe objective", data[0]["text"])
|
|
537
|
+
self.assertNotIn("$ARGUMENTS", data[0]["text"])
|
|
538
|
+
self.assertEqual(data[0]["delivery"], "steer")
|
|
539
|
+
|
|
540
|
+
def test_v2_tool_execute_returns_content(self):
|
|
541
|
+
res = run_node(
|
|
542
|
+
self.V2_SETUP
|
|
543
|
+
% """
|
|
544
|
+
const cfg = addedTools.find(t => t.name === "iumbtems_config");
|
|
545
|
+
const r = await cfg.execute({}, {});
|
|
546
|
+
console.log(JSON.stringify({keys: Object.keys(r), status: r.status, hasContent: typeof r.content === "string"}));
|
|
547
|
+
"""
|
|
548
|
+
)
|
|
549
|
+
self.assertEqual(res.returncode, 0, f"v2 tool test failed: {res.stderr}")
|
|
550
|
+
data = last_json_object(res.stdout)
|
|
551
|
+
self.assertEqual(data["keys"], ["content"])
|
|
552
|
+
self.assertTrue(data["hasContent"])
|
|
553
|
+
|
|
554
|
+
def test_setup_without_v2_domains_still_works(self):
|
|
555
|
+
res = run_node(
|
|
556
|
+
"""
|
|
557
|
+
import plugin from "./plugins/opencode/index.js";
|
|
558
|
+
const ret = await plugin.setup({});
|
|
559
|
+
ret();
|
|
560
|
+
ret();
|
|
561
|
+
console.log(JSON.stringify({cleanupFn: typeof ret === "function"}));
|
|
562
|
+
"""
|
|
563
|
+
)
|
|
564
|
+
self.assertEqual(res.returncode, 0, f"bare setup test failed: {res.stderr}")
|
|
565
|
+
data = last_json_object(res.stdout)
|
|
566
|
+
self.assertEqual(data["cleanupFn"], True)
|
|
567
|
+
|
|
568
|
+
def test_setup_cleanup_aborts_nudge_streams(self):
|
|
569
|
+
res = run_node(
|
|
570
|
+
"""
|
|
571
|
+
import plugin from "./plugins/opencode/index.js";
|
|
572
|
+
const seen = [];
|
|
573
|
+
const host = {
|
|
574
|
+
options: {},
|
|
575
|
+
location: {directory: "/tmp/oc-nudge-cleanup"},
|
|
576
|
+
event: {subscribe: (opts) => { seen.push(opts && opts.signal); return {[Symbol.asyncIterator]() { return {next: async () => ({done: true})}; }}; }},
|
|
577
|
+
session: {prompt: async () => ({})}
|
|
578
|
+
};
|
|
579
|
+
const cleanup = await plugin.setup(host);
|
|
580
|
+
const before = seen.length;
|
|
581
|
+
cleanup();
|
|
582
|
+
console.log(JSON.stringify({subscribed: before > 0, cleanupFn: typeof cleanup === "function", aborted: seen.length > 0 && seen[0].aborted === true}));
|
|
583
|
+
"""
|
|
584
|
+
)
|
|
585
|
+
self.assertEqual(res.returncode, 0, f"cleanup test failed: {res.stderr}")
|
|
586
|
+
data = last_json_object(res.stdout)
|
|
587
|
+
self.assertTrue(data["subscribed"])
|
|
588
|
+
self.assertTrue(data["cleanupFn"])
|
|
589
|
+
self.assertTrue(data["aborted"])
|
|
590
|
+
|
|
591
|
+
|
|
468
592
|
if __name__ == "__main__":
|
|
469
593
|
unittest.main()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|