@ctliz/agent-intercom-claude 0.12.0-connect.3

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 (59) hide show
  1. package/.claude-plugin/plugin.json +26 -0
  2. package/.mcp.json +10 -0
  3. package/CHANGELOG.md +62 -0
  4. package/COPYRIGHT +6 -0
  5. package/LICENSE +661 -0
  6. package/LICENSE_TRANSITION.md +25 -0
  7. package/README.md +506 -0
  8. package/THIRD_PARTY_NOTICES.md +25 -0
  9. package/broker/access-credential.ts +76 -0
  10. package/broker/access-registry.ts +460 -0
  11. package/broker/audit.ts +60 -0
  12. package/broker/authorization.ts +125 -0
  13. package/broker/boss-contracts.ts +267 -0
  14. package/broker/broker.ts +2028 -0
  15. package/broker/client.ts +1048 -0
  16. package/broker/framing.ts +87 -0
  17. package/broker/ownership.ts +61 -0
  18. package/broker/paths.ts +161 -0
  19. package/broker/spawn.ts +463 -0
  20. package/broker/validation.ts +132 -0
  21. package/claude/cci.ts +512 -0
  22. package/claude/ccim.ts +31 -0
  23. package/claude/cli-runner.ts +220 -0
  24. package/claude/contact.ts +33 -0
  25. package/claude/inbox-monitor.ts +49 -0
  26. package/claude/inbox.ts +77 -0
  27. package/claude/mcp-protocol.ts +253 -0
  28. package/claude/native-bridge.ts +219 -0
  29. package/claude/native-protocol.ts +346 -0
  30. package/claude/permission-policy.ts +287 -0
  31. package/claude/runtime.ts +510 -0
  32. package/claude/server.ts +69 -0
  33. package/claude/team.ts +348 -0
  34. package/claude/transport.ts +97 -0
  35. package/claude/worker-config.ts +198 -0
  36. package/claude/worker-daemon.ts +418 -0
  37. package/commands/intercom-id.md +9 -0
  38. package/commands/intercom.md +8 -0
  39. package/config.ts +195 -0
  40. package/dist/broker.mjs +2530 -0
  41. package/dist/cci.mjs +3607 -0
  42. package/dist/ccim.mjs +3621 -0
  43. package/dist/claude-server.mjs +2730 -0
  44. package/dist/inbox-monitor.mjs +68 -0
  45. package/dist/worker-daemon.mjs +2797 -0
  46. package/durable-json.ts +25 -0
  47. package/licenses/MIT-pi-claude-link.txt +21 -0
  48. package/licenses/MIT-pi-intercom.txt +21 -0
  49. package/monitors/monitors.json +8 -0
  50. package/outbound-outbox.ts +116 -0
  51. package/package.json +93 -0
  52. package/protocol-v4/contract.ts +45 -0
  53. package/provider/protected-service.ts +162 -0
  54. package/provider/provider.mjs +34 -0
  55. package/scripts/build.mjs +71 -0
  56. package/scripts/verify-core-provenance.mjs +64 -0
  57. package/scripts/verify-packed-runtime.mjs +140 -0
  58. package/skills/claude-intercom/SKILL.md +90 -0
  59. package/types.ts +212 -0
