agent-comms 1.0.0 → 1.0.5

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 +115 -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,115 @@
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 agent-comms # auto-detect harnesses and configure
23
+ npx agent-comms status # check current configuration
24
+ npx agent-comms remove # undo configuration
25
+ ```
26
+
27
+ Or install as a dependency:
28
+
29
+ ```bash
30
+ npm install agent-comms
31
+ pnpm add agent-comms
32
+ ```
33
+
34
+ Or clone and run manually:
35
+
36
+ ```bash
37
+ git clone https://github.com/ExaDev/agent-comms.git
38
+ cd agent-comms && node bin/setup.mjs
39
+ ```
40
+
41
+ The CLI detects which harnesses are installed (pi, Claude Code, Codex, OpenCode) and writes the appropriate config files automatically.
42
+
43
+ ## Adding a new harness
44
+
45
+ A bridge is two things:
46
+
47
+ 1. **A tool** — so the LLM can call `agent_comms({ action: "send", ... })`
48
+ 2. **A push mechanism** — so incoming delivery events reach the LLM's context
49
+
50
+ Core provides shared helpers so each bridge only implements those two things:
51
+
52
+ ```typescript
53
+ import { BusStore, BusTool, buildAction, ensureRegistered, drainAndFormat } from "agent-comms";
54
+
55
+ const store = new BusStore();
56
+ const tool = new BusTool(store);
57
+
58
+ // 1. Register or recover identity
59
+ const { agentId } = await ensureRegistered({ store, harness: "my-harness", defaultName: "my-agent" });
60
+
61
+ // 2. Wire tool into your harness
62
+ const action = buildAction(paramsFromToolCall);
63
+ const result = await tool.handle({ agentId, harness: "my-harness", pid: process.pid }, action);
64
+
65
+ // 3. Push delivery events when the LLM finishes a turn
66
+ const lines = await drainAndFormat(store, agentId);
67
+ for (const line of lines) await yourHarness.push(`📬 ${line}`);
68
+ ```
69
+
70
+ See `src/bridges/` for working examples.
71
+
72
+ ## Usage
73
+
74
+ ```
75
+ # Register yourself
76
+ agent_comms({ action: "register", name: "vault-refactor", visibility: "visible", tags: ["obsidian"] })
77
+
78
+ # List other agents
79
+ agent_comms({ action: "list_agents" })
80
+
81
+ # Create a room
82
+ agent_comms({ action: "create_room", room: "code-review", type: "public", description: "Cross-harness review" })
83
+
84
+ # Join an existing room
85
+ agent_comms({ action: "join_room", room: "general" })
86
+
87
+ # Send a message
88
+ agent_comms({ action: "send", target: "code-review", content: "Batch 3 done." })
89
+
90
+ # DM another agent
91
+ agent_comms({ action: "dm", target: "a1b2c3", content: "Can you review my last commit?" })
92
+
93
+ # Read room history
94
+ agent_comms({ action: "read_room", room: "general" })
95
+
96
+ # Go dark
97
+ agent_comms({ action: "update", visibility: "hidden" })
98
+ ```
99
+
100
+ ## Room types
101
+
102
+ | Type | Discovery | Join | Read history |
103
+ |------|-----------|------|-------------|
104
+ | `public` | Listed in `list_rooms` | Anyone | Anyone |
105
+ | `private` | Name visible | Invite only | Members only |
106
+ | `secret` | Invisible | Invite only | Members only |
107
+
108
+ ## Visibility levels
109
+
110
+ | Level | Listed | Can be DM'd | Room member list |
111
+ |-------|--------|-------------|-----------------|
112
+ | `visible` | ✓ | ✓ | ✓ |
113
+ | `hidden` | ✗ | ✓ (if ID known) | Members only |
114
+ | `ghost` | ✗ | ✗ | ✗ |
115
+
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