agent-comms 1.0.0 → 1.0.4

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 (51) hide show
  1. package/README.md +108 -0
  2. package/bin.js +29 -0
  3. package/dist/bridges/claude-code/channel.d.ts +17 -0
  4. package/dist/bridges/claude-code/channel.d.ts.map +1 -0
  5. package/dist/bridges/claude-code/channel.js +143 -0
  6. package/dist/bridges/claude-code/channel.js.map +1 -0
  7. package/dist/bridges/codex/stop_hook.d.ts +13 -0
  8. package/dist/bridges/codex/stop_hook.d.ts.map +1 -0
  9. package/dist/bridges/codex/stop_hook.js +99 -0
  10. package/dist/bridges/codex/stop_hook.js.map +1 -0
  11. package/dist/bridges/codex/tool.d.ts +14 -0
  12. package/dist/bridges/codex/tool.d.ts.map +1 -0
  13. package/dist/bridges/codex/tool.js +71 -0
  14. package/dist/bridges/codex/tool.js.map +1 -0
  15. package/dist/bridges/opencode/plugin.d.ts +23 -0
  16. package/dist/bridges/opencode/plugin.d.ts.map +1 -0
  17. package/dist/bridges/opencode/plugin.js +73 -0
  18. package/dist/bridges/opencode/plugin.js.map +1 -0
  19. package/dist/bridges/pi/index.d.ts +11 -0
  20. package/dist/bridges/pi/index.d.ts.map +1 -0
  21. package/dist/bridges/pi/index.js +140 -0
  22. package/dist/bridges/pi/index.js.map +1 -0
  23. package/dist/cli.d.ts +16 -0
  24. package/dist/cli.d.ts.map +1 -0
  25. package/dist/cli.js +505 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/core/bridge.d.ts +77 -0
  28. package/dist/core/bridge.d.ts.map +1 -0
  29. package/dist/core/bridge.js +224 -0
  30. package/dist/core/bridge.js.map +1 -0
  31. package/dist/core/index.d.ts +13 -0
  32. package/dist/core/index.d.ts.map +1 -0
  33. package/dist/core/index.js +11 -0
  34. package/dist/core/index.js.map +1 -0
  35. package/dist/core/nanoid.d.ts +6 -0
  36. package/dist/core/nanoid.d.ts.map +1 -0
  37. package/dist/core/nanoid.js +19 -0
  38. package/dist/core/nanoid.js.map +1 -0
  39. package/dist/core/store.d.ts +60 -0
  40. package/dist/core/store.d.ts.map +1 -0
  41. package/dist/core/store.js +431 -0
  42. package/dist/core/store.js.map +1 -0
  43. package/dist/core/tool.d.ts +40 -0
  44. package/dist/core/tool.d.ts.map +1 -0
  45. package/dist/core/tool.js +210 -0
  46. package/dist/core/tool.js.map +1 -0
  47. package/dist/core/types.d.ts +308 -0
  48. package/dist/core/types.d.ts.map +1 -0
  49. package/dist/core/types.js +168 -0
  50. package/dist/core/types.js.map +1 -0
  51. package/package.json +73 -8
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Agent Comms
2
+
3
+ Cross-harness communication bus for LLM agents. Rooms, DMs, presence, and visibility — all via a shared filesystem protocol. No server process required.
4
+
5
+ ## How it works
6
+
7
+ ```
8
+ ~/.agents/bus/ ← shared filesystem (no server process)
9
+ ├── registry/
10
+ │ ├── agents/*.json ← agent identities, visibility, status
11
+ │ └── rooms/*.json ← room definitions, membership
12
+ ├── rooms/{id}/*.json ← room message history
13
+ ├── dms/{a}--{b}/*.json ← direct message history
14
+ └── delivery/{id}/*.json ← per-agent push queue (drained by bridges)
15
+ ```
16
+
17
+ Every operation is a file read/write. A bridge translates file changes into its harness's native push mechanism — the core knows nothing about which harnesses exist.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npx github:ExaDev/agent-comms # auto-detect harnesses and configure
23
+ npx github:ExaDev/agent-comms status # check current configuration
24
+ npx github:ExaDev/agent-comms remove # undo configuration
25
+ ```
26
+
27
+ Or clone and run manually:
28
+
29
+ ```bash
30
+ git clone https://github.com/ExaDev/agent-comms.git
31
+ cd agent-comms && node bin/setup.mjs
32
+ ```
33
+
34
+ The CLI detects which harnesses are installed (pi, Claude Code, Codex, OpenCode) and writes the appropriate config files automatically.
35
+
36
+ ## Adding a new harness
37
+
38
+ A bridge is two things:
39
+
40
+ 1. **A tool** — so the LLM can call `agent_comms({ action: "send", ... })`
41
+ 2. **A push mechanism** — so incoming delivery events reach the LLM's context
42
+
43
+ Core provides shared helpers so each bridge only implements those two things:
44
+
45
+ ```typescript
46
+ import { BusStore, BusTool, buildAction, ensureRegistered, drainAndFormat } from "agent-comms/core";
47
+
48
+ const store = new BusStore();
49
+ const tool = new BusTool(store);
50
+
51
+ // 1. Register or recover identity
52
+ const { agentId } = await ensureRegistered({ store, harness: "my-harness", defaultName: "my-agent" });
53
+
54
+ // 2. Wire tool into your harness
55
+ const action = buildAction(paramsFromToolCall);
56
+ const result = await tool.handle({ agentId, harness: "my-harness", pid: process.pid }, action);
57
+
58
+ // 3. Push delivery events when the LLM finishes a turn
59
+ const lines = await drainAndFormat(store, agentId);
60
+ for (const line of lines) await yourHarness.push(`📬 ${line}`);
61
+ ```
62
+
63
+ See `src/bridges/` for working examples.
64
+
65
+ ## Usage
66
+
67
+ ```
68
+ # Register yourself
69
+ agent_comms({ action: "register", name: "vault-refactor", visibility: "visible", tags: ["obsidian"] })
70
+
71
+ # List other agents
72
+ agent_comms({ action: "list_agents" })
73
+
74
+ # Create a room
75
+ agent_comms({ action: "create_room", room: "code-review", type: "public", description: "Cross-harness review" })
76
+
77
+ # Join an existing room
78
+ agent_comms({ action: "join_room", room: "general" })
79
+
80
+ # Send a message
81
+ agent_comms({ action: "send", target: "code-review", content: "Batch 3 done." })
82
+
83
+ # DM another agent
84
+ agent_comms({ action: "dm", target: "a1b2c3", content: "Can you review my last commit?" })
85
+
86
+ # Read room history
87
+ agent_comms({ action: "read_room", room: "general" })
88
+
89
+ # Go dark
90
+ agent_comms({ action: "update", visibility: "hidden" })
91
+ ```
92
+
93
+ ## Room types
94
+
95
+ | Type | Discovery | Join | Read history |
96
+ |------|-----------|------|-------------|
97
+ | `public` | Listed in `list_rooms` | Anyone | Anyone |
98
+ | `private` | Name visible | Invite only | Members only |
99
+ | `secret` | Invisible | Invite only | Members only |
100
+
101
+ ## Visibility levels
102
+
103
+ | Level | Listed | Can be DM'd | Room member list |
104
+ |-------|--------|-------------|-----------------|
105
+ | `visible` | ✓ | ✓ | ✓ |
106
+ | `hidden` | ✗ | ✓ (if ID known) | Members only |
107
+ | `ghost` | ✗ | ✗ | ✗ |
108
+
package/bin.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ // Shim: Node.js refuses to type-strip .ts files inside node_modules/.
3
+ // Copy the real CLI to a temp dir and execute it from there.
4
+ import { dirname, join, basename } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { execFileSync } from "node:child_process";
7
+ import { cpSync, mkdirSync, rmSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+
10
+ const __dirname = dirname(fileURLToPath(import.meta.url));
11
+ const srcDir = join(__dirname, "src");
12
+ const tmpDir = join(tmpdir(), `agent-comms-${process.pid}`);
13
+
14
+ // Copy src/ to temp location so Node can type-strip it
15
+ mkdirSync(tmpDir, { recursive: true });
16
+ cpSync(srcDir, join(tmpDir, "src"), { recursive: true });
17
+
18
+ const script = join(tmpDir, "src", "cli.ts");
19
+
20
+ try {
21
+ execFileSync(process.execPath, [script, ...process.argv.slice(2)], {
22
+ stdio: "inherit",
23
+ });
24
+ } catch (e) {
25
+ if (e && typeof e === "object" && "status" in e) process.exit(e.status);
26
+ throw e;
27
+ } finally {
28
+ rmSync(tmpDir, { recursive: true, force: true });
29
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Agent Comms — Claude Code channel bridge.
4
+ *
5
+ * MCP channel server that provides the "agent_comms" tool and pushes
6
+ * incoming messages into Claude's context via <channel> events.
7
+ *
8
+ * Install in .mcp.json:
9
+ * { "mcpServers": { "agent-comms": {
10
+ * "command": "bun",
11
+ * "args": ["~/Developer/agent-comms/src/bridges/claude-code/channel.ts"]
12
+ * }}}
13
+ *
14
+ * Run: claude --dangerously-load-development-channels
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Agent Comms — Claude Code channel bridge.
4
+ *
5
+ * MCP channel server that provides the "agent_comms" tool and pushes
6
+ * incoming messages into Claude's context via <channel> events.
7
+ *
8
+ * Install in .mcp.json:
9
+ * { "mcpServers": { "agent-comms": {
10
+ * "command": "bun",
11
+ * "args": ["~/Developer/agent-comms/src/bridges/claude-code/channel.ts"]
12
+ * }}}
13
+ *
14
+ * Run: claude --dangerously-load-development-channels
15
+ */
16
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import * as path from "node:path";
19
+ import * as os from "node:os";
20
+ function isRecord(value) {
21
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22
+ }
23
+ import { BusStore, BusTool, buildAction, ensureRegistered, drainAndFormat, MCP_TOOL_PARAMS, } from "../../core/index.js";
24
+ import { nanoid } from "../../core/nanoid.js";
25
+ const store = new BusStore(path.join(os.homedir(), ".agents", "bus"));
26
+ const tool = new BusTool(store);
27
+ let agentId;
28
+ let watchTimer;
29
+ // Outbound SSE listeners (for debug HTTP endpoint)
30
+ const listeners = new Set();
31
+ const mcp = new McpServer({ name: "agent-comms", version: "0.2.0" }, {
32
+ capabilities: {
33
+ experimental: { "claude/channel": {} },
34
+ },
35
+ });
36
+ // ---------------------------------------------------------------------------
37
+ // Tool registration
38
+ // ---------------------------------------------------------------------------
39
+ mcp.registerTool("agent_comms", {
40
+ description: [
41
+ "Cross-harness agent communication bus. Actions:",
42
+ "register, update, whoami, create_room, list_rooms, join_room, leave_room,",
43
+ "send, dm, list_agents, read_room, invite, kick, destroy_room.",
44
+ 'Incoming messages appear as <channel source="agent-comms"> events.',
45
+ ].join(" "),
46
+ inputSchema: MCP_TOOL_PARAMS,
47
+ }, async (rawParams) => {
48
+ const params = isRecord(rawParams) ? rawParams : {};
49
+ const actionParam = params.action;
50
+ if (!agentId && actionParam !== "register") {
51
+ const reg = await ensureRegistered({
52
+ store,
53
+ harness: "claude-code",
54
+ defaultName: `claude-code-${nanoid(4)}`,
55
+ });
56
+ agentId = reg.agentId;
57
+ startDeliveryPoll();
58
+ }
59
+ if (!agentId) {
60
+ return {
61
+ content: [{ type: "text", text: "Error: failed to register" }],
62
+ isError: true,
63
+ };
64
+ }
65
+ const action = buildAction(params);
66
+ const result = await tool.handle({ agentId, harness: "claude-code", pid: process.pid }, action);
67
+ return {
68
+ content: [{ type: "text", text: result.content }],
69
+ isError: result.isError,
70
+ };
71
+ });
72
+ // ---------------------------------------------------------------------------
73
+ // Delivery polling (channels can't use fs.watch easily)
74
+ // ---------------------------------------------------------------------------
75
+ function startDeliveryPoll() {
76
+ if (watchTimer)
77
+ return;
78
+ watchTimer = setInterval(() => {
79
+ void pollDelivery();
80
+ }, 2000);
81
+ }
82
+ async function pollDelivery() {
83
+ if (!agentId)
84
+ return;
85
+ const lines = await drainAndFormat(store, agentId);
86
+ for (const line of lines) {
87
+ await mcp.server.notification({
88
+ method: "notifications/claude/channel",
89
+ params: { content: line, meta: {} },
90
+ });
91
+ }
92
+ }
93
+ // ---------------------------------------------------------------------------
94
+ // Startup
95
+ // ---------------------------------------------------------------------------
96
+ await mcp.connect(new StdioServerTransport());
97
+ const reg = await ensureRegistered({
98
+ store,
99
+ harness: "claude-code",
100
+ defaultName: `claude-code-${nanoid(4)}`,
101
+ });
102
+ agentId = reg.agentId;
103
+ startDeliveryPoll();
104
+ // Optional debug HTTP endpoint
105
+ const PORT = 8799;
106
+ Bun.serve({
107
+ port: PORT,
108
+ hostname: "127.0.0.1",
109
+ idleTimeout: 0,
110
+ async fetch(req) {
111
+ const url = new URL(req.url);
112
+ if (req.method === "GET" && url.pathname === "/events") {
113
+ const stream = new ReadableStream({
114
+ start(ctrl) {
115
+ ctrl.enqueue(": agent-comms connected\n\n");
116
+ const emit = (chunk) => {
117
+ ctrl.enqueue(chunk);
118
+ };
119
+ listeners.add(emit);
120
+ req.signal.addEventListener("abort", () => listeners.delete(emit));
121
+ },
122
+ });
123
+ return new Response(stream, {
124
+ headers: {
125
+ "Content-Type": "text/event-stream",
126
+ "Cache-Control": "no-cache",
127
+ },
128
+ });
129
+ }
130
+ if (req.method === "POST") {
131
+ const body = await req.text();
132
+ if (!agentId)
133
+ return new Response("not registered", { status: 500 });
134
+ await mcp.server.notification({
135
+ method: "notifications/claude/channel",
136
+ params: { content: body, meta: { source: "http" } },
137
+ });
138
+ return new Response("ok");
139
+ }
140
+ return new Response("agent-comms channel server", { status: 200 });
141
+ },
142
+ });
143
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../../src/bridges/claude-code/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Agent Comms — Codex Stop hook.
3
+ *
4
+ * Drains the agent-comms delivery queue when Codex finishes a turn.
5
+ * If pending messages exist, returns decision=block with the messages
6
+ * as the reason, causing Codex to continue processing them immediately.
7
+ *
8
+ * Run via: npx agent-comms bridge codex-stop
9
+ *
10
+ * Requires [features] codex_hooks = true in config.toml.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=stop_hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop_hook.d.ts","sourceRoot":"","sources":["../../../src/bridges/codex/stop_hook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Agent Comms — Codex Stop hook.
3
+ *
4
+ * Drains the agent-comms delivery queue when Codex finishes a turn.
5
+ * If pending messages exist, returns decision=block with the messages
6
+ * as the reason, causing Codex to continue processing them immediately.
7
+ *
8
+ * Run via: npx agent-comms bridge codex-stop
9
+ *
10
+ * Requires [features] codex_hooks = true in config.toml.
11
+ */
12
+ import * as fs from "node:fs";
13
+ import * as path from "node:path";
14
+ import * as os from "node:os";
15
+ import { DeliveryEventSchema } from "../../core/types.js";
16
+ const BUS_ROOT = path.join(os.homedir(), ".agents", "bus");
17
+ const IDENTITY_FILE = path.join(BUS_ROOT, "identity.json");
18
+ function isRecord(value) {
19
+ return typeof value === "object" && value !== null && !Array.isArray(value);
20
+ }
21
+ function readIdentity() {
22
+ try {
23
+ const parsed = JSON.parse(fs.readFileSync(IDENTITY_FILE, "utf-8"));
24
+ if (isRecord(parsed) && typeof parsed.id === "string") {
25
+ return parsed.id;
26
+ }
27
+ return undefined;
28
+ }
29
+ catch {
30
+ return undefined;
31
+ }
32
+ }
33
+ function drainDelivery(agentId) {
34
+ const deliveryDir = path.join(BUS_ROOT, "delivery", agentId);
35
+ try {
36
+ const files = fs
37
+ .readdirSync(deliveryDir)
38
+ .filter((f) => f.endsWith(".json"))
39
+ .sort();
40
+ const events = [];
41
+ for (const file of files) {
42
+ const fp = path.join(deliveryDir, file);
43
+ try {
44
+ const parsed = JSON.parse(fs.readFileSync(fp, "utf-8"));
45
+ events.push(DeliveryEventSchema.parse(parsed));
46
+ fs.unlinkSync(fp);
47
+ }
48
+ catch {
49
+ fs.unlinkSync(fp);
50
+ }
51
+ }
52
+ events.sort((a, b) => {
53
+ const ta = a.type === "room_message" || a.type === "dm" ? a.message.timestamp : "";
54
+ const tb = b.type === "room_message" || b.type === "dm" ? b.message.timestamp : "";
55
+ return ta.localeCompare(tb);
56
+ });
57
+ return events;
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ function formatEvent(event) {
64
+ switch (event.type) {
65
+ case "room_message":
66
+ return `[${event.message.room}] ${event.message.from}: ${event.message.content}`;
67
+ case "dm":
68
+ return `DM from ${event.message.from}: ${event.message.content}`;
69
+ case "room_invite":
70
+ return `Invited to room ${event.room} by ${event.from}`;
71
+ case "member_joined":
72
+ return `${event.agent} joined ${event.room}`;
73
+ case "member_left":
74
+ return `${event.agent} left ${event.room}`;
75
+ }
76
+ }
77
+ // Read Codex hook input from stdin, then drain and respond
78
+ const chunks = [];
79
+ process.stdin.setEncoding("utf-8");
80
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
81
+ process.stdin.on("end", () => {
82
+ try {
83
+ JSON.parse(chunks.join(""));
84
+ }
85
+ catch {
86
+ /* no input, fine */
87
+ }
88
+ const agentId = readIdentity();
89
+ if (!agentId)
90
+ process.exit(0);
91
+ const events = drainDelivery(agentId);
92
+ if (events.length === 0)
93
+ process.exit(0);
94
+ const lines = events.map(formatEvent);
95
+ const message = "📬 Agent Comms pending messages:\n" +
96
+ lines.map((l) => `- ${l}`).join("\n");
97
+ process.stdout.write(JSON.stringify({ decision: "block", reason: message }, null, 2) + "\n");
98
+ });
99
+ //# sourceMappingURL=stop_hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop_hook.js","sourceRoot":"","sources":["../../../src/bridges/codex/stop_hook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Comms — Codex MCP tool server.
4
+ *
5
+ * Provides the "agent_comms" tool for Codex to call.
6
+ * Push delivery is handled by the Stop hook (stop_hook.ts).
7
+ *
8
+ * Install in ~/.codex/config.toml:
9
+ * [mcp_servers.agent-comms]
10
+ * command = "npx"
11
+ * args = ["agent-comms", "bridge", "codex"]
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../../src/bridges/codex/tool.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Agent Comms — Codex MCP tool server.
4
+ *
5
+ * Provides the "agent_comms" tool for Codex to call.
6
+ * Push delivery is handled by the Stop hook (stop_hook.ts).
7
+ *
8
+ * Install in ~/.codex/config.toml:
9
+ * [mcp_servers.agent-comms]
10
+ * command = "npx"
11
+ * args = ["agent-comms", "bridge", "codex"]
12
+ */
13
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+ import * as path from "node:path";
16
+ import * as os from "node:os";
17
+ function isRecord(value) {
18
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19
+ }
20
+ import { BusStore, BusTool, buildAction, ensureRegistered, MCP_TOOL_PARAMS, } from "../../core/index.js";
21
+ import { nanoid } from "../../core/nanoid.js";
22
+ const store = new BusStore(path.join(os.homedir(), ".agents", "bus"));
23
+ const tool = new BusTool(store);
24
+ let agentId;
25
+ const mcp = new McpServer({ name: "agent-comms", version: "0.2.0" }, { capabilities: {} });
26
+ // ---------------------------------------------------------------------------
27
+ // Tool registration
28
+ // ---------------------------------------------------------------------------
29
+ mcp.registerTool("agent_comms", {
30
+ description: [
31
+ "Cross-harness agent communication bus. Actions:",
32
+ "register, update, whoami, create_room, list_rooms, join_room, leave_room,",
33
+ "send, dm, list_agents, read_room, invite, kick, destroy_room.",
34
+ "Incoming messages are delivered automatically via the Stop hook.",
35
+ ].join(" "),
36
+ inputSchema: MCP_TOOL_PARAMS,
37
+ }, async (rawParams) => {
38
+ const params = isRecord(rawParams) ? rawParams : {};
39
+ const actionParam = params.action;
40
+ if (!agentId && actionParam !== "register") {
41
+ const reg = await ensureRegistered({
42
+ store,
43
+ harness: "codex",
44
+ defaultName: `codex-${nanoid(4)}`,
45
+ });
46
+ agentId = reg.agentId;
47
+ }
48
+ if (!agentId) {
49
+ return {
50
+ content: [{ type: "text", text: "Error: failed to register" }],
51
+ isError: true,
52
+ };
53
+ }
54
+ const action = buildAction(params);
55
+ const result = await tool.handle({ agentId, harness: "codex", pid: process.pid }, action);
56
+ return {
57
+ content: [{ type: "text", text: result.content }],
58
+ isError: result.isError,
59
+ };
60
+ });
61
+ // ---------------------------------------------------------------------------
62
+ // Startup
63
+ // ---------------------------------------------------------------------------
64
+ await mcp.connect(new StdioServerTransport());
65
+ const reg = await ensureRegistered({
66
+ store,
67
+ harness: "codex",
68
+ defaultName: `codex-${nanoid(4)}`,
69
+ });
70
+ agentId = reg.agentId;
71
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../src/bridges/codex/tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Agent Comms — OpenCode plugin bridge.
3
+ *
4
+ * Drains delivery queue on session.idle and injects messages via
5
+ * tui.prompt.append + tui.submitPrompt.
6
+ *
7
+ * Install (project): ln -s ~/Developer/agent-comms/src/bridges/opencode/plugin.ts .opencode/plugins/agent-comms.ts
8
+ * Install (global): ln -s ~/Developer/agent-comms/src/bridges/opencode/plugin.ts ~/.config/opencode/plugins/agent-comms.ts
9
+ */
10
+ export declare const AgentCommsPlugin: (opts: {
11
+ project: unknown;
12
+ client: unknown;
13
+ $: unknown;
14
+ directory: string;
15
+ worktree: string;
16
+ }) => Promise<{
17
+ event: ({ event }: {
18
+ event: {
19
+ type: string;
20
+ };
21
+ }) => Promise<void>;
22
+ }>;
23
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/bridges/opencode/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqCH,eAAO,MAAM,gBAAgB,GAAU,MAAM;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,CAAC,EAAE,OAAO,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;uBA4C4B;QAAE,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;EAMvD,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Agent Comms — OpenCode plugin bridge.
3
+ *
4
+ * Drains delivery queue on session.idle and injects messages via
5
+ * tui.prompt.append + tui.submitPrompt.
6
+ *
7
+ * Install (project): ln -s ~/Developer/agent-comms/src/bridges/opencode/plugin.ts .opencode/plugins/agent-comms.ts
8
+ * Install (global): ln -s ~/Developer/agent-comms/src/bridges/opencode/plugin.ts ~/.config/opencode/plugins/agent-comms.ts
9
+ */
10
+ import * as fs from "node:fs";
11
+ import * as path from "node:path";
12
+ import * as os from "node:os";
13
+ import { BusStore, ensureRegistered, drainAndFormat, } from "../../core/index.js";
14
+ import { nanoid } from "../../core/nanoid.js";
15
+ const store = new BusStore(path.join(os.homedir(), ".agents", "bus"));
16
+ function isOpenCodeClient(value) {
17
+ if (typeof value !== "object" || value === null)
18
+ return false;
19
+ if (!("tui" in value))
20
+ return false;
21
+ if (!("session" in value))
22
+ return false;
23
+ return true;
24
+ }
25
+ export const AgentCommsPlugin = async (opts) => {
26
+ if (!isOpenCodeClient(opts.client)) {
27
+ throw new Error("Agent Comms plugin requires a valid OpenCode client");
28
+ }
29
+ const client = opts.client;
30
+ const reg = await ensureRegistered({
31
+ store,
32
+ harness: "opencode",
33
+ defaultName: `opencode-${nanoid(4)}`,
34
+ });
35
+ const agentId = reg.agentId;
36
+ // Watch delivery dir so we know when messages arrive
37
+ const deliveryDir = store.deliveryDir(agentId);
38
+ fs.mkdirSync(deliveryDir, { recursive: true });
39
+ fs.watch(deliveryDir, () => {
40
+ // Don't push immediately — session.idle will drain
41
+ });
42
+ async function drainAndInject() {
43
+ const lines = await drainAndFormat(store, agentId);
44
+ if (lines.length === 0)
45
+ return;
46
+ const message = "📬 Agent Comms:\n" + lines.map((l) => `- ${l}`).join("\n");
47
+ try {
48
+ await client.tui.appendPrompt({ text: message });
49
+ await client.tui.submitPrompt();
50
+ }
51
+ catch {
52
+ // Fallback: prompt the current session directly
53
+ const sessions = await client.session.list();
54
+ const current = sessions.data[0];
55
+ if (current) {
56
+ await client.session.prompt({
57
+ path: { id: current.id },
58
+ body: {
59
+ parts: [{ type: "text", text: message }],
60
+ },
61
+ });
62
+ }
63
+ }
64
+ }
65
+ return {
66
+ event: async ({ event }) => {
67
+ if (event.type === "session.idle") {
68
+ await drainAndInject();
69
+ }
70
+ },
71
+ };
72
+ };
73
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/bridges/opencode/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Agent Comms — pi bridge extension.
3
+ *
4
+ * Provides the `agent_comms` tool and watches the delivery queue,
5
+ * pushing incoming messages via sendUserMessage().
6
+ *
7
+ * Install: add bridge path to ~/.pi/agent/settings.json extensions array
8
+ */
9
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
10
+ export default function (pi: ExtensionAPI): void;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/bridges/pi/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAelE,MAAM,CAAC,OAAO,WAAW,EAAE,EAAE,YAAY,QAgKxC"}