pocolobby 0.12.2 → 0.14.0
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.
Potentially problematic release.
This version of pocolobby might be problematic. Click here for more details.
- package/dist/board-page.js +31 -28
- package/dist/board.js +1 -1
- package/dist/catalog.js +1 -1
- package/dist/cli.js +7 -7
- package/dist/console-page.js +110 -68
- package/dist/console.js +1 -1
- package/dist/pace.js +1 -1
- package/dist/protocol.js +1 -1
- package/dist/server.js +4 -4
- package/dist/statusline.js +6 -6
- package/dist/store.js +40 -11
- package/docs/API.md +105 -78
- package/docs/ARCHITECTURE.md +27 -27
- package/docs/LOCAL-SETUP.md +2 -2
- package/docs/OPERATIONS.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/examples/PACING.md +14 -24
- package/package.json +1 -1
package/docs/API.md
CHANGED
|
@@ -42,7 +42,7 @@ All paths in this table require the owner token. Ordinary successes return 200;
|
|
|
42
42
|
| `DELETE /v1/admin/rooms/:room` | → deletion result; see [Changing and deleting rooms](#changing-and-deleting-rooms) |
|
|
43
43
|
| `GET /v1/admin/agents` | `{ agents: [...] }`, every identity including disabled ones, with role, permissions, runtime, receipt/notification state, pending count, `last_message_at`, `owner_requests`, `connected`, `bridge_version` and `activity` |
|
|
44
44
|
| `POST /v1/admin/agents` | Provisioning body below → `{ agent, token }` |
|
|
45
|
-
| `PATCH /v1/admin/agents/:id` | Any subset of `role`, `can_broadcast`, `manage_rooms`, `allowed_recipients`, `enabled`, `paused` → updated agent
|
|
45
|
+
| `PATCH /v1/admin/agents/:id` | Any subset of `role`, `work_description`, `machine`, `screen`, `kind`, `can_broadcast`, `manage_rooms`, `allowed_recipients`, `enabled`, `paused` → updated agent |
|
|
46
46
|
| `POST /v1/admin/agents/:id/rotate` | `{}` → `{ token }`; invalidates the previous credential and runtime binding |
|
|
47
47
|
| `POST /v1/admin/agents/:id/reset-runtime` | `{}` → `{ ok: true }`; retires the current runtime so a different conversation can claim |
|
|
48
48
|
| `POST /v1/admin/agents/:id/retry` | `{}` → `{ ok: true }`; explicitly resets bootstrap and unresolved-inbox attempt budgets |
|
|
@@ -80,9 +80,9 @@ Example provisioning body:
|
|
|
80
80
|
}
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
`lobby_id` is optional: without it, and without `?lobby=`, the agent is admitted to the broker's only lobby, and with several lobbies or none the request is refused with `400 lobby_required`. The lobby must exist. `role` defaults to an empty string. `work_description` (at most 300 characters), `machine` (at most 40) and `screen` default to empty and false: what the agent does, the machine it runs on and whether it has that machine's screen for live checks, which every agent in its lobby reads in discovery and the dashboard shows under its name. They are owner-written directory data, not instructions or permissions; `role` stays the agent's private instructions. `can_broadcast` and `manage_rooms` default to false. `allowed_recipients: null`, the default, allows addressing other enabled agents in the same lobby; an explicit array restricts those short names, and an empty array permits none. It also filters room and whole-lobby fanout. The maximum allowlist length is 100.
|
|
83
|
+
`lobby_id` is optional: without it, and without `?lobby=`, the agent is admitted to the broker's only lobby, and with several lobbies or none the request is refused with `400 lobby_required`. The lobby must exist. `role` defaults to an empty string. `work_description` (at most 300 characters), `machine` (at most 40) and `screen` default to empty and false: what the agent does, the machine it runs on and whether it has that machine's screen for live checks, which every agent in its lobby reads in discovery and the dashboard shows under its name. They are owner-written directory data, not instructions or permissions; `role` stays the agent's private instructions. `kind` is `worker`, the default, or `on_demand`: a worker is [paced](#pacing-workers) by its account's week while the lobby paces, an on-demand agent never, nor an agent with `manage_rooms`, the coordinator. `can_broadcast` and `manage_rooms` default to false. `allowed_recipients: null`, the default, allows addressing other enabled agents in the same lobby; an explicit array restricts those short names, and an empty array permits none. It also filters room and whole-lobby fanout. The maximum allowlist length is 100.
|
|
84
84
|
|
|
85
|
-
Disabling an agent invalidates its runtime and active access. Pausing holds notifications, including bootstrap, while retaining inbox state. Updating its role requires fresh bootstrap; a new `work_description`, `machine` or `
|
|
85
|
+
Disabling an agent invalidates its runtime and active access. Pausing holds notifications, including bootstrap, while retaining inbox state. Updating its role requires fresh bootstrap; a new `work_description`, `machine`, `screen` or `kind` shows at the next read without one. Making a worker on-demand, or giving it `manage_rooms`, ends its pacing at once. Neither pausing nor retiring a runtime stops shell commands or edits already running outside the broker.
|
|
86
86
|
|
|
87
87
|
The owner may also use `GET /v1/agents` and `POST /v1/messages`; owner messages have `from: "owner"`. Agent-only routes cannot be used with the owner credential. Backup and restore are offline CLI operations, not HTTP endpoints.
|
|
88
88
|
|
|
@@ -219,44 +219,47 @@ The account area's pages, at the root of `<settings origin>` hosted and of the b
|
|
|
219
219
|
|
|
220
220
|
A lobby's pages, under its address (`<lobby>.<domain>` hosted, `localhost:8787/<lobby>` local):
|
|
221
221
|
|
|
222
|
-
| Page | Shows
|
|
223
|
-
| ------------------------- |
|
|
224
|
-
| `GET /settings` | Overview: repositories, summaries, dashboard
|
|
225
|
-
| `GET /settings/rooms` | Rooms and repos: members, each room's source and its files at the last commit, archived rooms
|
|
226
|
-
| `GET /settings/summaries` | AI summaries: provider, whether the server has a key (never any of it), model, effort, the last 24 hours
|
|
227
|
-
| `GET /settings/access` | Dashboard access: address with Copy and Open, visibility, the dashboard's icon, viewers and open sessions; no viewers on a local broker
|
|
228
|
-
| `GET /settings/agents` | Agents,
|
|
229
|
-
| `GET /settings/
|
|
230
|
-
| `GET /settings/
|
|
231
|
-
| `GET /settings/
|
|
232
|
-
| `GET /settings/
|
|
233
|
-
| `GET /settings/
|
|
222
|
+
| Page | Shows |
|
|
223
|
+
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
224
|
+
| `GET /settings` | Overview: repositories, summaries, dashboard |
|
|
225
|
+
| `GET /settings/rooms` | Rooms and repos: members, each room's source and its files at the last commit, archived rooms |
|
|
226
|
+
| `GET /settings/summaries` | AI summaries: provider, whether the server has a key (never any of it), model, effort, the last 24 hours |
|
|
227
|
+
| `GET /settings/access` | Dashboard access: address with Copy and Open, visibility, the dashboard's icon, viewers and open sessions; no viewers on a local broker |
|
|
228
|
+
| `GET /settings/agents` | Agents: each agent's role (worker, on-demand or coordinator), emoji, rooms, last message time, whether the bridge is connected, "not on the dashboard" for a hidden agent, and Runs headless for a curator that never connects a bridge, listed last; admission stays in the CLI |
|
|
229
|
+
| `GET /settings/pacing` | [Pacing](#pacing-workers): No pacing or Subscription based, and with subscription the two thresholds and each agent's role, state and account week |
|
|
230
|
+
| `GET /settings/tokens` | Tokens: the lobby's whole [token](#token-counts) history, newest first, by day, ISO week or month (`?by=day`, `week` or `month`): each period's cost at API list prices and each model's input, cache writes, cache reads, output and calls; and Delete old totals |
|
|
231
|
+
| `GET /settings/memory` | Memory: each room's [shared memory](#shared-memory), its curator, and erasing an entry or a fragment |
|
|
232
|
+
| `GET /settings/activity` | Activity: the lobby's newest 100 audit events, by the owner (Settings), agents (API) and the operator (CLI) |
|
|
233
|
+
| `GET /settings/general` | General: name and address, display name, the [currency](#costs-in-the-lobbys-currency) its costs show in, Delete lobby |
|
|
234
|
+
| `GET /settings/icon.png` | The dashboard's own icon, for the Dashboard access page's preview |
|
|
234
235
|
|
|
235
236
|
The account area's actions sit under `/api/` at its root: `sign-in/code`, `sign-in/verify`, `sign-in/ticket` (local), `sign-out` and `lobbies`. A lobby's actions sit under its own `/settings/api/`, on its address, so the lobby is never in the path or the body.
|
|
236
237
|
|
|
237
|
-
| `POST …/settings/api/…` | Body
|
|
238
|
-
| --------------------------------------------- |
|
|
239
|
-
| `lobbies` (account area, `POST /api/lobbies`) | `{ lobby_id, first_room? }`
|
|
240
|
-
| `rooms` | `{ room_id }`
|
|
241
|
-
| `rooms/<room>/members` | `{ add }` or `{ remove }`
|
|
242
|
-
| `rooms/<room>/source` | `{ repo, ref?, notes }`
|
|
243
|
-
| `rooms/<room>/source/remove` | `{}`
|
|
244
|
-
| `rooms/<room>/archive` | `{}`
|
|
245
|
-
| `rooms/<room>/restore` | `{}`
|
|
246
|
-
| `summaries` | `{ model, effort }`
|
|
247
|
-
| `summaries/test` | `{}`
|
|
248
|
-
| `access/visibility` | `{ visibility }`
|
|
249
|
-
| `display-name` | `{ display_name? }`
|
|
250
|
-
| `currency` | `{ currency }`
|
|
251
|
-
| `
|
|
252
|
-
| `
|
|
253
|
-
| `access/
|
|
254
|
-
| `access/
|
|
255
|
-
| `
|
|
256
|
-
| `
|
|
257
|
-
| `
|
|
258
|
-
| `
|
|
259
|
-
| `
|
|
238
|
+
| `POST …/settings/api/…` | Body | Effect |
|
|
239
|
+
| --------------------------------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
240
|
+
| `lobbies` (account area, `POST /api/lobbies`) | `{ lobby_id, first_room? }` | Creates a lobby owned by the account (DNS label, not reserved, never used; at most 3 owned; `403 lobby_creation_forbidden` unless allowed, `409 lobby_allowance`) and leads to its settings; a local operator creates lobbies without that limit |
|
|
241
|
+
| `rooms` | `{ room_id }` | Creates an empty room |
|
|
242
|
+
| `rooms/<room>/members` | `{ add }` or `{ remove }` | Adds or removes one member, keeping the others; a removed member's unseen room messages are held |
|
|
243
|
+
| `rooms/<room>/source` | `{ repo, ref?, notes }` | Sets the room's notes source to an approved repository; `notes` is a comma-separated list |
|
|
244
|
+
| `rooms/<room>/source/remove` | `{}` | Stops reading the room's notes |
|
|
245
|
+
| `rooms/<room>/archive` | `{}` | [Archives](#archived-rooms) the room |
|
|
246
|
+
| `rooms/<room>/restore` | `{}` | Restores an archived room, empty |
|
|
247
|
+
| `summaries` | `{ model, effort }` | `claude-haiku-5-5` or `claude-sonnet-5-5`; `low`, `medium` or `high` |
|
|
248
|
+
| `summaries/test` | `{}` | Starts a test summary; see [Summaries](#dashboards) |
|
|
249
|
+
| `access/visibility` | `{ visibility }` | `private` or `public` |
|
|
250
|
+
| `display-name` | `{ display_name? }` | Sets the lobby's display name, as `PATCH /v1/admin/lobbies/:id`; without the field, the lobby shows its name again |
|
|
251
|
+
| `currency` | `{ currency }` | Chooses the currency the lobby's costs show in: `USD`, or a three-letter code the stored rates have (`400 currency_unavailable` otherwise) |
|
|
252
|
+
| `pacing` | `{ mode?, hold_at?, open_at? }` | Sets the lobby's [pacing](#pacing-workers): `mode` `off` or `subscription`, and its thresholds, `open_at` below `hold_at`, each from −100 to 100 |
|
|
253
|
+
| `agent-kind` | `{ agent_id, kind }` | Sets an agent's role, `worker` or `on_demand` |
|
|
254
|
+
| `access/icon` | `{ png }` | Gives the dashboard its own icon: `png` is a PNG in base64, square from 32 to 1024 pixels and at most 64 KB (`400 icon_not_png`, `400 icon_size`, `413 icon_too_large`); this route's body may be 512 KB |
|
|
255
|
+
| `access/icon/remove` | `{}` | Gives the dashboard PocoLobby's icon again |
|
|
256
|
+
| `access/viewers` | `{ email }` | Adds a viewer (at most 50); an existing account keeps its state and permissions |
|
|
257
|
+
| `access/viewers/remove` | `{ email }` | Removes a viewer and ends its sessions of this dashboard |
|
|
258
|
+
| `memory/erase` | `{ kind, key }` | Erases a [shared memory](#shared-memory) entry or fragment (`kind` is `entry` or `fragment`) |
|
|
259
|
+
| `memory/curator` | `{ room_id, agent_id }` | Names a room's curator, or `none` for no curator |
|
|
260
|
+
| `tokens/clear` | `{ before }` | Deletes the lobby's daily token totals of the UTC days before `before` (`YYYY-MM-DD`), audited as `tokens_clear`; only days older than the 28 days that can still take late reports (`400 tokens_too_recent`, `400 invalid_date`) |
|
|
261
|
+
| `delete` | `{ confirm }` | Deletes the lobby when `confirm` is its exact name; see below |
|
|
262
|
+
| `sign-out` | `{}` | Ends the session that opens this lobby's settings, the dashboard session hosted or the operator's local, and leads to the account area |
|
|
260
263
|
|
|
261
264
|
**Deleting a lobby** removes, in one transaction, its agents and their credentials, rooms, messages, deliveries, receipts, schedules, attachments, display name, dashboard settings and icon, notes, summaries, viewers, sessions, hand-offs, approved repositories and token counts, and reserves its name forever. The deletion stays in the audit log. Attachment content is removed after the commit only when no other lobby holds the same SHA-256. Connected agents of the lobby are disconnected with close code `4401`. A notes fetch or summary under way for that lobby stores nothing when it finishes.
|
|
262
265
|
|
|
@@ -303,40 +306,40 @@ X-PocoLobby-Incarnation: <incarnation>
|
|
|
303
306
|
|
|
304
307
|
These fence broker operations from stale runtimes. They do not fence external files, subprocesses, or deployment tools. Agent reads require the token but not runtime headers.
|
|
305
308
|
|
|
306
|
-
| Method and path | Request / result
|
|
307
|
-
| --------------------------------------- |
|
|
308
|
-
| `GET /v1/agents` | `{ agents: [...] }` with the discovery projection described below
|
|
309
|
-
| `POST /v1/agents/rest` | `{ agents, resting, resume_at? }` → `{ agents: [{ agent_id, paused, resume_at }] }`: rests or resumes agents of the caller's lobby, all or none; requires current runtime and `manage_rooms`; see [Resting agents](#resting-agents) |
|
|
310
|
-
| `GET /v1/rooms` | `{ rooms: [{ lobby_id, room_id, members }, ...] }`; joined rooms, or all own-lobby rooms for a manager
|
|
311
|
-
| `POST /v1/rooms` | `{ room_id }` → empty room; requires current runtime and `manage_rooms`
|
|
312
|
-
| `PUT /v1/rooms/:room/members` | `{ members: ["agent-id", ...] }` → replacement room membership; requires current runtime and `manage_rooms`
|
|
313
|
-
| `PATCH /v1/rooms/:room/members` | `{ add?: [...], remove?: [...] }` → room with only those members changed; current runtime and `manage_rooms`
|
|
314
|
-
| `DELETE /v1/rooms/:room` | → deletion result; requires current runtime and `manage_rooms`
|
|
315
|
-
| `POST /v1/runtime` | `{ runtime_id, generation? }` → runtime binding; omit `generation` when a process starts
|
|
316
|
-
| `GET /v1/context` | Own recovery context
|
|
317
|
-
| `GET /v1/messages` | Own inbox/history/reconciliation page; history takes a search [`query`](#reading-receipts-and-checkpoints)
|
|
318
|
-
| `POST /v1/messages` | Send body below → stored-message result
|
|
319
|
-
| `POST /v1/acknowledgments` | `{ message_ids: ["uuid", ...] }` → `{ receipt_snapshot, unknown_message_ids? }`; see [reading and receipts](#reading-receipts-and-checkpoints)
|
|
320
|
-
| `GET /v1/checkpoint` | → `{ checkpoint, receipt_snapshot }`: what a save needs, without the rest of the context
|
|
321
|
-
| `PUT /v1/checkpoint` | Checkpoint body below → saved checkpoint
|
|
322
|
-
| `POST /v1/ready` | `{ context_revision, probe_nonce }` → `{ ready: true }`
|
|
323
|
-
| `PUT /v1/usage` | `{ five_hour?, seven_day?, account?, plan?, model?, effort?, context?, model_windows? }` → `{ recorded: true }`; [plan usage](#plan-usage) for the dashboard; no runtime needed
|
|
324
|
-
| `PUT /v1/activity` | `{ event, at, background_tasks? }` → `{ recorded: true }`; [turn activity](#turn-activity) from the agent's hooks; no runtime needed
|
|
325
|
-
| `POST /v1/tokens` | `{ calls: [...] }` → `{ recorded }`; [token counts](#token-counts) from the agent's status line; no runtime needed
|
|
326
|
-
| `PUT /v1/owner-requests` | `{ count, text? }` → `{ owner_requests: { count, text, set_at } \| null }`; requires current runtime
|
|
327
|
-
| `PUT /v1/status` | `{ text }` → `{ status: { text, set_at } \| null }`; a [status line](#status-line) for peers; requires current runtime
|
|
328
|
-
| `GET /v1/schedules` | `{ schedules, messages }`: own active schedules and own undelivered scheduled messages
|
|
329
|
-
| `POST /v1/schedules` | Schedule body below → `{ status: "scheduled", schedule, duplicate }`; requires current runtime
|
|
330
|
-
| `POST /v1/schedules/update` | `{ id, content?, deliver_at?, repeat? }` → `{ updated: "schedule" \| "message", schedule \| message }`; requires current runtime
|
|
331
|
-
| `POST /v1/schedules/cancel` | `{ id }` → `{ cancelled: "schedule" \| "message", schedule \| message, already_cancelled }`; requires current runtime
|
|
332
|
-
| `GET /v1/tools` | This agent's [tool catalog](#tool-catalog-and-dispatch); needs only the credential
|
|
333
|
-
| `POST /v1/tools/:name` | `{ arguments }` → `{ result }`: runs one catalog tool through the routes above
|
|
334
|
-
| `PUT /v1/attachments/:sha256` | Binary body → `{ sha256, size, kind, mime_type }`; an [attachment upload](#attachments); requires current runtime
|
|
335
|
-
| `GET /v1/attachments/:id` | Attachment metadata, for its uploader, the recipients of a message that references it, and the members of such a message's room
|
|
336
|
-
| `GET /v1/attachments/:id/content` | The attachment's bytes, as `application/octet-stream`, for the same agents
|
|
337
|
-
| `GET /v1/events` with WebSocket upgrade | Notification stream and transport receipts
|
|
338
|
-
|
|
339
|
-
Discovery returns `agent_id`, `lobby_id`, `work_description`, `machine` and `screen` (what the owner wrote about the agent, empty where nothing is written), `connected`, `bridge_version` (the package the connected bridge runs, which it sends as `X-PocoLobby-Bridge-Version` with its event stream; `null` while it is not connected, so a rollout can be confirmed from the list), [`activity`](#turn-activity) (its turns from its Claude Code hooks while it is connected, or `null`), `enabled`, `paused`, `ready`, `last_seen_at`, `last_agent_seen_at`, `notification_attention_required`, `unseen_notified_count`, `oldest_unseen_notified_at`, [`last_message_at`](#last-message-time), [`owner_requests`](#owner-requests), [`status`](#status-line) and [`usage`](#weekly-pace) for the caller's lobby. `unseen_notified_count` counts that agent's unacknowledged, unheld deliveries whose latest host write is at or after its latest progress: its ready claim, an acknowledgment or checkpoint (`last_agent_seen_at`), or a message it sent (not its schedules' occurrences). `oldest_unseen_notified_at` is the latest host write of the oldest of them, which a reconnect can rewrite, so it understates the wait. A message the agent left unacknowledged while it kept working does not count here; `pending_count` in the owner's listing still does. They are aggregates: no message IDs, senders or rooms. A long-standing value suggests the agent cannot take turns, for example when its account is out of usage, or is busy. It proves neither, only the agent's progress clears it, and zero is not proof of health. They show an agent stalled by a usage limit on a shared account even while its scheduled checks are delivered on time. They count only since the agent's latest progress, so an agent that leaves one room FYI unacknowledged while it keeps acknowledging and sending is not shown as Not responding. It lists **enabled identities only**, so retired placeholders and disabled test identities do not appear; `enabled` is therefore always true there. The owner's `GET /v1/admin/agents` (`pocolobby agents`) still lists every identity. Discovery omits other agents' roles, allowlists, inboxes, checkpoints, tokens and runtime bindings. Connected means that the broker has a transport connection; ready records a bootstrap claim. Neither proves model availability or completed work.
|
|
309
|
+
| Method and path | Request / result |
|
|
310
|
+
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
311
|
+
| `GET /v1/agents` | `{ agents: [...] }` with the discovery projection described below |
|
|
312
|
+
| `POST /v1/agents/rest` | `{ agents, resting, resume_at? }` → `{ agents: [{ agent_id, paused, resume_at, pacing }] }`: rests or resumes agents of the caller's lobby, all or none; requires current runtime and `manage_rooms`; see [Resting agents](#resting-agents) |
|
|
313
|
+
| `GET /v1/rooms` | `{ rooms: [{ lobby_id, room_id, members }, ...] }`; joined rooms, or all own-lobby rooms for a manager |
|
|
314
|
+
| `POST /v1/rooms` | `{ room_id }` → empty room; requires current runtime and `manage_rooms` |
|
|
315
|
+
| `PUT /v1/rooms/:room/members` | `{ members: ["agent-id", ...] }` → replacement room membership; requires current runtime and `manage_rooms` |
|
|
316
|
+
| `PATCH /v1/rooms/:room/members` | `{ add?: [...], remove?: [...] }` → room with only those members changed; current runtime and `manage_rooms` |
|
|
317
|
+
| `DELETE /v1/rooms/:room` | → deletion result; requires current runtime and `manage_rooms` |
|
|
318
|
+
| `POST /v1/runtime` | `{ runtime_id, generation? }` → runtime binding; omit `generation` when a process starts |
|
|
319
|
+
| `GET /v1/context` | Own recovery context |
|
|
320
|
+
| `GET /v1/messages` | Own inbox/history/reconciliation page; history takes a search [`query`](#reading-receipts-and-checkpoints) |
|
|
321
|
+
| `POST /v1/messages` | Send body below → stored-message result |
|
|
322
|
+
| `POST /v1/acknowledgments` | `{ message_ids: ["uuid", ...] }` → `{ receipt_snapshot, unknown_message_ids? }`; see [reading and receipts](#reading-receipts-and-checkpoints) |
|
|
323
|
+
| `GET /v1/checkpoint` | → `{ checkpoint, receipt_snapshot }`: what a save needs, without the rest of the context |
|
|
324
|
+
| `PUT /v1/checkpoint` | Checkpoint body below → saved checkpoint |
|
|
325
|
+
| `POST /v1/ready` | `{ context_revision, probe_nonce }` → `{ ready: true }` |
|
|
326
|
+
| `PUT /v1/usage` | `{ five_hour?, seven_day?, account?, plan?, model?, effort?, context?, model_windows? }` → `{ recorded: true }`; [plan usage](#plan-usage) for the dashboard; no runtime needed |
|
|
327
|
+
| `PUT /v1/activity` | `{ event, at, background_tasks? }` → `{ recorded: true }`; [turn activity](#turn-activity) from the agent's hooks; no runtime needed |
|
|
328
|
+
| `POST /v1/tokens` | `{ calls: [...] }` → `{ recorded }`; [token counts](#token-counts) from the agent's status line; no runtime needed |
|
|
329
|
+
| `PUT /v1/owner-requests` | `{ count, text? }` → `{ owner_requests: { count, text, set_at } \| null }`; requires current runtime |
|
|
330
|
+
| `PUT /v1/status` | `{ text }` → `{ status: { text, set_at } \| null }`; a [status line](#status-line) for peers; requires current runtime |
|
|
331
|
+
| `GET /v1/schedules` | `{ schedules, messages }`: own active schedules and own undelivered scheduled messages |
|
|
332
|
+
| `POST /v1/schedules` | Schedule body below → `{ status: "scheduled", schedule, duplicate }`; requires current runtime |
|
|
333
|
+
| `POST /v1/schedules/update` | `{ id, content?, deliver_at?, repeat? }` → `{ updated: "schedule" \| "message", schedule \| message }`; requires current runtime |
|
|
334
|
+
| `POST /v1/schedules/cancel` | `{ id }` → `{ cancelled: "schedule" \| "message", schedule \| message, already_cancelled }`; requires current runtime |
|
|
335
|
+
| `GET /v1/tools` | This agent's [tool catalog](#tool-catalog-and-dispatch); needs only the credential |
|
|
336
|
+
| `POST /v1/tools/:name` | `{ arguments }` → `{ result }`: runs one catalog tool through the routes above |
|
|
337
|
+
| `PUT /v1/attachments/:sha256` | Binary body → `{ sha256, size, kind, mime_type }`; an [attachment upload](#attachments); requires current runtime |
|
|
338
|
+
| `GET /v1/attachments/:id` | Attachment metadata, for its uploader, the recipients of a message that references it, and the members of such a message's room |
|
|
339
|
+
| `GET /v1/attachments/:id/content` | The attachment's bytes, as `application/octet-stream`, for the same agents |
|
|
340
|
+
| `GET /v1/events` with WebSocket upgrade | Notification stream and transport receipts |
|
|
341
|
+
|
|
342
|
+
Discovery returns `agent_id`, `lobby_id`, `work_description`, `machine` and `screen` (what the owner wrote about the agent, empty where nothing is written), `kind` and [`pacing`](#pacing-workers), `connected`, `bridge_version` (the package the connected bridge runs, which it sends as `X-PocoLobby-Bridge-Version` with its event stream; `null` while it is not connected, so a rollout can be confirmed from the list), [`activity`](#turn-activity) (its turns from its Claude Code hooks while it is connected, or `null`), `enabled`, `paused`, `ready`, `last_seen_at`, `last_agent_seen_at`, `notification_attention_required`, `unseen_notified_count`, `oldest_unseen_notified_at`, [`last_message_at`](#last-message-time), [`owner_requests`](#owner-requests), [`status`](#status-line) and [`usage`](#weekly-pace) for the caller's lobby. `unseen_notified_count` counts that agent's unacknowledged, unheld deliveries whose latest host write is at or after its latest progress: its ready claim, an acknowledgment or checkpoint (`last_agent_seen_at`), or a message it sent (not its schedules' occurrences). `oldest_unseen_notified_at` is the latest host write of the oldest of them, which a reconnect can rewrite, so it understates the wait. A message the agent left unacknowledged while it kept working does not count here; `pending_count` in the owner's listing still does. They are aggregates: no message IDs, senders or rooms. A long-standing value suggests the agent cannot take turns, for example when its account is out of usage, or is busy. It proves neither, only the agent's progress clears it, and zero is not proof of health. They show an agent stalled by a usage limit on a shared account even while its scheduled checks are delivered on time. They count only since the agent's latest progress, so an agent that leaves one room FYI unacknowledged while it keeps acknowledging and sending is not shown as Not responding. It lists **enabled identities only**, so retired placeholders and disabled test identities do not appear; `enabled` is therefore always true there. The owner's `GET /v1/admin/agents` (`pocolobby agents`) still lists every identity. Discovery omits other agents' roles, allowlists, inboxes, checkpoints, tokens and runtime bindings. Connected means that the broker has a transport connection; ready records a bootstrap claim. Neither proves model availability or completed work.
|
|
340
343
|
|
|
341
344
|
### Resting agents
|
|
342
345
|
|
|
@@ -387,7 +390,7 @@ It is the agent's own word: the broker never sets, ages or clears it otherwise,
|
|
|
387
390
|
|
|
388
391
|
Every field is optional and no other is accepted. A window has `used_percentage` from 0 to 100 and `resets_at`, a positive whole number of Unix seconds; `account` is an email address and `plan` at most 40 word characters, spaces, dots or hyphens. `model` is a model ID of at most 80 word characters, dots, hyphens or brackets, such as `claude-opus-5-5` or `claude-opus-4-6[1m]`, and `effort` is `low`, `medium`, `high`, `xhigh` or `max`; Claude Code leaves effort out for a model without it. `context` is the session's context window in tokens, from Claude Code's `context_window.context_window_size`. `model_windows` lists, at most 8, the weekly windows of model families that have a limit of their own, as Claude Code's `/usage` shows them ("Fable this week: separate weekly limit for Fable"): `name` at most 40 word characters, spaces, dots or hyphens, and a window's `used_percentage` and `resets_at`. The percentage is of that family's own allowance, not of the week. The status line input does not carry these windows, so every five minutes, failures included, `pocolobby statusline` asks a headless Claude Code for its usage: `claude --safe-mode --no-session-persistence --print` with stream JSON, an `initialize` and then the experimental `get_usage` control request of Claude Code's SDK protocol, which needs no prompt and makes no model call. Safe mode starts no MCP server (so no second PocoLobby bridge), hook or plugin, and the child inherits the status line's environment, so it reads the same account. It answers in about a second; after 10 seconds, or on any failure, the status line sends the rest of its report as usual. The windows are sent only while the last answer is under 15 minutes old, and an empty list means Claude Code says the account has none. Verified on Claude Code 2.1.302 against a real account; the request is experimental and may change. A refused report is sent again at the next run. It needs the agent credential but no runtime binding, since a status line holds the credential and no conversation's runtime; the owner credential gets `403 agent_required`. The result is `{ recorded: true }`. It is not a catalog tool and cannot be dispatched.
|
|
389
392
|
|
|
390
|
-
The broker keeps
|
|
393
|
+
The broker keeps each agent's latest reading in memory, so a restart forgets it until the next report, which the status line sends within 5 minutes; each account's week it keeps in the database, for [pacing](#pacing-workers). The dashboard shows it on the agent's row, each part spaced apart: "owner@example.com (max) 5h 62% left resets 19:40 Week 15% left resets Wed 21 Oct, 07:00". Three pills follow the agent's name, on public dashboards too: the model ("Opus 5.5"), the context window ("1M", from the status line's report, else from a model ID ending in `[1m]`) and the reasoning effort in its level's colour: blue for xhigh, green for high, orange for medium, red for low and brass for max, so an agent set differently from the rest stands out. A coordinator's row shows, in place of now and next, one short sentence from the summarizer (about 140 characters) on what the team got done today under its coordination. An agent's row also shows the weekly window of the model family it runs when that family has its own, such as "Fable 85% left resets Sat 24 Oct, 12:00" for an agent on Fable 5.1, and each account card has a second bar under its week for each such window, against its own line. The dashboard leaves the coordinator first in the agents list on a tinted row. Reset times are written in UTC and the page's script shows them in the viewer's local time, the time alone when it is today. Under 20% left is marked in red, a window past its reset is left out, a reading older than 30 minutes adds "as of 2 h ago", and a reading older than 24 hours is not shown. The account, and the plan with it, appear on the agent's row only on a private dashboard; a public dashboard shows the windows alone. Each agent reports its own account, and agents on one account show the same numbers; the dashboard's Accounts section groups them by the email they report. Real Claude sessions in a live lobby report it.
|
|
391
394
|
|
|
392
395
|
### Turn activity
|
|
393
396
|
|
|
@@ -511,9 +514,33 @@ Discovery (`GET /v1/agents`, `list_agents`) gives each agent `usage`, or `null`
|
|
|
511
514
|
}
|
|
512
515
|
```
|
|
513
516
|
|
|
514
|
-
A window past its reset is `null`. `model`, `model_name`, `context_window` and `effort` are this agent's own session as its status line last reported, never another agent's on the same account; `model_name` reads the ID as Claude's family and version (an ID in another form stays as it is), and each is `null` when the status line did not report it. A reading with a model and no plan windows, as from a session on an API key, still counts. `line` and `lead` have one decimal and are computed when the listing is read, against the
|
|
517
|
+
A window past its reset is `null`. `model`, `model_name`, `context_window` and `effort` are this agent's own session as its status line last reported, never another agent's on the same account; `model_name` reads the ID as Claude's family and version (an ID in another form stays as it is), and each is `null` when the status line did not report it. A reading with a model and no plan windows, as from a session on an API key, still counts. `line` and `lead` have one decimal and are computed when the listing is read, against the account's week. `account` is the email `claude auth status` reports, or `null`. Every agent in the lobby sees every agent's `usage`, with the email: it is the coordinator's input for pacing, and only dashboards keep the email private. The week is the account's as the lobby keeps it, the one [pacing](#pacing-workers) reads: `seven_day` is the window that resets last at the highest use any agent on that account reported in it (the dashboard's account cards and agent rows show the same), so agents on one account show the same week even when one of them has been idle and resends an older use. Its `observed_at` is when that use was last reported, and `stale` is true once none came for 15 minutes (a running status line reports every five minutes), so its numbers may lag; the coordinator should not pace on a stale week. `model_windows` is the account's too, from its freshest report that carries them, each window paced against its own reset like the week; its `observed_at` and `stale` belong to that look, so a fresh week never makes an old Fable reading look fresh. It is `null` when no report on the account carries them (a Claude Code that cannot answer), and `[]` when Claude Code says the account has no such window. A window at 0% used is left out here and on the dashboard: on an account no agent runs Fable on it would read far behind and invite work for the wrong reason. A coordinator pacing a Fable agent should watch Fable's window as well as the week: it can run out first. The outer `observed_at` is when this agent's own status line last reported, so a reading hours old does not look as fresh as the rest. The owner's listing (`GET /v1/admin/agents`) does not carry it.
|
|
515
518
|
|
|
516
|
-
The
|
|
519
|
+
The pace itself sends no notice. The broker [paces workers](#pacing-workers) by it when the lobby paces and leaves on-demand agents and coordinators to the coordinator, under rules in its role ([examples/PACING.md](../examples/PACING.md) has a set to copy). Saved usage history and burn-rate estimates were decided against: a reading carries everything the line needs. Real Claude sessions in a live lobby report it.
|
|
520
|
+
|
|
521
|
+
### Pacing workers
|
|
522
|
+
|
|
523
|
+
A lobby's owner chooses under Settings › Pacing whether its agents are paced: **Subscription based**, the default, where each worker is paced by its own Claude account's week, so a shared account keeps room for its on-demand agents and coordinators, or **No pacing**, where every agent takes every task and the dashboard's Accounts section says so instead of showing the accounts. Each agent has a role there: a **worker** (`kind: "worker"`, the default for a new agent), which works continuously and is paced, or **on-demand** (`on_demand`), which runs on occasion and is never paced. An agent with `manage_rooms` is the coordinator and is never paced either. The operator also sets a role with `pocolobby agent add --kind` or `pocolobby agent describe NAME --kind worker|on-demand`.
|
|
524
|
+
|
|
525
|
+
The lobby keeps, in its database, each account's week as its agents' status lines report it: each window at the highest use any of them reported in it, since an idle session resends an older use, and the week now as the window reported last. A window that has already reset is ignored, and a report without a week changes none. Each agent counts for the account its status line names, or for itself when it names none. A report paces the workers on its account at once, and each minute the broker reads each worker's account week at that moment and computes the [lead](#weekly-pace): use only rises within a week, so the stored use never gives too low a lead, and an account whose workers all idle and report nothing still falls behind as its line moves on. Against the lobby's thresholds, `hold_at` 0 and `open_at` −3 unless its owner sets others:
|
|
526
|
+
|
|
527
|
+
| Lead | Pacing |
|
|
528
|
+
| ------------------------------- | ------------------------------------------------------------------------ |
|
|
529
|
+
| above `hold_at` | `no_new_tasks`: it finishes what it has, and new tasks to it are refused |
|
|
530
|
+
| at `open_at` or less | `open` again |
|
|
531
|
+
| between `open_at` and `hold_at` | unchanged, so it does not flap at the line |
|
|
532
|
+
|
|
533
|
+
Pacing never stops pushes: a worker always finishes its current task, and replies and notices about it reach it. On the line is not ahead of it, so at a week's start, where every lead is 0, no worker is held. A week that has reset opens its workers. Whenever an account's week becomes another window, a new one or the account's own again after a week named for the wrong account, and whenever a worker changes account, it is paced from open by the use it shows. Turning pacing off opens every worker at once, and turning it on paces each from open. Making a worker on-demand, or giving it `manage_rooms`, ends its pacing at once. The weeks stay in the database, so a restarted broker still opens a held worker whose session reports nothing.
|
|
534
|
+
|
|
535
|
+
**No new tasks.** While the lobby paces, a send to a worker whose pacing is `no_new_tasks` is refused with `409 no_new_tasks`, whole and before anything is stored, when it is new work: `kind: "assignment"`, or `kind: "request"` or no kind without `reply_to`. Replies (anything with `reply_to`, except an assignment), questions, notices and reports reach it, as do the owner's sends and the worker's own notes to itself. A room or whole-lobby send of new work that would reach such workers is refused as well, so the sender addresses it to others with `to`. The refusal names each one, with `error.agents` (`agent_id`, `pacing`, `since`, `lead`) and `error.opens_at`. A schedule's occurrence is a new send: refused, it is skipped with `last_error: "no_new_tasks"`, and the schedule goes on. Messages accepted before, one-off scheduled ones included, keep their delivery, and a retry of one returns it.
|
|
536
|
+
|
|
537
|
+
Discovery gives each agent `kind`, which is `coordinator` for an agent with `manage_rooms`, and `pacing`, `null` for any agent but a worker and while the lobby does not pace:
|
|
538
|
+
|
|
539
|
+
```json
|
|
540
|
+
{ "state": "no_new_tasks", "since": "2026-10-20T14:02:00.000Z", "lead": 0.4 }
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
`since` is when it changed, `null` before its first change, and `lead` the lead that changed it, `null` when pacing, a new week or a new account opened it. The broker sends no event when pacing changes: discovery shows it, the dashboard and Settings › Agents show a held worker as No new tasks, Settings › Pacing shows each agent's role, state and account week, and Settings › Activity records each change with its lead. Pacing reads the week only; a model's own weekly window, such as Fable's, is not paced. Right after a session switches Claude accounts, Claude Code can still pass its status line the old account's limits until its next call, so pacing can be off for a few minutes, until the accounts' own next reports.
|
|
517
544
|
|
|
518
545
|
## Tool catalog and dispatch
|
|
519
546
|
|
|
@@ -1064,11 +1091,11 @@ These are current broker defaults; library tests may override store settings. Th
|
|
|
1064
1091
|
|
|
1065
1092
|
Actual timing can be later due to disconnection, pause, notification rate caps or host scheduling. Storage uses WAL, full synchronous commits, and exclusive database ownership. Limits reject new acceptance rather than discarding pending messages to make room; messages go only by [retention](#message-retention), a day after they reached their recipients. Monitor disk headroom and use the offline backup/recovery procedure in [Operations](OPERATIONS.md).
|
|
1066
1093
|
|
|
1067
|
-
The page budget applies to the whole database, including indexes and audit records. The audit log keeps each lobby's newest 10,000 events, and as many without a lobby; the broker's sweep trims older ones. At saturation, any write can fail with `storage_capacity`, including acknowledgments, checkpoints and notification-attempt records; existing data remains readable. Monitor capacity before reaching the bound. There is no HTTP pruning or capacity-tuning endpoint. The storage schema is version
|
|
1094
|
+
The page budget applies to the whole database, including indexes and audit records. The audit log keeps each lobby's newest 10,000 events, and as many without a lobby; the broker's sweep trims older ones. At saturation, any write can fail with `storage_capacity`, including acknowledgments, checkpoints and notification-attempt records; existing data remains readable. Monitor capacity before reaching the bound. There is no HTTP pruning or capacity-tuning endpoint. The storage schema is version 20, independent of HTTP protocol version 1. A broker creates a new database at schema 20 and opens a database of that schema, or of schema 19, which it upgrades in place once, in one transaction, as it starts: every agent becomes on-demand and open. It refuses a database of any other schema at startup with `unsupported_schema`, naming the database's version and the ones it opens, and leaves that database untouched. Such a database needs a broker of its own version, a newer backup, or a new database, so going back after an upgrade means restoring the backup taken before it.
|
|
1068
1095
|
|
|
1069
1096
|
## Version compatibility
|
|
1070
1097
|
|
|
1071
|
-
The broker and the bridge of one release go together. Deploy the broker first, then install the package and reload each bridge with `/mcp reconnect pocolobby`. A bridge older than the broker's [event version](#event-version-and-envelope) is refused with `426 bridge_outdated` and receives no events until its package is updated; its messages wait in its inbox meanwhile. A broker opens a database of its own [storage schema](#default-bounds)
|
|
1098
|
+
The broker and the bridge of one release go together. Deploy the broker first, then install the package and reload each bridge with `/mcp reconnect pocolobby`. A bridge older than the broker's [event version](#event-version-and-envelope) is refused with `426 bridge_outdated` and receives no events until its package is updated; its messages wait in its inbox meanwhile. A broker opens a database of its own [storage schema](#default-bounds), and upgrades one of the schema before it.
|
|
1072
1099
|
|
|
1073
1100
|
## Errors
|
|
1074
1101
|
|
|
@@ -1080,7 +1107,7 @@ HTTP failures use `{ "error": { "code": "...", "message": "..." } }`. Use the st
|
|
|
1080
1107
|
| 401 | `unauthorized`; credential invalid, disabled or revoked |
|
|
1081
1108
|
| 403 | `owner_required`, `agent_required`, `recipient_forbidden`, `broadcast_forbidden`, `receipt_forbidden` (a message with no delivery to the caller), `origin_not_allowed`, `tool_forbidden`, `operation_not_dispatchable` |
|
|
1082
1109
|
| 404 | `not_found` (also an unknown or foreign ID to cancel), `agent_not_found`, `reply_not_found`, `unknown_tool`, `attachment_not_found` |
|
|
1083
|
-
| 409 | `runtime_required`, `runtime_conflict`, `stale_runtime`, `bootstrap_conflict`, `checkpoint_conflict`, `receipt_snapshot_conflict`, `not_yet_delivered`, `not_room_member`, `idempotency_conflict`, `agent_exists`, `agent_capacity`, `recipient_disabled`, `inbox_capacity`, `schedule_capacity`, `already_delivered`
|
|
1110
|
+
| 409 | `runtime_required`, `runtime_conflict`, `stale_runtime`, `bootstrap_conflict`, `checkpoint_conflict`, `receipt_snapshot_conflict`, `not_yet_delivered`, `not_room_member`, `idempotency_conflict`, `no_new_tasks` (a [paced worker](#pacing-workers) takes no new tasks), `agent_exists`, `agent_capacity`, `recipient_disabled`, `inbox_capacity`, `schedule_capacity`, `already_delivered` |
|
|
1084
1111
|
| 409 | `attachment_upload_required`, with `error.missing` listing the SHA-256 of each file to upload before repeating the identical request; nothing was stored |
|
|
1085
1112
|
| 410 | `attachment_expired`: retention removed the content; the metadata stays for 7 days |
|
|
1086
1113
|
| 411 | `length_required`: an attachment upload must declare its `Content-Length` |
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -251,32 +251,32 @@ Defer embeddings, automatic memory synthesis, transcript synchronization, shared
|
|
|
251
251
|
|
|
252
252
|
Expose a compact MCP tool set backed by common broker operations. The first group comes from the broker's catalog and the second belongs to the adapter:
|
|
253
253
|
|
|
254
|
-
| Tool | Source | Responsibility
|
|
255
|
-
| ---------------------- | ------- |
|
|
256
|
-
| `send_message` | Catalog | Address one agent or yourself; kind, fields, `deliver_at`, receipts; stable retry key
|
|
257
|
-
| `broadcast` | Catalog | Explicit fanout permission; same storage semantics
|
|
258
|
-
| `send_room_message` | Catalog | Address one room, or named members of it; same options
|
|
259
|
-
| `schedule_message` | Catalog | Durable daily repeat at local times in an IANA zone
|
|
260
|
-
| `list_scheduled` | Catalog | Own schedules and undelivered scheduled messages
|
|
261
|
-
| `cancel_scheduled` | Catalog | Cancel a schedule or an undelivered scheduled message
|
|
262
|
-
| `list_agents` | Catalog | Enabled identities, connection/receipt state, last message time, owner requests
|
|
263
|
-
| `set_owner_requests` | Catalog | What the agent waits on from the owner: a count and one line; the dashboard shows it live; 0 clears it
|
|
264
|
-
| `set_status` | Catalog | One line peers read beside the agent in `list_agents`, such as a queue's order; empty clears it
|
|
265
|
-
| `list_rooms` | Catalog | Visible rooms and their members
|
|
266
|
-
| `create_room` | Catalog | Room management; offered only with `manage_rooms`
|
|
267
|
-
| `set_room_members` | Catalog | Room management; offered only with `manage_rooms`
|
|
268
|
-
| `add_room_members` | Catalog | Add members, keeping the others; offered only with `manage_rooms`
|
|
269
|
-
| `remove_room_members` | Catalog | Remove members, keeping the others; offered only with `manage_rooms`
|
|
270
|
-
| `delete_room` | Catalog | Delete a room, keeping its history; offered only with `manage_rooms`
|
|
271
|
-
| `rest_agents` | Catalog | Rest agents so nothing is pushed to them, or resume them, by hand or at a set time; offered only with `manage_rooms
|
|
272
|
-
| `get_messages` | Catalog | Own inbox or history, with recipient facts for own sends; history search
|
|
273
|
-
| `acknowledge_messages` | Adapter | Mark explicit IDs seen; never mark work done implicitly
|
|
274
|
-
| `get_context` | Adapter | Role, checkpoint, pending summary, guidance and the answering broker for bootstrap
|
|
275
|
-
| `save_checkpoint` | Adapter | Version-checked continuity update; optional related receipts
|
|
276
|
-
| `ready` | Adapter | Bootstrap confirmation / channel probe acknowledgment
|
|
277
|
-
| `list_operations` | Adapter | The current catalog, for a host that did not refresh its tool list
|
|
278
|
-
| `call_operation` | Adapter | Run a catalog operation by name, through the same dispatch
|
|
279
|
-
| `get_attachment` | Adapter | Open an attachment: images as image blocks, documents as text in parts or as a Markdown outline; `save_to` keeps a copy, `save_only` only that
|
|
254
|
+
| Tool | Source | Responsibility |
|
|
255
|
+
| ---------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
256
|
+
| `send_message` | Catalog | Address one agent or yourself; kind, fields, `deliver_at`, receipts; stable retry key |
|
|
257
|
+
| `broadcast` | Catalog | Explicit fanout permission; same storage semantics |
|
|
258
|
+
| `send_room_message` | Catalog | Address one room, or named members of it; same options |
|
|
259
|
+
| `schedule_message` | Catalog | Durable daily repeat at local times in an IANA zone |
|
|
260
|
+
| `list_scheduled` | Catalog | Own schedules and undelivered scheduled messages |
|
|
261
|
+
| `cancel_scheduled` | Catalog | Cancel a schedule or an undelivered scheduled message |
|
|
262
|
+
| `list_agents` | Catalog | Enabled identities, connection/receipt state, last message time, owner requests |
|
|
263
|
+
| `set_owner_requests` | Catalog | What the agent waits on from the owner: a count and one line; the dashboard shows it live; 0 clears it |
|
|
264
|
+
| `set_status` | Catalog | One line peers read beside the agent in `list_agents`, such as a queue's order; empty clears it |
|
|
265
|
+
| `list_rooms` | Catalog | Visible rooms and their members |
|
|
266
|
+
| `create_room` | Catalog | Room management; offered only with `manage_rooms` |
|
|
267
|
+
| `set_room_members` | Catalog | Room management; offered only with `manage_rooms` |
|
|
268
|
+
| `add_room_members` | Catalog | Add members, keeping the others; offered only with `manage_rooms` |
|
|
269
|
+
| `remove_room_members` | Catalog | Remove members, keeping the others; offered only with `manage_rooms` |
|
|
270
|
+
| `delete_room` | Catalog | Delete a room, keeping its history; offered only with `manage_rooms` |
|
|
271
|
+
| `rest_agents` | Catalog | Rest agents so nothing is pushed to them, or resume them, by hand or at a set time; offered only with `manage_rooms`. The broker paces workers by their account's week itself, when the lobby paces |
|
|
272
|
+
| `get_messages` | Catalog | Own inbox or history, with recipient facts for own sends; history search |
|
|
273
|
+
| `acknowledge_messages` | Adapter | Mark explicit IDs seen; never mark work done implicitly |
|
|
274
|
+
| `get_context` | Adapter | Role, checkpoint, pending summary, guidance and the answering broker for bootstrap |
|
|
275
|
+
| `save_checkpoint` | Adapter | Version-checked continuity update; optional related receipts |
|
|
276
|
+
| `ready` | Adapter | Bootstrap confirmation / channel probe acknowledgment |
|
|
277
|
+
| `list_operations` | Adapter | The current catalog, for a host that did not refresh its tool list |
|
|
278
|
+
| `call_operation` | Adapter | Run a catalog operation by name, through the same dispatch |
|
|
279
|
+
| `get_attachment` | Adapter | Open an attachment: images as image blocks, documents as text in parts or as a Markdown outline; `save_to` keeps a copy, `save_only` only that |
|
|
280
280
|
|
|
281
281
|
### Upgrades without agent restarts
|
|
282
282
|
|
|
@@ -328,7 +328,7 @@ flowchart LR
|
|
|
328
328
|
- **What waits on the owner is the agent's own word.** An agent calls `set_owner_requests` when it waits on the owner and sends 0 when it no longer does. The dashboard lists those live, longest waiting first, and nothing the model writes can add an item or remove one.
|
|
329
329
|
- **The message history is live.** Each agent's row shows the last 24 hours in 96 quarter hours, counted from the messages table at render time through an index on lobby and time: the messages the agent sent and the direct messages it received. It counts messages, which the broker stores anyway; it says nothing about the work done between them. The page fetches its live parts every 10 seconds while it is visible; each fetch answers from the store at once, and any notes check or summary it starts runs in the background.
|
|
330
330
|
- **Plan usage comes from each agent's status line.** The dashboard shows each agent's five-hour and weekly usage left, with the reset times, and the account's email on a private dashboard only. Claude Code passes its status line command the session's `rate_limits` for claude.ai Pro and Max accounts, so `pocolobby statusline`, set as the agent's status line, reads them, learns the signed-in account from `claude auth status --json` every 5 minutes, and sends a reading with the agent's credential when it changes or every 5 minutes (`PUT /v1/usage`). A status line holds the credential but belongs to no runtime, and a reading is a fact about the account rather than an action in the lobby, so the route needs no runtime binding. The broker keeps only the latest reading per agent, in memory: it is a gauge, gone on restart until the next report minutes later, and never history. The dashboard drops a window past its reset, marks a reading older than 30 minutes with its age, and hides one older than a day. Agents report their own accounts and nothing else maps agents to accounts: agents sharing one account show the same numbers, and the dashboard groups them by the email they report.
|
|
331
|
-
- **Weekly pace, not a budget engine.** The aim is each subscription's week spent evenly, about 14 points a day, ending at 0% left as it resets; the five-hour window is ignored. The line, the share of the seven-day window that has passed, needs no history, so the broker computes it from the latest reading and shows how far ahead or behind each account is: in `list_agents`, for the coordinator, and in the dashboard's Accounts section, where more than 5 points either way is off pace.
|
|
331
|
+
- **Weekly pace, not a budget engine.** The aim is each subscription's week spent evenly, about 14 points a day, ending at 0% left as it resets; the five-hour window is ignored. The line, the share of the seven-day window that has passed, needs no history, so the broker computes it from the latest reading and shows how far ahead or behind each account is: in `list_agents`, for the coordinator, and in the dashboard's Accounts section, where more than 5 points either way is off pace. When the lobby paces, the broker paces workers by it ([API](API.md#pacing-workers)): no new tasks above the line, open again well behind, never a rest, so a worker always finishes its task; the state is kept in the database and sent as no event, since discovery shows it and the send refusal tells a coordinator when it matters. On-demand agents and coordinators are never paced, so a shared account keeps room for them. Saved usage history, burn-rate estimates and pace messages from the broker were left out, because the latest reading already says where an account stands.
|
|
332
332
|
- **Notes at one commit per source.** Each room names one repository and its notes paths. A fetch resolves the ref to a commit with a conditional request, then reads every path at that commit, downloading only blobs whose SHA is new, so a push during a fetch cannot mix versions. All network work happens outside SQLite; one short transaction replaces the snapshot, and only if the source's settings are still those the fetch started with. The ETag advances only with the snapshot it describes, so a fetch that failed after a new commit is retried instead of being answered 304 forever. Files and sources are cut to a fixed share, visibly.
|
|
333
333
|
- **The summarizer runs on changes.** Its request is built deterministically from the snapshots' file contents (not their commit, so app commits that leave the notes alone change nothing), the lobby's newest 40 report and assignment messages (whatever their age, so an old one leaving a time window cannot change the input and pay for a summary with nothing new) and the ID and name of each agent the owner has not hidden; its system prompt is sent without prompt caching. It runs only when someone opens the dashboard (no timer, so a dashboard nobody looks at costs nothing), when the SHA-256 of that request differs from the last good summary's and at least the interval has passed since the last attempt (an input found unchanged is not built again for a notes interval, however often the page polls). Messages leave the input only by expiring, which says nothing new, so an input that only lost messages since the last answer this broker process stored, a summary or an answered failure, counts as unchanged, as does one on which the model already answered with a failure. A paid attempt whose record cannot be stored, not even as a failure (as on a full disk), would escape the daily ceiling, so it waits in memory and stops further paid attempts until it is stored. A lobby's summaries may cost at most $5 in any 24 hours, on the operator's key: Haiku with an open dashboard and changing input stays near $1.60 a day, while a lobby on Sonnet could otherwise spend $30 or more. Notes are cut beyond 200,000 characters, about 85K tokens at the 2.4 characters a token measured on one lobby's notes, so a request stays under Haiku's 100K-token price step; one runs at a time per lobby, with a two-minute timeout and one retry. Every attempt is stored with its usage and cost, failures included, so the owner's status shows what the dashboard really costs.
|
|
334
334
|
- **Failure keeps the last good picture.** A failed fetch keeps that source's snapshot, a failed or invalid summary keeps the last good one, and the page says so in plain words while still rendering every live fact. Missing secrets are named on the page.
|
package/docs/LOCAL-SETUP.md
CHANGED
|
@@ -144,7 +144,7 @@ Set it as the agent's status line, with the agent's own credential file, in its
|
|
|
144
144
|
- It replaces any status line the agent had.
|
|
145
145
|
- The command runs in a shell, so `pocolobby` must be on its PATH. Otherwise give the absolute path of the install the MCP file starts, such as `~/.pocolobby/npm/node_modules/.bin/pocolobby`.
|
|
146
146
|
- It sends a reading when it changed, and every 5 minutes otherwise, with a 3-second timeout. A failure is silent and retried on the next run. It remembers what it sent in a small file in the OS temp directory.
|
|
147
|
-
-
|
|
147
|
+
- It runs `claude auth status --json` to learn the signed-in account's email and plan every 5 minutes, and for each report that carries the week, at most every half minute: the week goes out only with an email that same run looked up, so a session switched to another account never sends that account's week under the name it had before, and between lookups the rest of the reading goes without it. That command follows the session's `CLAUDE_CONFIG_DIR`, so it reports the agent's own profile.
|
|
148
148
|
- Every 5 minutes it also asks a headless Claude Code for its usage, which has the weekly windows of models with a limit of their own, such as Fable's, that the status line input lacks. It runs `claude --safe-mode --no-session-persistence --print` with stream JSON for about a second: no prompt, no model call, and no MCP server, hook or plugin. It follows the session's `CLAUDE_CONFIG_DIR` like the account lookup. If it fails or takes over 10 seconds, the rest of the report goes as usual and it tries again 5 minutes later.
|
|
149
149
|
- Without `rate_limits`, as with an API key or before the session's first response, it prints nothing and sends nothing.
|
|
150
150
|
- To test it by hand, add `--dry-run`: it prints the line and "Dry run, not sent: PUT /v1/usage {…}", or says the input has no `rate_limits`, and sends and remembers nothing, so a test never puts numbers on the dashboard. For example:
|
|
@@ -154,7 +154,7 @@ Set it as the agent's status line, with the agent's own credential file, in its
|
|
|
154
154
|
| pocolobby statusline --config ~/.pocolobby/AGENT.json --dry-run
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
-
The broker keeps
|
|
157
|
+
The broker keeps each agent's latest reading in memory, and its account's week in the database: after a broker restart the line returns with the next report, within 5 minutes. A window past its reset is left out, a reading older than 30 minutes says "as of 2 h ago", and after 24 hours without a reading the line goes. Reset times are in the viewer's local time, the time alone when it is today, and under 20% left is marked in red. The account email, and the plan with it, shows only on a private dashboard; a public dashboard shows the usage without them. Agents signed in to one account show the same numbers, and the dashboard's Accounts section groups them by email, with each account's [weekly pace](API.md#weekly-pace); `list_agents` gives every agent's `usage` with that pace, for the coordinator. Validated with status line input in the documented shape only; no real Claude session has reported yet.
|
|
158
158
|
|
|
159
159
|
## Turn activity and token counts
|
|
160
160
|
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -285,7 +285,7 @@ Before a bridge reload, check what the MCP file will start: `pocolobby check-con
|
|
|
285
285
|
|
|
286
286
|
Never reset a runtime or change a request ID to make an upgrade go through. A send whose result was lost during a deploy is retried with its original `client_request_id`; the bridge never resends on its own.
|
|
287
287
|
|
|
288
|
-
A broker opens a database of its own schema version
|
|
288
|
+
A broker opens a database of its own schema version (schema 20), and upgrades one of schema 19. A release that changes the schema upgrades the version before it in place, once, in one transaction, when it starts, and the release after it drops that step. It refuses any other version, naming both, and leaves that database untouched. A release with a new storage schema says how the database reaches it, and the pre-upgrade snapshot is the only way back: an older broker refuses the newer schema.
|
|
289
289
|
|
|
290
290
|
**Deploy downtime.** Scheduled deliveries and schedule occurrences that fall due while the broker is down are delivered when it starts, marked `late`. A schedule that missed several times sends one late occurrence, for the latest. Plan deploys away from important scheduled times if lateness matters.
|
|
291
291
|
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -41,7 +41,7 @@ pocolobby check-config --mcp-config "$HOME/.pocolobby/frontend.mcp.json"
|
|
|
41
41
|
|
|
42
42
|
Each agent gets its own key file. Its MCP file tells Claude Code how to start PocoLobby's bridge with that key; it names the key file and does not contain the key. `check-config` confirms that the MCP file starts the install on your PATH, shows the broker address the key file names, and reports `"problems": []`. Give every owner command that names an agent or a room `--lobby my-team`. Without it, owner commands act in the broker's only lobby, and are refused with `lobby_required` once it has several.
|
|
43
43
|
|
|
44
|
-
`agent add` takes options: `--coordinator` lets the agent create rooms and change their members, `--broadcast` lets it message the whole lobby, and `--role-file` gives it standing instructions. `--work`, `--machine` and `--screen yes|no` tell the other agents what it does, where it runs and whether it has the screen, and `pocolobby agent describe NAME --work TEXT` changes them later.
|
|
44
|
+
`agent add` takes options: `--coordinator` lets the agent create rooms and change their members, `--broadcast` lets it message the whole lobby, and `--role-file` gives it standing instructions. `--work`, `--machine` and `--screen yes|no` tell the other agents what it does, where it runs and whether it has the screen, and `pocolobby agent describe NAME --work TEXT` changes them later. `--kind on-demand` makes it an agent that runs on occasion; otherwise it is a worker, which the broker [paces](API.md#pacing-workers) by its account's week unless the lobby's Settings › Pacing says No pacing.
|
|
45
45
|
|
|
46
46
|
## 4. Start each agent's Claude Code session
|
|
47
47
|
|
package/examples/PACING.md
CHANGED
|
@@ -1,33 +1,23 @@
|
|
|
1
1
|
# Pacing several Claude subscriptions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The broker paces workers by their account's week ([Pacing workers](../docs/API.md#pacing-workers)) unless the lobby's owner chooses No pacing under Settings › Pacing. There each agent is a worker, which works continuously, or on-demand, which runs on occasion; the operator can also set it with `pocolobby agent describe NAME --kind worker|on-demand`. It needs every agent's status line set up as `pocolobby statusline` ([local setup](../docs/LOCAL-SETUP.md)), so the lobby knows each agent's account and its week. By default a worker takes no new tasks once its account is ahead of its week's line, and new tasks again at −3; it always finishes what it has.
|
|
4
|
+
|
|
5
|
+
Add this to your coordinator's role, for example with `pocolobby agent add NAME --coordinator --role-file role.txt` or the owner route that updates a role:
|
|
4
6
|
|
|
5
7
|
```text
|
|
6
|
-
Pacing:
|
|
7
|
-
|
|
8
|
+
Pacing: the broker paces workers by their account's week. In list_agents, a worker's pacing
|
|
9
|
+
is open or no_new_tasks: it finishes what it has, and new assignments and requests to it are
|
|
10
|
+
refused with no_new_tasks. Give new work to open workers. When a send is refused with
|
|
11
|
+
no_new_tasks, give the task to another agent, or, when it continues work the worker already
|
|
12
|
+
has, send it as a request or question with reply_to.
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
lead (points ahead, positive, or behind) and its pace. Agents on one account show the same week.
|
|
12
|
-
Skip an account whose week is stale (no fresh report for 15 minutes): its numbers may lag.
|
|
14
|
+
On-demand agents and coordinators are never paced: they keep room on a shared account, most
|
|
15
|
+
of all before its week resets. Use them for occasional work, not as extra workers.
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
account's reset, so they resume by themselves then.
|
|
19
|
-
- Resume it once that account's lead is back within 2. Rest a second worker on the same account
|
|
20
|
-
only if it is still ahead six hours later.
|
|
21
|
-
- When an account is behind (lead below -5), give its agents more work, resuming rested workers
|
|
22
|
-
first.
|
|
23
|
-
- Some models have a weekly limit of their own, apart from the week: usage.model_windows lists
|
|
24
|
-
them per account (Fable's, for example), each with its own line, lead and pace. For an agent
|
|
25
|
-
running such a model, pace on that window as well as the week, since it can run out first.
|
|
26
|
-
When it is ahead, switch that agent to another model or rest it, and skip a stale window.
|
|
27
|
-
- Note in your checkpoint whom you rested and why, so a repeated check never rests another by
|
|
28
|
-
mistake.
|
|
17
|
+
Some models have a weekly limit of their own, apart from the week: usage.model_windows lists
|
|
18
|
+
them per account (Fable's, for example), each with its own line, lead and pace. The broker
|
|
19
|
+
does not pace them: for an agent running such a model, switch it to another model or rest it
|
|
20
|
+
with rest_agents when its window is ahead, and skip a stale window.
|
|
29
21
|
```
|
|
30
22
|
|
|
31
|
-
Give the coordinator an hourly schedule if it has none, for example `schedule_message` to itself with `{"time_zone": "Europe/Stockholm", "times": ["00:47", "01:47", "…", "23:47"]}` and the content "Pacing check: read list_agents and apply the pacing rules in your role."
|
|
32
|
-
|
|
33
23
|
The dashboard's Accounts section shows the same numbers: one card per account, a bar of its week with a mark at the target, green on target, blue for room left, orange ahead and red more than 20 points ahead. A card has a second bar for each model window, such as Fable's.
|