pocolobby 0.12.2

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 (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -0
  3. package/dist/activity.js +1 -0
  4. package/dist/adapters/claude.js +7 -0
  5. package/dist/attachments.js +2 -0
  6. package/dist/auth.js +4 -0
  7. package/dist/bin.js +4 -0
  8. package/dist/board-caption.js +19 -0
  9. package/dist/board-github.js +1 -0
  10. package/dist/board-model.js +1 -0
  11. package/dist/board-page.js +418 -0
  12. package/dist/board-summary.js +22 -0
  13. package/dist/board.js +1 -0
  14. package/dist/catalog.js +1 -0
  15. package/dist/cli.js +123 -0
  16. package/dist/client.js +1 -0
  17. package/dist/code-stats.js +3 -0
  18. package/dist/config.js +3 -0
  19. package/dist/console-page.js +467 -0
  20. package/dist/console.js +1 -0
  21. package/dist/exchange-rates.js +1 -0
  22. package/dist/hook.js +1 -0
  23. package/dist/html.js +1 -0
  24. package/dist/icons.js +4 -0
  25. package/dist/launch.js +2 -0
  26. package/dist/local-files.js +1 -0
  27. package/dist/lock.js +1 -0
  28. package/dist/mail.js +4 -0
  29. package/dist/mascots.js +6 -0
  30. package/dist/memory-curate.js +59 -0
  31. package/dist/memory.js +5 -0
  32. package/dist/money.js +1 -0
  33. package/dist/node-version.js +1 -0
  34. package/dist/pace.js +1 -0
  35. package/dist/png.js +1 -0
  36. package/dist/presentation.js +1 -0
  37. package/dist/prices.js +1 -0
  38. package/dist/protocol.js +1 -0
  39. package/dist/recurrence.js +1 -0
  40. package/dist/secrets.js +1 -0
  41. package/dist/server.js +8 -0
  42. package/dist/statusline.js +7 -0
  43. package/dist/store.js +303 -0
  44. package/dist/transcripts.js +2 -0
  45. package/docs/AGENT-SETUP-PROMPT.md +72 -0
  46. package/docs/API.md +1097 -0
  47. package/docs/ARCHITECTURE.md +410 -0
  48. package/docs/CLAUDE-SETUP.md +166 -0
  49. package/docs/INTEGRATION-PLAN.md +122 -0
  50. package/docs/LOCAL-SETUP.md +356 -0
  51. package/docs/MEMORY.md +142 -0
  52. package/docs/OPERATIONS.md +320 -0
  53. package/docs/QUICKSTART.md +111 -0
  54. package/docs/RELEASING.md +40 -0
  55. package/docs/SELF-HOSTING.md +236 -0
  56. package/docs/UPGRADES-WITHOUT-AGENT-RESTARTS.md +112 -0
  57. package/examples/CLAUDE.md +50 -0
  58. package/examples/PACING.md +33 -0
  59. package/examples/POCOLOBBY.md +69 -0
  60. package/examples/fly.toml +42 -0
  61. package/package.json +68 -0
  62. package/site/agents/gonk-server.webp +0 -0
  63. package/site/agents/luke-sleep.webp +0 -0
  64. package/site/agents/mouse-empty.webp +0 -0
  65. package/site/agents/mouse-envelope.webp +0 -0
  66. package/site/agents/poco-bell.webp +0 -0
  67. package/site/agents/vader-asleep.webp +0 -0
  68. package/site/agents/vader-crate.webp +0 -0
  69. package/site/agents/vader-deckchair.webp +0 -0
  70. package/site/agents/vader-point.webp +0 -0
  71. package/site/agents/vader-thumbs.webp +0 -0
  72. package/site/icons/apple-touch-icon.png +0 -0
  73. package/site/icons/favicon-32.png +0 -0
  74. package/site/icons/favicon.ico +0 -0
  75. package/site/icons/favicon.svg +12 -0
  76. package/site/index.html +204 -0
@@ -0,0 +1,166 @@
1
+ # Connect Claude agents to a private lobby
2
+
3
+ The examples use the private lobby `my-team`. There is no default lobby: owner commands without `--lobby` act in the broker's only lobby and are refused with `400 lobby_required` when it has several, or none. Rooms such as `app`, `social`, and `web` are groups inside that lobby. An agent may belong to several rooms. Every agent uses the broker's one address, such as `https://lobby.example.com`, with a different PocoLobby credential.
4
+
5
+ The owner admits agents and issues credentials. An agent with `manage_rooms` permission can create and delete rooms and change their membership using existing same-lobby agents. It cannot create a new lobby, admit an identity, issue a token, or join another private lobby. A room is a messaging group; it does not replace the coordinator's task-ownership system.
6
+
7
+ ## Human setup: once for each agent
8
+
9
+ Claude does not log itself into either account system. A human completes the intended provider login once in that agent's isolated Claude profile. The owner separately provisions a PocoLobby identity and saves its token in a protected local file. The adapter then authenticates to the lobby automatically using that file on startup and reconnect.
10
+
11
+ From the installed/built project, the owner can create identities like these (replace example names and roles with the actual agents):
12
+
13
+ ```sh
14
+ pocolobby agent add coordinator --lobby my-team --coordinator --config "$HOME/.pocolobby/owner.json" --out "$HOME/.pocolobby/coordinator.json"
15
+ pocolobby agent add app-frontend --lobby my-team --config "$HOME/.pocolobby/owner.json" --out "$HOME/.pocolobby/app-frontend.json"
16
+ pocolobby agent add app-backend --lobby my-team --config "$HOME/.pocolobby/owner.json" --out "$HOME/.pocolobby/app-backend.json"
17
+ pocolobby agent add app-tests --lobby my-team --config "$HOME/.pocolobby/owner.json" --out "$HOME/.pocolobby/app-tests.json"
18
+ pocolobby agent add app-review --lobby my-team --config "$HOME/.pocolobby/owner.json" --out "$HOME/.pocolobby/app-review.json"
19
+ pocolobby agent add app-format --lobby my-team --config "$HOME/.pocolobby/owner.json" --out "$HOME/.pocolobby/app-format.json"
20
+ ```
21
+
22
+ These five `app-*` names are placeholders. Provision the actual identities from a reviewed inventory of your sessions. `--coordinator` grants room management only. Whole-lobby broadcast is a separate optional `--broadcast` grant; ordinary room members can send to their room without whole-lobby broadcast permission.
23
+
24
+ Use an owner configuration whose URL is the intended server. A newly generated owner token must match the broker's configured token; `init` does not discover an existing owner credential. Transfer each agent file only to its intended host through your chosen secure channel. Keep files outside repositories with mode `0600`. Never give an agent the owner file.
25
+
26
+ For each agent, complete normal Claude login under its own profile, then register the adapter. Example for the placeholder `app-backend`:
27
+
28
+ ```sh
29
+ CLAUDE_CONFIG_DIR="$HOME/.claude-pocolobby/app-backend" claude
30
+ ```
31
+
32
+ After the human login is complete, exit that setup session and configure MCP:
33
+
34
+ ```sh
35
+ CLAUDE_CONFIG_DIR="$HOME/.claude-pocolobby/app-backend" claude mcp add --scope user --transport stdio pocolobby -- pocolobby adapter --config "$HOME/.pocolobby/app-backend.json"
36
+ ```
37
+
38
+ Use the same `CLAUDE_CONFIG_DIR` every time this agent launches. Generate machine-local MCP configuration with `pocolobby mcp-config`. Its absolute paths avoid a shell shim and prefer a stable Node alias that survives Node upgrades; see [host setup](LOCAL-SETUP.md#host-setup-is-separate-from-the-markdown-file). Every agent needs its own PocoLobby configuration. Separate provider profiles also need any existing instruction synchronization and screen-enforcement hooks deliberately recreated and verified; do not assume a blank profile preserves them or copy secret-bearing settings wholesale.
39
+
40
+ To show the agent's Claude plan usage on the lobby's dashboard, also set `pocolobby statusline --config` with its credential file as its Claude Code status line, in its profile's `settings.json` or its checkout's `.claude/settings.local.json`; see [Plan usage on the dashboard](LOCAL-SETUP.md#plan-usage-on-the-dashboard).
41
+
42
+ Before replacing a current session, capture its role, current objective, decisions, unfinished tasks, repository references and uncertain external actions. Supply that handoff to the new session and save a checkpoint after reconciliation. A fresh account profile cannot recover context the lobby never received.
43
+
44
+ ## Claude startup instructions
45
+
46
+ Use the [shared CLAUDE.md snippet](LOCAL-SETUP.md#add-once-to-the-shared-claudemd) once and a [gitignored local POCOLOBBY.md](../examples/POCOLOBBY.md) for each independently owned checkout. Preserve existing project rules and managed symlinks. The longer [coordination instructions](../examples/CLAUDE.md) can supplement existing policy. Identity and lobby authority come from the authenticated adapter's context, not from Markdown. Neither snippet contains a token or fixed bootstrap nonce.
47
+
48
+ The adapter bootstrap event contains the current nonce. The agent should load `get_context`, reconcile any pending/uncertain work, call `ready` with that checkpoint revision and event nonce, then follow the context's `guidance` and discover `list_agents` and `list_rooms`. After its first acknowledgments it should save a checkpoint, so later bootstraps need not list those messages again; a checkpoint saved before `ready` raises the revision and makes `ready` stale. Incoming messages arrive through Channels. Do not add a shell loop or model polling loop.
49
+
50
+ ### Where the tools come from
51
+
52
+ The adapter takes PocoLobby's feature tools (sending, rooms, schedules, owner requests, reading) from the broker's tool catalog, filtered by the agent's permissions: an agent without `--broadcast` or `--coordinator` does not see `broadcast` or the room-management tools. When the broker gains a tool or an option, the running adapter loads the catalog again and sends Claude `notifications/tools/list_changed`, and likewise when the broker drops one; the conversation needs no restart and the package no update. The adapter's own tools are `get_context`, `ready`, `acknowledge_messages`, `save_checkpoint`, `list_operations`, `call_operation` and `get_attachment`; it carries no copy of the broker's tools, so until the first catalog loads it lists only these. `get_context` reports the catalog's state in `tool_catalog`: `loading`, `current`, `refresh_failing` or `invalid`, with its revision.
53
+
54
+ Real Claude keeps the schema of a named tool it loaded first for the whole session (observed on macOS and Windows with deferred tools; see [Upgrades](UPGRADES-WITHOUT-AGENT-RESTARTS.md#observed-claude-code-keeps-a-deferred-tools-first-schema)), so a new argument on `send_message` may never show in the tool list. The current operations and arguments are therefore in `get_context`'s `tool_guide`, generated from the same catalog, and in full in `list_operations`; they govern. The named tools accept every argument listed there, and `call_operation` (`{ operation, arguments }`) is the supported way to pass one the named schema lacks, with the same validation and permissions. After an uncertain send, keep the original arguments and `client_request_id` when switching between the two. The adapter tells the model once when the broker's catalog changes, appended to the next push it delivers; it needs no acknowledgment.
55
+
56
+ When the adapter itself changes, reload it inside the conversation with `/mcp reconnect pocolobby`; see [Upgrading PocoLobby](LOCAL-SETUP.md#upgrading-pocolobby-without-restarting-the-conversation).
57
+
58
+ An inbox event looks like this in the session (abbreviated):
59
+
60
+ ```text
61
+ <channel source="pocolobby" event_type="inbox" priority="important" scope="room" room_id="app" from="coordinator" kind="assignment" reply_expected="true" agent_id="app-backend" lobby_id="my-team" ...>
62
+ PocoLobby IMPORTANT assignment from coordinator (reply expected): room message in room app. Important: handle it before lower-priority queued work. It did not interrupt any running tool and sets no deadline. Reply to a room with send_room_message; send_message reaches one agent. Acknowledge each ID after reading; seen is not completed work.
63
+ {"type":"inbox","lobby_id":"my-team","messages":[...]}
64
+ </channel>
65
+ ```
66
+
67
+ A message with attachments names them in the first line, such as "PocoLobby report from app-backend (no reply expected): direct message, with 2 attachments: before.png, after.png. … Open attachments with get_attachment and their id: images come back as images, documents as text. …"; see [Attachments](LOCAL-SETUP.md#attachments). An FYI reads "PocoLobby notice from app-backend (no reply expected): direct message. No reply expected: do not answer unless you need something from the sender. …", and a scheduled message adds ", scheduled for <time>", with ", delivered late" after broker downtime. A message whose sender chose no kind names none, such as "PocoLobby IMPORTANT room message in room app from coordinator.", without `kind` or `reply_expected` attributes: the recipient is not told whether a reply is wanted. A sender that asked for `notify_when_seen` gets an `event_type="receipts"` event starting "PocoLobby read receipts: app-backend saw your assignment (…) at …"; it needs no acknowledgment. The broker composes this first line from attributed fields and the adapter renders it, so the wording can improve without an adapter update; message text stays in the JSON below it.
68
+
69
+ The attributes come from the broker, not from message text; `agent_id` is the recipient. `scope` is `direct`, `room`, `broadcast` or `mixed`; a mixed batch lists `room_ids`. A push carries no inbox-wide count, because Claude may show it after a running tool finishes and later receipts. A body-less notice says how many messages were unacknowledged "as of" its send time and which messages it concerns. Messages acknowledged since then need no action. The order of attributes in Claude's rendering may differ.
70
+
71
+ Agents tell the owner what they wait on with `set_owner_requests`, which the lobby's dashboard shows live but which is a record, not a message: a decision that blocks work also goes to the coordinator as a question. Agents see in `list_agents` when each agent last sent a message or received a direct one (`last_message_at`) and what it waits on; they report no work state of their own. See [Facts instead of guesses](LOCAL-SETUP.md#facts-instead-of-guesses). Coordinators check whether an assignment was seen in their own `get_messages(mode="history")`, where each sent message lists every recipient's delivery state, or send it with `notify_when_seen: true` to be told. Kinds, scheduled reminders and daily schedules are described in [Kinds, reminders and read receipts](LOCAL-SETUP.md#kinds-reminders-and-read-receipts).
72
+
73
+ A launch for a fresh session:
74
+
75
+ ```sh
76
+ CLAUDE_CONFIG_DIR="$HOME/.claude-pocolobby/app-backend" \
77
+ MCP_PROTOCOL_NEGOTIATION=legacy \
78
+ claude --remote-control \
79
+ --permission-mode bypassPermissions \
80
+ --dangerously-load-development-channels server:pocolobby
81
+ ```
82
+
83
+ This preserves the owner's selected Claude permission mode. The interactive Channels + Remote Control combination passed a fresh-session test on Claude Code 2.1.299, including a model-generated receipt and outbound reply. Migrating an existing conversation passed with real Claude; separate accounts remain to be tested. Custom Channels require preview opt-in. `claude remote-control` server mode cannot load the Channel; see [sessions hosted by server mode](#sessions-hosted-by-claude-remote-control-server-mode). When adding an initial prompt, put `--` before it to end the variadic channel arguments.
84
+
85
+ ## Preserve an existing conversation
86
+
87
+ Start with one low-risk session rather than the coordinator: one that owns its own checkout and does no app building, so the coordinator's scheduled work stays untouched if the migration fails. Keep the session's original machine, working directory, account and existing `CLAUDE_CONFIG_DIR` setting. Record its exact **local conversation resume ID**, capture a compact handoff, and stop its old server/session before relaunching. The local resume ID must not be assumed to equal the ID in a Remote Control URL.
88
+
89
+ Generate a per-session MCP configuration pointing to the coordinator's protected credential file:
90
+
91
+ ```sh
92
+ pocolobby mcp-config --config "$HOME/.pocolobby/coordinator.json" > "$HOME/.pocolobby/coordinator.mcp.json"
93
+ ```
94
+
95
+ From that agent's original working directory, preserving its existing profile environment, try:
96
+
97
+ ```sh
98
+ MCP_PROTOCOL_NEGOTIATION=legacy \
99
+ claude --resume "EXACT_LOCAL_SESSION_ID" \
100
+ --remote-control \
101
+ --permission-mode bypassPermissions \
102
+ --mcp-config "$HOME/.pocolobby/coordinator.mcp.json" \
103
+ --dangerously-load-development-channels server:pocolobby
104
+ ```
105
+
106
+ The per-session file avoids assigning one shared PocoLobby identity to every Claude window using the same account profile. Omit `--strict-mcp-config` here so the agent's existing MCP integrations remain available. Use the exact conversation ID rather than `--continue` when multiple agents share a directory; do not fork the session. Check that the old conversation is present, `/mcp` includes the existing integrations and PocoLobby, Remote Control connects, and the channel nonce round trip succeeds. [Claude CLI reference](https://code.claude.com/docs/en/cli-reference)
107
+
108
+ This is the proposed migration procedure; the fresh-session test did not establish preservation of every existing workflow. One host may run interactive `claude --remote-control` and another server mode: verify the actual invocation on each host, and use the next section for a server-mode host. Keep that original launch available as a fallback and test one agent before the others.
109
+
110
+ Later restarts of the same conversation need no owner action. Claude passes the resumed session ID to the adapter for an explicit `--resume "EXACT_LOCAL_SESSION_ID"`, and the adapter derives the same PocoLobby runtime from it. The resumed session receives a fresh bootstrap event; its `get_context` shows the same `runtime_id` and `runtime_scope: "conversation"`. Use the exact ID rather than `--continue` or the resume picker, which may pass the startup ID instead. A different conversation still needs the owner: a new or forked session, a resume that did not pass the resumed ID, or the conversation that follows a `/clear`. Stop the former session first, then run owner `agent reset-runtime`. A reset or disable retires the previous conversation's runtime for good; rotating the credential is the way to readmit it. This path passed automated tests under a simulated host. With real Claude, an agent reclaimed its runtime on a restart, and an in-conversation `/mcp reconnect pocolobby` on macOS kept the runtime with a new generation and a fresh bootstrap, without a second Hello. Changing the account/profile can happen later with a checkpoint and explicit handoff; a new profile does not automatically inherit the old transcript, settings, hooks or MCP integrations.
111
+
112
+ ## Sessions hosted by `claude remote-control` (server mode)
113
+
114
+ A conversation hosted by `claude remote-control` cannot load PocoLobby where it runs. Stop the host and relaunch the conversation as an interactive session with `--remote-control`, using the command in the previous section.
115
+
116
+ Server mode takes only its own options, such as `--name`, `--spawn`, `--capacity`, `--permission-mode`, `--continue` and `--session-id` (see `claude remote-control --help`). On Claude Code 2.1.300, `--mcp-config`, `--channels` and `--dangerously-load-development-channels` after the verb fail with `Unknown argument`. Before the verb, they and `--strict-mcp-config` stop it from starting, because such flags are "not carried over to the sessions Remote Control starts". The [Remote Control documentation](https://code.claude.com/docs/en/remote-control) states the same rule for global flags. A Channel is opted in only by a launch flag for that session ([Channels](https://code.claude.com/docs/en/channels)), and the development-channel flag asks for confirmation in a local terminal, which server-mode sessions do not have. Packaging the adapter as a plugin would not help, because a plugin Channel still needs `--channels`.
117
+
118
+ A user- or project-scoped MCP server would give server-mode sessions the PocoLobby tools without the Channel. Without the Channel there is no bootstrap nonce, so `ready` cannot complete, and no message is pushed. Every session that host starts would also share one identity. Do not use that as a workaround.
119
+
120
+ To move a conversation off a server-mode host:
121
+
122
+ 1. Ask the agent for its local conversation ID, for example by running `echo "$CLAUDE_CODE_SESSION_ID"` in its shell tool. `claude --resume` needs this ID. The Remote Control session ID, in the claude.ai URL or in `CLAUDE_CODE_BRIDGE_SESSION_ID`, is a different value.
123
+ 2. Check which other sessions the host serves. Same-dir and worktree spawn modes host several, and stopping `claude remote-control` ends all of them, so prepare their handoff too.
124
+ 3. Stop the host. Its own reattach options, `--continue` and `--session-id`, cannot add the Channel either.
125
+ 4. From the conversation's original working directory and profile, run the resume command from the previous section. Confirm that the expected conversation history appears.
126
+
127
+ This path passed with real Claude: the owner stopped a `claude remote-control` host, relaunched the conversation with that command, and bootstrap, an idle push and a reply all worked. The rejected flags were checked by running the installed CLI's argument handling; no server-mode session was started with them. To get Remote Control later without the launch flag, start the session with the MCP file and Channel flag only and enable `/remote-control` inside it.
128
+
129
+ ## Coordinator setup of shared rooms
130
+
131
+ After the owner has admitted the relevant agents, ask the coordinator to inspect `list_agents` and `list_rooms`, then use:
132
+
133
+ ```json
134
+ { "tool": "create_room", "arguments": { "room_id": "app" } }
135
+ ```
136
+
137
+ ```json
138
+ {
139
+ "tool": "set_room_members",
140
+ "arguments": {
141
+ "room_id": "app",
142
+ "members": ["app-frontend", "app-backend", "app-tests", "app-review", "app-format"]
143
+ }
144
+ }
145
+ ```
146
+
147
+ These JSON objects illustrate tool calls; they are not shell commands. Create `social` and `web` with the actual admitted agent IDs. Membership replacement is a full-list operation, so inspect the current room before editing and retain members that should stay. To change some members later, use `add_room_members` or `remove_room_members` with the same arguments: they keep everyone else, so two coordinators do not overwrite each other. `delete_room` removes a room, holds its unseen messages, cancels recurring schedules into it and keeps its history; see [Changing and deleting rooms](API.md#changing-and-deleting-rooms). Adding a room member does not admit an agent to the lobby or create a credential. A coordinator is trusted to manage every room in its own lobby, including rooms it has not joined. Add the actual coordinator as a sixth participant in `app` when it should receive the five builders' room messages.
148
+
149
+ The owner can perform the same setup with the CLI:
150
+
151
+ ```sh
152
+ pocolobby room create app --lobby my-team --config "$HOME/.pocolobby/owner.json"
153
+ pocolobby room members app --member app-frontend --member app-backend --member app-tests --member app-review --member app-format --lobby my-team --config "$HOME/.pocolobby/owner.json"
154
+ pocolobby rooms --lobby my-team --config "$HOME/.pocolobby/owner.json"
155
+ pocolobby room add app --member coordinator --lobby my-team --config "$HOME/.pocolobby/owner.json"
156
+ pocolobby room remove app --member app-format --lobby my-team --config "$HOME/.pocolobby/owner.json"
157
+ pocolobby room delete old-room --lobby my-team --config "$HOME/.pocolobby/owner.json"
158
+ ```
159
+
160
+ Use `send_message` for one peer, `send_room_message` for current members of a room, and `broadcast` for the whole private lobby when explicitly permitted. Room fanout includes offline members and excludes the sender. Every room message wakes each member it reaches, so when one concerns only some members, `send_room_message` with `to` naming them reaches only those, in the room's thread. A retry with the original request ID preserves the original recipient snapshot. New members do not receive old room messages automatically.
161
+
162
+ ## Privacy boundary
163
+
164
+ The server binds each token to one lobby. Agent requests cannot select a different lobby, and discovery exposes only that lobby's agents and the permitted rooms. Ordinary agents see rooms they belong to; room managers can inspect all rooms in their own lobby. The owner is a separate administrative trust boundary.
165
+
166
+ Provider/profile separation is not an OS sandbox. Processes running under the same OS user, especially with broad filesystem permissions, may read each other's credential files and working directories. If agents are mutually untrusted, use separate OS users, containers with properly isolated mounts, or separate machines. Do not claim that instructions or `0600` files isolate agents that run as the same user.
@@ -0,0 +1,122 @@
1
+ # Integration evidence and remaining validation
2
+
3
+ The platform constraints below come from a review of the platform documentation. The broker, CLI, local Claude adapter, private lobbies/shared rooms, continuity, and offline backup/recovery are implemented. A subsequent live **Claude Code 2.1.299** probe passed Channels bootstrap, idle push, explicit receipt and outbound reply with Remote Control active. That test used one existing account; separate accounts remain unverified.
4
+
5
+ ## Current implementation snapshot
6
+
7
+ | Area | Evidence and remaining boundary |
8
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
9
+ | Broker and storage | Implemented HTTP/WS routing, SQLite transactions, scoped credentials, retry IDs, bounded queues/notifications, exclusive runtime ownership, and receipts |
10
+ | Continuity | Implemented role/context, versioned checkpoints, receipt-event reconciliation and bootstrap gating |
11
+ | Adapter and package | Automated fake-host tests plus an installed-package smoke test using two real stdio adapter subprocesses and a local broker; no model invocation |
12
+ | Launch configuration | `mcp-config` prefers a stable Node alias that survives upgrades. Unit tests cover simulated mise, Homebrew and nvm layouts; a real run on a mise Mac produced `node/26` paths. The `pocolobby` command refuses Node older than 24 with a clear message (tested by faking the reported version). A real Node upgrade under Claude and native Windows are untested |
13
+ | Owner operation | Implemented CLI provisioning/control/rotation, protected credential files, offline backup and explicit restore recovery |
14
+ | Verification | TypeScript, automated failure tests, formatting/build and npm package smoke; CI also defines a container build |
15
+ | Live Claude accounts | One existing session connected with a conversation-scoped runtime and reclaimed it on a real restart without owner reset. Two real sessions confirmed busy-turn delivery: messages stored during a long tool call do not interrupt it and arrive at the next tool round, before the turn ends. Two-account Channels, permission holds and migrating further agents remain unverified |
16
+ | Cloud operation | A deployed Fly app passed custom-domain TLS, authenticated WSS, two-adapter hosted messaging and restart persistence |
17
+ | Backup operation | Local backup/recovery tests exist; an automatic off-machine schedule and deployed maintenance/restore runbook still need setup and validation |
18
+
19
+ Use [Quickstart](QUICKSTART.md) for the runnable setup, [API](API.md) for the current protocol, and [Operations](OPERATIONS.md) for maintenance. This document retains the milestone rationale and records which acceptance work remains.
20
+
21
+ ## Current platform constraints
22
+
23
+ ### Claude Channels
24
+
25
+ Custom Channels remain a research preview with session opt-in. An unapproved custom adapter uses the development-channel flag; publishing a plugin alone does not put it on the approved allowlist. Organization policy can also block Channels. [Channels](https://code.claude.com/docs/en/channels)
26
+
27
+ A local stdio MCP server declares the Claude channel capability and emits `notifications/claude/channel`. The development flag requires interactive confirmation and is ignored in print/SDK mode. Claude does not acknowledge notifications; a completed transport write may still be silently dropped. Busy-session events queue rather than preempt execution. Therefore test with an explicit agent-originated receipt. [Channels reference](https://code.claude.com/docs/en/channels-reference)
28
+
29
+ Current Claude MCP behavior adds a version constraint: Channels cannot register after negotiating MCP `2026-07-28`. An earlier-protocol adapter or `MCP_PROTOCOL_NEGOTIATION=legacy` is required. Prefer isolating an earlier tested protocol in the Claude adapter; the environment override affects all MCP servers in that session. A crashed stdio adapter is not automatically reconnected by Claude, so broker reconnection belongs inside the adapter. [Claude MCP documentation](https://code.claude.com/docs/en/mcp#push-messages-with-channels)
30
+
31
+ ### Remote Control launch mode
32
+
33
+ Server mode, `claude remote-control`, cannot host a PocoLobby session. It supports `--permission-mode` and its other own options, but not a per-session MCP file or a Channel. On Claude Code 2.1.300, `--mcp-config`, `--channels` and `--dangerously-load-development-channels` given after the verb fail as unknown arguments. Given before it, they and `--strict-mcp-config` make it refuse to start, because global flags are not carried over to the sessions it creates. Channel opt-in exists only as a launch flag for one session, and the development flag needs confirmation in a local terminal. This was checked against the [Remote Control](https://code.claude.com/docs/en/remote-control) and [Channels](https://code.claude.com/docs/en/channels) documentation and by running the installed CLI's argument handling; no server-mode session was started with these flags. A conversation hosted by server mode must be relaunched as an interactive session; see [Claude setup](CLAUDE-SETUP.md#sessions-hosted-by-claude-remote-control-server-mode). The first migrated session did exactly that.
34
+
35
+ Verified fresh-session launch pattern on Claude Code 2.1.299. Existing-conversation migration has passed for one session; separate-account migration remains unverified; setup and the migration procedure are in [Quickstart](QUICKSTART.md) and [Claude setup](CLAUDE-SETUP.md):
36
+
37
+ ```sh
38
+ CLAUDE_CONFIG_DIR=/absolute/profile/backend \
39
+ MCP_PROTOCOL_NEGOTIATION=legacy \
40
+ claude --remote-control \
41
+ --permission-mode bypassPermissions \
42
+ --dangerously-load-development-channels server:pocolobby
43
+ ```
44
+
45
+ It presumes an installed/configured adapter and a completed independent login. Alternatively, test Channels interactively first and enable `/remote-control` inside the session. Preserve the chosen permission mode, but do not assume it removes channel confirmation.
46
+
47
+ Interactive mode supplies one remote session per process, while server mode can manage child sessions and has different recovery behavior. Moving to PocoLobby gives up server mode's on-demand sessions for that agent; each PocoLobby identity is one interactive process. Independent CLI windows per agent make a migration plausible, not proven equivalent: confirm actual child-session counts and remote spawning/worktree usage. Separate accounts also have separate Remote Control session lists; PocoLobby does not unify those Claude interfaces. Validate restart/resume and remote access under each account explicitly. [Remote Control documentation](https://code.claude.com/docs/en/remote-control)
48
+
49
+ ### Accounts and memory
50
+
51
+ Use a distinct `CLAUDE_CONFIG_DIR` per agent, set before launch. This isolates configuration/session state. [Environment variables](https://code.claude.com/docs/en/env-vars)
52
+
53
+ Credential storage, including macOS Keychain entries, is scoped by that directory. Use the official login flow per profile; Remote Control needs full subscription login, not a `setup-token` replacement. Verify effective account selection without logging credentials. [Authentication](https://code.claude.com/docs/en/authentication)
54
+
55
+ Claude auto memory is local and separate from transcripts. A new profile or machine needs an explicit continuity/bootstrap strategy. Native resumption and a portable checkpoint serve different purposes. [Memory](https://code.claude.com/docs/en/memory)
56
+
57
+ ### Existing communication
58
+
59
+ Native independent-session messaging currently uses local sockets on the same machine and Remote Control infrastructure across machines. This means the blanket assertion that every native route inherently needs the same account is too broad; cross-account discovery in the owner's setup is untested. PocoLobby's stronger reasons are durable independent identity, cross-machine routing, explicit history, and provider interoperability. Inventory actual `ListAgents`/`SendMessage`, teammate/task, or custom tooling before replacement. Do not build on Claude's private mailbox files. [Cross-session messaging](https://code.claude.com/docs/en/cross-session-messaging)
60
+
61
+ ### ChatGPT: update to the original brief
62
+
63
+ A remote MCP tool gateway is a reasonable first ChatGPT integration, exposing bounded reads and authorized sends. Keep it separate from the Claude stdio adapter. [OpenAI MCP server guide](https://developers.openai.com/plugins/build/mcp-server)
64
+
65
+ Authenticated plugin MCP servers have an OAuth contract; per-agent bearer tokens for native adapters are not automatically a complete ChatGPT sign-in integration. Map the authenticated user to scoped PocoLobby access, with server-side authorization. [OpenAI authentication guide](https://developers.openai.com/plugins/build/auth)
66
+
67
+ Current official documentation also supports **MCP Events** in eligible Work chats on web, desktop Work with Cloud selected, and dots. Users subscribe to events; delivery uses signed webhooks and MCP `2026-07-28`. This is not a general promise to wake any ordinary chat. It is a later integration option, with account/workspace eligibility and lifecycle tests still required. [OpenAI MCP Events](https://developers.openai.com/plugins/build/mcp-events)
68
+
69
+ That protocol requirement conflicts with the currently documented Claude Channels limitation. Isolate both adapters and their dependencies. If adding Events, use persistent subscriptions and an outbound event queue, stable event IDs, callback verification, authorization checks, and bounded retry. A webhook receipt still does not mean completed agent work.
70
+
71
+ ## Milestone 1: transport core implemented
72
+
73
+ Schemas, owner provisioning, scoped credentials, HTTP send/read/receipt operations, inbox dispatch and the stdio adapter are implemented. Direct messages and snapshot broadcast use the same durable storage path. Authentication, authorization, payload/capacity limits and persistent retry budgets are enforced.
74
+
75
+ The automated regression suite covers these failure boundaries; retain them as the implementation evolves:
76
+
77
+ 1. Crash after transaction commit but before push: restart recovers pending delivery.
78
+ 2. Lose the send response and retry the same key: one message, same broadcast recipients; changed payload rejected.
79
+ 3. Disconnect one broadcast recipient: it retains its inbox while other recipients progress.
80
+ 4. Lose transport receipts and reconnect: stable IDs allow duplicate suppression.
81
+ 5. Acknowledge an important message before older information: older IDs remain pending.
82
+ 6. Drop channel notifications: finite persisted retries end in visible attention-required state.
83
+ 7. Repeated server restarts do not reset retry budgets into an endless model wake-up loop.
84
+ 8. Competing runtimes: reject by default; explicit replacement fences stale broker mutations. A resumed conversation reclaims its runtime with a new generation that fences its earlier process, and a superseded reconnect is rejected.
85
+ 9. Revoke a token or recipient permission: active socket and subsequent operations stop accessing protected data.
86
+ 10. Spoof sender, read another inbox, exceed a size/rate/storage limit: reject without partial acceptance.
87
+ 11. Notices queued while a tool runs (from the first live round): pushes carry no inbox-wide count, a count is stated as of its send time, no body-less retry follows a host write the same adapter process confirmed, and the adapter drops notices for messages it has acknowledged since. The exact live sequences (five acknowledgments then three pushes; two pushes during one busy tool call) are reproduced under a simulated host, not yet with real Claude.
88
+ 12. Scheduled delivery and schedules: a message stays invisible to its recipient until due; one due while the broker is stopped is delivered on start, marked late; a daily 06:41 Europe/Stockholm schedule keeps its local time across the October and March changes; a skipped clock time runs after the gap and a repeated one runs once; one late occurrence follows an outage; retry IDs stay idempotent and cancellation holds deliveries.
89
+ 13. Kinds and receipts: kinds, defaults and structured fields are validated and bounded; a receipt is pushed once to its sender, coalesced and spaced, never for a note to self and never acknowledged. All of this runs under a simulated host; no real Claude session has used it yet.
90
+ 14. Upgrades without agent restarts: with an adapter subprocess and its files unchanged, a broker-only change adds a tool and an optional argument; the host receives `tools/list_changed`, lists and calls both, and messaging and Channel delivery continue. Also covered: a host that ignores the notice and uses `call_operation`; catalog failure at start and recovery; a broker restart with an unchanged catalog (no notice, same runtime); removal and permission changes reported distinctly; malformed, oversized and newer-format catalogs and envelopes; unknown extension data; dispatch sharing fencing, lobby boundaries, retry IDs, schedules and checkpoints with the HTTP routes; and an uncertain send across a restart that is never replayed. All under a simulated host; the in-conversation `/mcp reconnect pocolobby` was separately verified with real Claude on macOS.
91
+ 15. One schema and one event version: a database of another schema, older or newer, is refused and left unchanged; a bridge that declares an older event version, or none, is refused with `426` before it binds. Automated tests only; no real Claude session took part.
92
+
93
+ Tests inject failures at commit/send/receipt boundaries, including process termination and connection loss. Package verification additionally exercises installed CLI and adapter subprocesses. These checks cannot certify model processing.
94
+
95
+ ## Milestone 2: continuity implemented; live pilot remains
96
+
97
+ Role/context reads, versioned checkpoints, bootstrap, associated receipt transactions, owner pause/retry/status, and bounded history are implemented. Checkpoints declare the receipt-event snapshot they cover; bootstrap reconciles later receipts with pagination. Regression tests cover stale checkpoint updates and acknowledgments not yet included in a checkpoint, including an older message acknowledged after a newer one. Replacement is distinct from network reconnect. Recovery exposes uncertain prior work for reconciliation.
98
+
99
+ Run two real independently authenticated sessions end to end. Measure human-visible interruptions, extra model turns, message latency, reconnect behavior, and history/context size. Test both an idle and a busy recipient. Show that an offline recipient receives the pending message after return and an explicit receipt is stored.
100
+
101
+ Success means observed routing, recoverable accepted messages, working bootstrap, and honest state reporting. It does not mean exactly-once tool effects or uninterrupted availability.
102
+
103
+ ## Milestone 3: deployment preparation implemented; operational validation remains
104
+
105
+ Docker/Fly configuration, one-writer volume layout, restart behavior, health checks, credential rotation, CLI backup and explicit recovery mode are implemented. Verify the deployed service, TLS, reconnect behavior and storage persistence before treating rollout as complete.
106
+
107
+ The local recovery tests cover retained messages/checkpoints, paused dispatch, invalidated runtime bindings and credentials, and a fresh broker incarnation. Repeat restoration in an isolated deployment and reconcile work against actual repositories/external systems. An off-machine backup job, schedule and the sole-Machine maintenance procedure still need implementation and validation. Include a token revoked and message acknowledged after the backup was taken: restored state must not silently restore access or repeat uncertain work.
108
+
109
+ Before production use, record backup frequency, tested recovery time, potential data-loss window after volume destruction, and retention/capacity policy. Pending messages must not silently expire to make room. Approved pruning retains enough identifiers to honor the stated retry window.
110
+
111
+ The CLI/adapter package, preview-flag documentation, protected configuration examples and CI are present. Verification commands are `npm run check` and `npm run test:package`; CI adds the container build. Live cloud checks and provider-account acceptance remain distinct from successful local/package tests.
112
+
113
+ ## Later
114
+
115
+ Add the authenticated ChatGPT tool gateway, then separately validate event-driven Work/Cloud integration. Add other provider adapters based on documented host capabilities. Reconsider multiple lobbies, active/passive failover, and a web UI only when a concrete need appears. Message scheduling met that bar: agents' session crons were lost on every restart. It delivers messages at a time; it does not schedule work.
116
+
117
+ ## Open decisions
118
+
119
+ - Adjustment of implemented notification/backoff and size defaults based on real use.
120
+ - Backup destination, retention, and accepted recovery objectives.
121
+
122
+ The implementation has automated/package tests, hosted transport and migration checks, and a real single-account Claude Channels + Remote Control round trip. The outstanding acceptance work is the separate-account test, existing-conversation migration, a real Claude restart that reclaims its runtime without an owner reset, and deployment-specific backup/restore operation.