@@ -0,0 +1,25 @@
1
+ # License Transition
2
+
3
+ This repository changed its current project license from MIT to the GNU Affero General
4
+ Public License v3.0 or later (`AGPL-3.0-or-later`) on 2026-07-14.
5
+
6
+ ## Boundary
7
+
8
+ - Final MIT commit: [`3d0ac5f`](https://github.com/dataforxyz/agent-intercom-claude/commit/3d0ac5f)
9
+ - Final MIT tag: [`mit-final`](https://github.com/dataforxyz/agent-intercom-claude/tree/mit-final)
10
+ - First AGPL commit: [`5f7adbb`](https://github.com/dataforxyz/agent-intercom-claude/commit/5f7adbb)
11
+ - First AGPL tag: [`agpl-transition`](https://github.com/dataforxyz/agent-intercom-claude/tree/agpl-transition)
12
+ - First versioned AGPL release: `v0.3.0`
13
+
14
+ The transition is prospective. It does not revoke or alter rights granted for copies
15
+ already received under MIT. Current and future versions derived from the AGPL side of
16
+ the boundary are distributed under `AGPL-3.0-or-later`.
17
+
18
+ No earlier npm release is being retroactively changed. Git snapshots and packages built
19
+ from commits at or before `mit-final` remain under their original MIT terms.
20
+
21
+ ## Third-party material
22
+
23
+ Portions derived from Nico Bailon's original `pi-intercom` retain their original MIT
24
+ notices. See [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md) and
25
+ [`licenses/MIT-pi-intercom.txt`](licenses/MIT-pi-intercom.txt).
package/README.md ADDED
@@ -0,0 +1,506 @@
1
+ # Claude Intercom
2
+
3
+ **Agent Intercom** is a cross-harness, same-machine messaging system for coding agents. Its Pi, Codex, Claude Code, and OpenCode adapters share one local broker and protocol, so sessions can discover and message each other regardless of which harness they run in.
4
+
5
+ | Harness | Repository |
6
+ |---|---|
7
+ | Core / Protocol | [`agent-intercom-core`](https://github.com/ctliz/agent-intercom-core) |
8
+ | Pi | [`agent-intercom-pi`](https://github.com/ctliz/agent-intercom-pi) |
9
+ | Codex | [`agent-intercom-codex`](https://github.com/ctliz/agent-intercom-codex) |
10
+ | Claude Code | [`agent-intercom-claude`](https://github.com/ctliz/agent-intercom-claude) |
11
+ | OpenCode | [`agent-intercom-opencode`](https://github.com/ctliz/agent-intercom-opencode) |
12
+ | Fleet lifecycle | [`agent-intercom-orchestrator`](https://github.com/ctliz/agent-intercom-orchestrator) |
13
+
14
+ ## Maintenance & Upstream Provenance
15
+
16
+ - **Maintained by `ctliz`**: This distribution is maintained independently by [ctliz](https://github.com/ctliz).
17
+ - **Upstream Heritage**: Agent Intercom grew from [Nico Bailon's original `pi-intercom`](https://github.com/nicobailon/pi-intercom) and the upstream [`dataforxyz/agent-intercom-*`](https://github.com/dataforxyz/agent-intercom-claude) repositories. This project is not officially endorsed by or affiliated with upstream organizations.
18
+ - **Package Namespace**: The canonical npm namespace is `@ctliz/*`. The historical `@dataforxyz/*` namespace was used up to and including `connect.1` and is retained only as provenance and as a migration-detection input; it is never treated as a current or healthy installation. The **Agent Intercom** branding and the `intercom_*` API surface are unchanged.
19
+
20
+ ## Protocol v4 & Broker-Enforced Scope
21
+
22
+ Agent Intercom protocol v4 introduces **broker-enforced scope routing** via `AGENT_INTERCOM_SCOPE_ID`:
23
+
24
+ - **Registration**: The client submits its `scopeId` once in the top-level registration payload.
25
+ - **Broker Enforcement**: The shared local broker stores the scope in its private `ConnectedSession` record and enforces same-scope discovery (`intercom_list`), naming, and prefix matching.
26
+ - **Cross-Scope Routing**: Cross-scope messaging is fail-closed; communication across different scopes is permitted only when addressing an explicit full session ID.
27
+ - **UX Routing Isolation**: Scope is designed for same-OS-user workflow isolation (e.g. per-project or per-workspace agent teams), **not** as a cryptographic security principal, tenant boundary, or authentication credential.
28
+ - **Leak-Free**: The raw `scopeId` value never enters `SessionInfo`, list payloads, lifecycle events, frontend displays, or execution logs.
29
+ - **Standalone First**: `AGENT_INTERCOM_SCOPE_ID` is a general shell/IDE/service launcher contract. Agent Intercom works completely standalone in any terminal, tmux window, or script; TmuxDeck is optional visual tooling.
30
+
31
+ ## Origin and thanks
32
+
33
+ Agent Intercom grew from [Nico Bailon's original `pi-intercom`](https://github.com/nicobailon/pi-intercom). A sincere thank you to Nico and the original contributors for creating the Pi extension and the foundation this cross-harness family builds on.
34
+
35
+ This repository contains the Claude Code adapter. It uses the shared strict `pi-intercom` protocol v4. Any adapter may start the broker first; incompatible legacy brokers fail closed without killing, downgrading, or creating second islands. Sends are retained in a durable per-session outbox and replayed after reconnect, while receiver acknowledgement distinguishes broker acceptance from durable receipt.
36
+
37
+ Attached Claude sessions support two local delivery transports. `native` bridges the broker to Claude Code's cross-session Unix socket protocol; `mcp` preserves the plugin/inbox/Monitor path. The default `auto` mode selects native only for Claude Code versions in the verified compatibility window (currently 2.1.220–2.1.226) and otherwise selects MCP. Explicit native selection fails closed outside that window.
38
+
39
+ When running `cci` or `ccim` in an attached terminal, press **Alt+M** to choose a connected session and send it a message, or **Alt+I** to copy that worker's intercom contact target. The MCP plugin cannot register native Claude Code keyboard shortcuts because Claude Code does not expose plugin keybinding registration; the plugin instead provides `/claude-intercom:intercom` and `/claude-intercom:intercom-id`. Detached worker-daemon mode has no terminal shortcuts.
40
+
41
+ Claude Intercom adds local messaging between Claude Code, Codex, Pi, OpenCode,
42
+ and other coding-agent sessions on the same machine. It speaks the same local broker
43
+ protocol as [`pi-intercom`](https://github.com/ctliz/agent-intercom-pi) and
44
+ [`codex-intercom`](https://github.com/ctliz/agent-intercom-codex), so sessions
45
+ can discover each other, send updates, ask blocking questions, read pending
46
+ messages, and reply to asks across all four supported harnesses.
47
+
48
+ The project has two related pieces:
49
+
50
+ - `claude-intercom-mcp`: an MCP server that exposes intercom tools inside a
51
+ normal Claude Code session.
52
+ - `cci` / `claude-intercom-worker`: a **wakeable Claude worker**. It registers
53
+ an intercom identity, and when another session sends it work, it starts a
54
+ fresh headless `claude -p` turn that resumes the worker's own conversation —
55
+ so the worker can read files, run commands, edit code, and reply on its own.
56
+
57
+ Use plain MCP when you only need tools inside an already-active Claude turn. Use
58
+ a wakeable worker when you want another session to wake Claude automatically and
59
+ have it act with real system access.
60
+
61
+ ## Status
62
+
63
+ Preview. This is the Claude-side adapter, built alongside `pi-intercom` and
64
+ `codex-intercom`.
65
+
66
+ A plain Claude Code MCP session does not receive unsolicited visible turns.
67
+ Incoming messages are queued while the MCP server is running; call
68
+ `intercom_pending` to read them. Wake-on-message workflows use `cci` /
69
+ `claude-intercom-worker`.
70
+
71
+ ## How Claude gets woken
72
+
73
+ Claude Code has no long-lived programmatic "app-server" the way Codex does, so
74
+ the worker uses the most robust primitive available: the headless CLI.
75
+
76
+ 1. The worker registers an intercom identity on the local broker and idles.
77
+ 2. When a message arrives, the worker runs
78
+ `claude -p --output-format json --resume <session-id> ...`, feeding the
79
+ message text on stdin. Normal `cci` workers automatically receive the
80
+ packaged Intercom MCP server, even under an isolated `CLAUDE_CONFIG_DIR` or
81
+ custom `ANTHROPIC_BASE_URL`.
82
+ 3. Claude runs a full turn — it can use Bash, Read, Edit, and every other
83
+ Claude Code tool, subject to the worker's permission mode — and prints a
84
+ final result plus a stable `session_id`.
85
+ 4. The worker persists that `session_id` so the next message resumes the same
86
+ conversation, and (for blocking asks) sends the final assistant message back
87
+ to the asker as the reply.
88
+
89
+ This gives a woken worker genuine access to the system while keeping each worker
90
+ a continuous, resumable conversation. You can attach to a worker's conversation
91
+ at any time with `claude --resume <session-id>`.
92
+
93
+ ## Install
94
+
95
+ Install via npm using the `connect` dist-tag:
96
+
97
+ ```bash
98
+ npm install -g @ctliz/agent-intercom-claude@connect
99
+ # or by exact prerelease version
100
+ npm install -g @ctliz/agent-intercom-claude@0.12.0-connect.3
101
+ ```
102
+
103
+ Or install from GitHub source at the exact tag so the command-line entry points are on `PATH`:
104
+
105
+ ```bash
106
+ git clone --depth 1 --branch v0.12.0-connect.3 https://github.com/ctliz/agent-intercom-claude.git
107
+ cd agent-intercom-claude && npm ci && npm link
108
+ ```
109
+
110
+ This provides:
111
+
112
+ - `claude-intercom-mcp`
113
+ - `claude-intercom-worker`
114
+ - `cci` — start a normal wakeable worker
115
+ - `ccim` — start a minimal wakeable worker (`cci --minimal`)
116
+
117
+ To let a Pi manager create Claude workers with owned systemd cgroups, leases, model/effort selection, logs, and verified cleanup, install the companion Pi packages:
118
+
119
+ ```bash
120
+ pi install git:github.com/ctliz/agent-intercom-pi@v0.11.0-connect.2
121
+ pi install git:github.com/ctliz/agent-intercom-orchestrator@v0.11.0-connect.2
122
+ ```
123
+
124
+ Restart Pi or run `/reload`, then call `agent_fleet({ action: "doctor" })`. The orchestrator invokes the installed `cci`/`ccim` commands; it does not replace this Claude adapter.
125
+
126
+ `cci` and `ccim` are the recommended entry points when you want an attached,
127
+ wakeable Claude session. Unlike a plain MCP session or a detached headless
128
+ worker, the attached wrappers provide the **Alt+M** session picker/message
129
+ composer and the **Alt+I** contact-copy shortcut; they also keep an intercom
130
+ identity online so another agent can wake the worker.
131
+ If you use the same worker profiles repeatedly, add memorable shell aliases
132
+ with your own portable project paths and stable IDs:
133
+
134
+ ```bash
135
+ alias claude-reviewer='cci --cwd "$HOME/src/my-project" --name reviewer --id reviewer'
136
+ alias claude-reviewer-min='ccim --cwd "$HOME/src/my-project" --name reviewer-min --id reviewer-min'
137
+ ```
138
+
139
+ Put aliases in your shell startup file (for example `~/.bashrc` or `~/.zshrc`).
140
+ They are optional convenience shortcuts: the installed `cci` and `ccim`
141
+ commands work directly, but aliases make stable identities and project-specific
142
+ defaults easier to reuse without copying a long command.
143
+
144
+ For a plain, already-active Claude Code session, add the MCP server explicitly:
145
+
146
+ ```bash
147
+ claude mcp add claude-intercom -- claude-intercom-mcp
148
+ ```
149
+
150
+ With `--transport mcp`, `cci` does this automatically for each normal headless worker. Native headless workers are still woken and replied through the worker daemon's broker connection, but omit the packaged MCP server from the Claude turn. `ccim` intentionally uses Claude's `--safe-mode`, which disables MCP servers along with plugins, hooks, and skills.
151
+
152
+ Optional identity variables can be attached at registration time:
153
+
154
+ ```bash
155
+ claude mcp add claude-planner \
156
+ --env CLAUDE_INTERCOM_NAME=planner \
157
+ --env CLAUDE_INTERCOM_SESSION_ID=claude-planner \
158
+ --env CLAUDE_INTERCOM_MODEL=opus \
159
+ -- claude-intercom-mcp
160
+ ```
161
+
162
+ ## Plugin Use
163
+
164
+ The repo also ships Claude Code plugin metadata:
165
+
166
+ - `.claude-plugin/plugin.json`
167
+ - `.mcp.json`
168
+ - `skills/claude-intercom/SKILL.md`
169
+ - `commands/intercom.md` and `commands/intercom-id.md`
170
+
171
+ The plugin packages the MCP server and the bundled `claude-intercom` skill (which
172
+ gives Claude copy-paste coordination patterns). It also installs these Claude
173
+ Code slash commands:
174
+
175
+ - `/claude-intercom:intercom [target and message]` — list sessions and send a
176
+ message. Without arguments, Claude asks which peer to contact and what to send.
177
+ - `/claude-intercom:intercom-id` — print this session's stable, copyable
178
+ intercom target.
179
+
180
+ Claude custom commands are model-driven prompt commands, not native modal UI.
181
+ Claude namespaces plugin commands by plugin name, so an installed plugin cannot
182
+ claim the unqualified `/intercom` command globally.
183
+ They call the same MCP tools and work in a normal Claude Code session, but only
184
+ the attached `cci`/`ccim` wrappers can own the terminal and provide an immediate
185
+ Alt+M picker. Load the plugin for a single session with `--plugin-dir`:
186
+
187
+ ```bash
188
+ claude --plugin-dir /path/to/agent-intercom-claude # this session only
189
+ ```
190
+
191
+ For the minimal tool surface, prefer plain MCP registration
192
+ (`claude mcp add claude-intercom -- claude-intercom-mcp`) so you get the intercom
193
+ tools without the skill.
194
+
195
+ ## Tools
196
+
197
+ - `intercom_whoami`: show this session's intercom ID, name, cwd, and model.
198
+ - `intercom_team`: show the current manager and live coworkers owned by that manager.
199
+ - `intercom_status`: show connection status and pending message counts.
200
+ - `intercom_list`: list local Pi, Codex, and Claude sessions in your scope (protocol v4 is same-scope; cross-scope contact requires an exact full session ID).
201
+ - `intercom_set_summary`: publish a short discoverable status.
202
+ - `intercom_send`: send a non-blocking message.
203
+ - `intercom_ask`: send a question and wait for the target's reply.
204
+ - `intercom_pending`: read queued inbound messages and unresolved asks.
205
+ - `intercom_reply`: reply to a pending inbound ask; use `to` plus `which: "oldest" | "latest"` if one sender has multiple unresolved asks.
206
+
207
+ Pending output never exposes protocol message IDs. Keep at most one unresolved `intercom_ask` to the same recipient; the broker rejects a second ask and recommends `intercom_send` for a non-blocking follow-up. Use `intercom_send`—not `intercom_ask`—for assignments and progress/status checkpoints.
208
+
209
+ Persistent Claude workers and plain MCP runtimes automatically reconnect their stable Intercom identity after a broker restart, so a live worker does not need to be respawned merely to become reachable again.
210
+
211
+ Example:
212
+
213
+ ```typescript
214
+ intercom_team({})
215
+ // Manager: manager-id [connected]
216
+ // You: worker-a
217
+ // Coworkers: reviewer target=reviewer (codex, reviewer, running) [connected]
218
+
219
+ intercom_ask({
220
+ to: "worker-a",
221
+ message: "Please inspect the failing test and reply with the likely cause.",
222
+ timeout_ms: 45000
223
+ })
224
+ ```
225
+
226
+ Blocking asks default to a short bounded wait and reject waits over 120 seconds.
227
+ For longer work, use `intercom_send` and check later with `intercom_pending`.
228
+
229
+ ## Wakeable Workers With `cci`
230
+
231
+ `cci` (Claude Code Intercom) starts a single wakeable worker in the foreground.
232
+ It registers the worker on the broker. For every inbound message, the attached
233
+ terminal visibly prints the sender and message, a working indicator, and the
234
+ final Claude result or error. Blocking asks still receive that final result as
235
+ their automatic intercom reply. Press **Alt+M** for a numbered list of connected
236
+ peers, then choose one and enter a message. Press **Alt+I** to copy the worker's
237
+ contact target.
238
+
239
+ This is an attached worker console, not Claude Code's interactive TUI: woken
240
+ turns run through `claude -p`, and their final output is mirrored into the
241
+ console. To continue or inspect the full Claude conversation, run `claude
242
+ --resume <session-id>` using the session ID printed with the completed turn.
243
+ `ccim` has the same visible wake behavior and shortcuts.
244
+
245
+ Start a named worker:
246
+
247
+ ```bash
248
+ cci --name worker-a --id worker-a
249
+ ```
250
+
251
+ Flags (all optional; `ccim` accepts the same set):
252
+
253
+ | Flag | Meaning |
254
+ |------|---------|
255
+ | `--name <name>` | Discoverable session name other sessions target |
256
+ | `--id <id>` | Stable intercom session id (defaults to a git-derived id) |
257
+ | `--cwd <dir>` | Working directory for the worker's turns (default: cwd) |
258
+ | `--model <model>` | Model for woken turns (`opus`, `sonnet`, `haiku`, or a full id) |
259
+ | `--effort <level>` | Claude effort for every woken turn (`low`, `medium`, `high`, `xhigh`, or `max`) |
260
+ | `--instructions <text>` | System-prompt guidance appended to every woken turn |
261
+ | `--tui` / `--live` | Run as a LIVE interactive Claude session woken in place (see below) instead of a headless `claude -p` worker |
262
+ | `--minimal` / `--bare` | Run woken turns with `--safe-mode` (see below); implied by `ccim` (ignored with `--tui`) |
263
+ | `--safe` | Compatibility alias for the safe `manual` permission mode |
264
+ | `--yolo` / `--dangerously-skip-permissions` | Explicitly bypass permission checks (never the default) |
265
+ | `--permission-mode <mode>` | Validated against Claude Code 2.1.220 (`acceptEdits`, `auto`, `bypassPermissions`, `manual`, `dontAsk`, or `plan`) |
266
+ | `--add-dir <dir>` | Extra directory the worker may access (repeatable) |
267
+ | `--mcp-config <json\|file>` | Extra MCP servers for woken turns (e.g. to give the worker intercom tools) |
268
+ | `--state <path>` | Where to persist the worker's session id (default under `~/.pi/agent/intercom/`) |
269
+ | `--claude <cmd>` | Claude Code executable to invoke (default `claude`) |
270
+ | `--transport <auto\|native\|mcp>` | Delivery transport; `auto` uses native only for verified-compatible Claude versions |
271
+
272
+ ```bash
273
+ cci --cwd /path/to/project --instructions "Reply tersely. Ask before destructive changes."
274
+ cci --model opus --effort max --name reviewer --id reviewer
275
+ cci --yolo --name trusted-worker --id trusted-worker # explicit opt-in only
276
+ cci --add-dir ../shared-lib --name worker-a --id worker-a
277
+ ```
278
+
279
+ By default `cci` passes the standard `--permission-mode manual`; it never adds
280
+ `--dangerously-skip-permissions` on the user's behalf. Headless turns cannot
281
+ answer an interactive permission prompt, so choose a validated explicit mode
282
+ when a different non-interactive posture is required. `--yolo` remains an
283
+ explicit trusted-user opt-in outside hardened roles.
284
+
285
+ ## Live TUI Mode (`cci --tui`)
286
+
287
+ Default `cci` is a headless worker: each message spawns a `claude -p` turn. With
288
+ `--tui`, `cci` instead opens a **live interactive Claude session that you sit in
289
+ and that is woken in place** — the Codex `coi` experience. Inbound intercom
290
+ messages are injected into the running session and it replies over the broker;
291
+ you see everything and can type alongside it.
292
+
293
+ ```bash
294
+ cci --tui --name worker-a --id worker-a
295
+ ```
296
+
297
+ Claude Code has no Codex-style app-server. `cci --tui` therefore resolves one of two local transports before launch:
298
+
299
+ - **Native** bridges the Intercom broker to Claude Code's local cross-session Unix socket. Inbound messages appear as attributed peer messages in the live session; Claude must answer them with its built-in `SendMessage` tool so the bridge can preserve blocking ask/reply correlation. Native launches enable Claude's cross-session feature flag automatically. `auto` uses this only for the verified Claude Code compatibility window (currently 2.1.220–2.1.226). If native attachment fails under `auto`, `cci` restarts once with MCP; explicit `--transport native` fails closed instead.
300
+ - **MCP** is the preserved compatibility path. It launches Claude with the packaged plugin, whose MCP server registers the identity, appends inbound messages to a durable inbox, and auto-arms `monitors/monitors.json` to inject them with Claude Code's local Monitor mechanism. Blocking asks are answered with `intercom_reply`.
301
+
302
+ Choose explicitly with `--transport native` or `--transport mcp`, or set `CLAUDE_INTERCOM_TRANSPORT`. Worker JSON entries also accept `"transport": "auto" | "native" | "mcp"`. The Claude executable is probed with `claude --version`; unreadable or out-of-window versions never silently enable native mode.
303
+
304
+ `--minimal` is ignored in live TUI mode. The native path requires an interactive Claude process that publishes its local messaging socket. The MCP path additionally needs a built checkout (`npm run build`) and an available Monitor feature; Monitor is unavailable when `DISABLE_TELEMETRY` or `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set, or on Bedrock/Vertex/Foundry. Both paths remain local and work behind a custom `ANTHROPIC_BASE_URL`/proxy. See [docs/wake-mechanisms.md](docs/wake-mechanisms.md).
305
+
306
+ ## Normal And Minimal Workers
307
+
308
+ Like Codex's `coi` (normal) and `coim` (minimal), `cci` has a minimal mode. Codex
309
+ needs a dedicated `CODEX_HOME` and a hand-written `config.toml` to strip
310
+ memories, plugins, skills, and browser surfaces (while keeping `multi_agent`).
311
+ Claude Code has this built in: `cci --minimal` runs every woken turn with Claude
312
+ Code's `--safe-mode`, which disables CLAUDE.md, skills, plugins, hooks, and MCP
313
+ servers while keeping auth, built-in tools (Bash/Read/Edit/…), and permissions
314
+ working normally. It is the focused-worker profile: less prompt and tool surface,
315
+ same coding ability.
316
+
317
+ **Subagents are retained in minimal mode.** `--safe-mode` only disables *custom*
318
+ agent-type definitions (`.claude/agents/`), not the built-in `Task` tool — so a
319
+ minimal worker can still delegate to general-purpose subagents, matching Codex
320
+ minimal's `multi_agent = true`. This is verified end-to-end
321
+ (`test/e2e/minimal-subagent.sh`): a minimal worker spawns a subagent that runs a
322
+ shell command and reports back.
323
+
324
+ `cci` and `ccim` are installed as a matched pair (like Codex's `coi` and `coim`):
325
+ `ccim` is exactly `cci --minimal` — same flags, same identity handling, minimal
326
+ by default. You do not need an alias to enable minimal mode; aliases are useful
327
+ only for reusable names, IDs, paths, or permission settings.
328
+
329
+ ```bash
330
+ cci --name reviewer --id reviewer # normal: full config, CLAUDE.md, skills, MCP
331
+ ccim --name lean-worker --id lean-worker # minimal: --safe-mode woken turns
332
+ ccim --safe --name lean-safe --id lean-safe # minimal + standard permission prompts
333
+ cci --minimal --name worker-a --id worker-a # equivalent to `ccim ...`
334
+ ```
335
+
336
+ Because minimal mode disables MCP in the woken turn, a minimal worker cannot use
337
+ the intercom tools to message other sessions itself — it still receives work and
338
+ replies normally (the worker daemon captures its final message and sends the
339
+ reply). Use a normal worker when you want the woken turn to reach out to peers on
340
+ its own.
341
+
342
+ ## Manager And Worker Pattern
343
+
344
+ Use one Claude Code session as the manager and one or more `cci` workers.
345
+
346
+ Launch a worker in `tmux`:
347
+
348
+ ```bash
349
+ tmux new-session -d -s worker-a 'cd /path/to/project && cci --name worker-a --id worker-a'
350
+ ```
351
+
352
+ Then, from the manager session, delegate through the intercom tools:
353
+
354
+ ```typescript
355
+ intercom_ask({
356
+ to: "worker-a",
357
+ message: "Create a plan for adding retries to src/api/client.ts, then report your first step.",
358
+ timeout_ms: 60000
359
+ })
360
+ ```
361
+
362
+ For non-blocking delegation, use `intercom_send` and check back with
363
+ `intercom_pending`. For a decision you need before continuing, use
364
+ `intercom_ask`.
365
+
366
+ ## Worker Daemon (multiple workers)
367
+
368
+ Use `claude-intercom-worker` when you want one process to publish several
369
+ configured workers without a launcher per worker.
370
+
371
+ Create a config:
372
+
373
+ ```json
374
+ {
375
+ "statePath": "/path/to/intercom/claude-worker-state.json",
376
+ "claudeCommand": "claude",
377
+ "agents": [
378
+ {
379
+ "id": "claude-worker",
380
+ "name": "claude-worker",
381
+ "cwd": "/path/to/project",
382
+ "model": "sonnet",
383
+ "instructions": "Reply concisely. Ask before making destructive changes.",
384
+ "permissionMode": "manual"
385
+ }
386
+ ]
387
+ }
388
+ ```
389
+
390
+ Worker configuration validates permission modes and rejects permission flags
391
+ hidden in `claudeArgs`. A tightening-only `bossRole` hint of `adversary` or
392
+ `council` forces `--bare`, `permissionMode: "plan"`, and a `read-only` ceiling;
393
+ permission-granting settings, agents, plugins, and appended argv cannot widen
394
+ it. This local hint does not enroll a Boss participant or expose a reviewer
395
+ tool; those surfaces stay unavailable until a protected Controller supplies the
396
+ binding, transport, and durable dispatch path.
397
+
398
+ Start it:
399
+
400
+ ```bash
401
+ claude-intercom-worker --config "$HOME/.pi/agent/intercom/claude-worker.json"
402
+ ```
403
+
404
+ Each worker's `session_id` is persisted in `statePath`, so later messages resume
405
+ the same Claude conversation. The daemon reads a single worker's config from the
406
+ environment when no config file is given (`CLAUDE_INTERCOM_WORKER_ID`, `…_NAME`,
407
+ `…_CWD`, `…_MODEL`, `…_INSTRUCTIONS`, `…_STATE`).
408
+
409
+ ## Environment Variables
410
+
411
+ | Variable | Used by | Purpose |
412
+ |----------|---------|---------|
413
+ | `CLAUDE_INTERCOM_NAME` | MCP server | Discoverable session name |
414
+ | `CLAUDE_INTERCOM_SESSION_ID` | MCP server | Stable intercom id |
415
+ | `CLAUDE_INTERCOM_MODEL` | MCP server | Model label shown to peers |
416
+ | `CLAUDE_INTERCOM_EFFORT` | `cci` / `ccim` | Effort level forwarded to every Claude turn |
417
+ | `CLAUDE_INTERCOM_CWD` / `_INSTRUCTIONS` | `cci` / `ccim` | Defaults for `--cwd` / `--instructions` |
418
+ | `CLAUDE_INTERCOM_CLAUDE_COMMAND` | workers | Claude Code executable (default `claude`) |
419
+ | `CLAUDE_INTERCOM_WORKER_ID` / `_NAME` / `_CWD` / `_MODEL` / `_INSTRUCTIONS` / `_STATE` | `claude-intercom-worker` | Single-worker config when no `--config` file is given |
420
+ | `CLAUDE_INTERCOM_WORKER_CONFIG` | `claude-intercom-worker` | Path to the worker config JSON |
421
+ | `PI_INTERCOM_ASK_TIMEOUT_MS` | all | Default blocking-ask timeout (≤ 120000) |
422
+ | `PI_CODING_AGENT_DIR` | all | Overrides the `~/.pi/agent` base dir (broker socket + config live under it) |
423
+
424
+ The `PI_*` names are shared with the Pi, Codex, and OpenCode adapters on purpose —
425
+ all four read the same broker location and ask-timeout so they interoperate.
426
+
427
+ ## Development
428
+
429
+ ```bash
430
+ git clone https://github.com/ctliz/agent-intercom-claude.git
431
+ cd agent-intercom-claude
432
+ npm install
433
+ npm run build
434
+ npm test
435
+ ```
436
+
437
+ For MCP development, register the TypeScript source directly:
438
+
439
+ ```bash
440
+ claude mcp add claude-intercom-dev -- npx --no-install tsx ./claude/server.ts
441
+ ```
442
+
443
+ ## Agent Intercom Compatibility
444
+
445
+ `agent-intercom-pi` is the Pi-native adapter with overlays and inline rendering.
446
+ `agent-intercom-codex` is the Codex MCP/plugin adapter plus wake-on-message Codex
447
+ app-server sidecars. This repository, `agent-intercom-claude`, is the Claude Code
448
+ MCP/plugin adapter plus wake-on-message headless `claude -p` workers.
449
+ `agent-intercom-opencode` provides the native OpenCode plugin.
450
+
451
+ All four vendor the compatible local broker/client protocol and share one broker
452
+ socket, so a single session list spans Pi, Codex, Claude Code, and OpenCode.
453
+
454
+ ## Releasing
455
+
456
+ Releases are automated from version tags. Update `package.json`, the lockfile when
457
+ present, and `CHANGELOG.md` on `main`, then push an annotated tag that exactly
458
+ matches the package version:
459
+
460
+ ```bash
461
+ git tag -a vX.Y.Z -m "vX.Y.Z"
462
+ git push origin vX.Y.Z
463
+ ```
464
+
465
+ The release workflow verifies that the tag points into `main`, runs typecheck,
466
+ tests, and the build, publishes the public npm package with trusted OIDC
467
+ provenance, and creates the GitHub Release. Existing npm versions and GitHub
468
+ Releases are skipped safely when a workflow is rerun.
469
+
470
+ ## Compatibility, Migration & Rollback
471
+
472
+ - **Single Shared Broker**: The broker-capable adapters on the machine — `pi`, `claude`, `codex`, and `opencode` — connect to one local broker over a Unix domain socket (`~/.pi/agent/intercom/broker.sock` or `$PI_CODING_AGENT_DIR/intercom/broker.sock`).
473
+ - **Coordinated Upgrade Set**: Protocol v4 changes broker negotiation, so the broker-capable adapters that are *actually installed and enabled on this machine* must be upgraded together in one maintenance window. Adapters you do not use do not need to be installed to satisfy the upgrade. `@ctliz/agent-intercom-core` is an internal dependency that arrives with the adapters and is never installed or upgraded on its own.
474
+ - **Orchestrator is Optional**: `agent-intercom-orchestrator` is an optional Linux/systemd lifecycle component. It does not implement or start a Broker and is not part of the Broker compatibility set. Omitting it — for example on macOS, or when using TmuxDeck — is a fully supported configuration and is **not** a mixed or unsupported state. If it is installed on a supported Linux host, or on WSL with a systemd user manager enabled, update it together with the adapters it manages.
475
+ - **Fail-Closed Legacy Handling**: An incompatible legacy (v3) broker or client fails closed. It is rejected at negotiation and never killed, never downgraded, and never allowed to form a second broker island.
476
+ - **Rollback**: Rolling back covers only the components that were actually installed on this machine before the upgrade. Restore the exact specs and lockfiles you backed up, then reload the affected agent sessions. Roll Orchestrator back only if it was installed to begin with. There is no published pre-v4 tag under `ctliz`, so a pre-upgrade backup of the exact installed specs/locks is the supported rollback material. Leaving some installed broker-capable adapters on the old protocol while others are upgraded is an unsupported mixed state.
477
+
478
+ ## License
479
+
480
+ The current project is licensed under the [GNU Affero General Public License
481
+ v3.0 or later](LICENSE) (`AGPL-3.0-or-later`). If you modify this software and
482
+ make the modified version available to users over a network, the AGPL requires
483
+ you to offer those users the corresponding source code.
484
+
485
+ Portions derived from the original MIT-licensed `pi-intercom` project retain
486
+ their original notices. See [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) and
487
+ [licenses/MIT-pi-intercom.txt](licenses/MIT-pi-intercom.txt). Versions already
488
+ published under MIT remain available under their original terms. See
489
+ [LICENSE_TRANSITION.md](LICENSE_TRANSITION.md) for the exact commit and tag boundary.
490
+
491
+ ## Upgrading to the @ctliz namespace
492
+
493
+ The v4 release line renames the package namespace from `@dataforxyz/*` to `@ctliz/*`. The two namespaces are different packages to npm. For Claude Code, `0.12.0-connect.3` packages `monitors/monitors.json` alongside the plugin; other installed adapters update to their compatible v4 release (e.g. `connect.2`). Pi Git package installations deduplicate by repository URL without ref, but running agent sessions continue to execute legacy code in memory, and npm or global installs along with binary links can coexist and conflict. Operators must stop active sessions, clean active install surfaces, and follow remove-before-install — side-by-side installation is not supported.
494
+
495
+ 1. Back up the exact specs, lock files, and settings of every installed component.
496
+ 2. Stop or close the installed broker-capable adapters.
497
+ 3. Remove the old `@dataforxyz/*` specs, packages, and binary links that are actually installed.
498
+ 4. Assert the old identity is gone from the **active install surfaces of the current OS user**: Pi settings and extension specs, resolved managed install roots, actual `node_modules` installations, and conflicting binary links that the current `PATH` would resolve. Do not scan or delete unrelated source checkouts, historical documentation, or other users' files — a `@dataforxyz/*` string in an unrelated development clone is not an installation.
499
+ 5. Install the `@ctliz/*` packages for the components you actually use (e.g. `npm install -g @ctliz/agent-intercom-claude@connect` / `v0.12.0-connect.3`, and companion `connect.2` releases for other harnesses).
500
+ 6. Reload or restart, then verify exactly one broker is running.
501
+
502
+ **Classification rule.** Migration-aware setup and update tooling classifies an old-namespace-only install surface as `MIGRATION_REQUIRED`, and the simultaneous presence of both namespaces as a duplicate/dual-load hard error that refuses setup, update, and further installation. This tooling does not exist for every platform and adapter combination; where it is not available, apply the same two rules manually against the surfaces in step 4. Do not assume every adapter emits this code automatically.
503
+
504
+ **Rollback** reverses this and covers only the components that were installed on this machine before the upgrade: remove the `@ctliz/*` packages, then restore the backed-up exact `@dataforxyz/*` specs and locks. Roll Orchestrator back only if it was installed to begin with.
505
+
506
+ The `connect.1` tags, source commits, and published release assets are immutable and are not modified by this migration. Release notes may carry an explicit erratum, which corrects the description only and never moves a tag or replaces an asset.
@@ -0,0 +1,25 @@
1
+ # Third-Party Notices
2
+
3
+ ## pi-intercom
4
+
5
+ Portions of this repository are derived from [Nico Bailon's original
6
+ `pi-intercom`](https://github.com/nicobailon/pi-intercom) and work contributed
7
+ to that project.
8
+
9
+ The original work was made available under the MIT License. Its copyright and
10
+ permission notice are reproduced in [`licenses/MIT-pi-intercom.txt`](licenses/MIT-pi-intercom.txt).
11
+ Those notices must be retained with copies or substantial portions of the
12
+ original MIT-licensed material.
13
+
14
+ ## pi-claude-link
15
+
16
+ The native Claude Code registry and Unix-socket protocol implementation is
17
+ adapted from [`pi-claude-link`](https://github.com/alonw0/pi-claude-link),
18
+ copyright (c) 2026 alonw0 and made available under the MIT License. Its
19
+ copyright and permission notice are reproduced in
20
+ [`licenses/MIT-pi-claude-link.txt`](licenses/MIT-pi-claude-link.txt).
21
+
22
+ The current repository, including later modifications and the combined work,
23
+ is distributed under the GNU Affero General Public License v3.0 or later as
24
+ stated in [`LICENSE`](LICENSE). Previously published MIT-licensed versions
25
+ remain available under the terms under which they were released.
@@ -0,0 +1,76 @@
1
+ import { readFileSync } from "fs";
2
+ import { writeDurableJson } from "../durable-json.ts";
3
+ import type { RemoteAccessMetadata, RemoteRegistrationAccess } from "../types.ts";
4
+
5
+ export const ACCESS_CREDENTIAL_ENV = "AGENT_INTERCOM_ACCESS_CREDENTIAL_PATH";
6
+ export const ACCESS_CREDENTIAL_VERSION = 1;
7
+
8
+ export interface EnrollmentCredentialFile {
9
+ version?: typeof ACCESS_CREDENTIAL_VERSION;
10
+ enrollmentToken: string;
11
+ }
12
+
13
+ export interface SessionCredentialFile {
14
+ version: typeof ACCESS_CREDENTIAL_VERSION;
15
+ sessionCredential: string;
16
+ sessionId: string;
17
+ generation: number;
18
+ }
19
+
20
+ export interface LoadedRemoteAccessCredential {
21
+ path: string;
22
+ access: RemoteRegistrationAccess;
23
+ enrollment: boolean;
24
+ }
25
+
26
+ function nonEmptyString(value: unknown): value is string {
27
+ return typeof value === "string" && value.length > 0 && !value.includes("\0");
28
+ }
29
+
30
+ export function loadRemoteAccessCredential(env: NodeJS.ProcessEnv = process.env): LoadedRemoteAccessCredential | undefined {
31
+ const path = env[ACCESS_CREDENTIAL_ENV]?.trim();
32
+ if (!path) return undefined;
33
+ const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
34
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
35
+ throw new Error(`Invalid Agent Intercom access credential at ${path}`);
36
+ }
37
+ const credential = parsed as Record<string, unknown>;
38
+ if (nonEmptyString(credential.enrollmentToken)) {
39
+ return { path, access: { enrollmentToken: credential.enrollmentToken }, enrollment: true };
40
+ }
41
+ if (
42
+ credential.version === ACCESS_CREDENTIAL_VERSION
43
+ && nonEmptyString(credential.sessionCredential)
44
+ && nonEmptyString(credential.sessionId)
45
+ && typeof credential.generation === "number"
46
+ && Number.isSafeInteger(credential.generation)
47
+ && credential.generation > 0
48
+ ) {
49
+ return {
50
+ path,
51
+ access: {
52
+ sessionCredential: credential.sessionCredential,
53
+ sessionId: credential.sessionId,
54
+ generation: credential.generation,
55
+ },
56
+ enrollment: false,
57
+ };
58
+ }
59
+ throw new Error(`Invalid Agent Intercom access credential at ${path}`);
60
+ }
61
+
62
+ export function writeRemoteSessionCredential(
63
+ path: string,
64
+ sessionId: string,
65
+ metadata: RemoteAccessMetadata,
66
+ ): void {
67
+ if (!metadata.sessionCredential) {
68
+ throw new Error("Remote enrollment response omitted the session credential");
69
+ }
70
+ writeDurableJson(path, {
71
+ version: ACCESS_CREDENTIAL_VERSION,
72
+ sessionCredential: metadata.sessionCredential,
73
+ sessionId,
74
+ generation: metadata.generation,
75
+ } satisfies SessionCredentialFile);
76
+ }