pi-roundtable-web 0.22.5 → 0.23.1
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.
- package/CHANGELOG.md +53 -1
- package/README.md +444 -104
- package/assets/icons/192.png +0 -0
- package/assets/icons/512.png +0 -0
- package/assets/icons/apple-180.png +0 -0
- package/assets/icons/badge-96.png +0 -0
- package/assets/icons/mark-maskable.svg +1 -0
- package/assets/icons/mark.svg +1 -0
- package/assets/icons/maskable-512.png +0 -0
- package/dist/chunk-character-creator-660d2t6p.js +1 -0
- package/dist/chunk-connectors-d19v8cjc.js +1 -0
- package/dist/chunk-conversations-4cmexmqq.js +1 -0
- package/dist/chunk-entry-8xpx35ef.js +1 -0
- package/dist/chunk-index-19g8jmr8.js +2 -0
- package/dist/chunk-index-362dfedj.js +1 -0
- package/dist/chunk-index-6g69tqt0.js +47 -0
- package/dist/chunk-index-89q2qdkj.css +1 -0
- package/dist/chunk-index-9tvf6es7.js +41 -0
- package/dist/chunk-index-p8h0e861.js +2 -0
- package/dist/chunk-index-xw5hhmfq.js +2 -0
- package/dist/chunk-notes-fefwds14.js +1 -0
- package/dist/chunk-overview-bxpwpmn6.js +1 -0
- package/dist/chunk-skills-pce3zgxy.js +1 -0
- package/dist/chunk-table-zmpbrfvz.js +1 -0
- package/dist/chunk-table2d-ac2gez71.js +1 -0
- package/dist/chunk-table3d-g258jemr.js +4229 -0
- package/dist/chunk-transcript-yhtjp3pd.js +1 -0
- package/dist/chunk-zh-tw-anqhqpaz.js +1 -0
- package/dist/index.html +6 -2
- package/dist/sw.js +1 -0
- package/package.json +8 -3
- package/src/api-types.ts +448 -1
- package/src/app/app.ts +351 -0
- package/src/app/card-log.ts +119 -0
- package/src/app/desk.ts +533 -0
- package/src/app/files.ts +262 -0
- package/src/app/frames.ts +132 -0
- package/src/app/multipart.ts +172 -0
- package/src/app/presence.ts +73 -0
- package/src/app/protocol.ts +144 -0
- package/src/app/room-log.ts +137 -0
- package/src/app/rooms.ts +189 -0
- package/src/app/routes.ts +434 -0
- package/src/app/socket.ts +406 -0
- package/src/app/sockets.ts +84 -0
- package/src/app/surface-threads.ts +92 -0
- package/src/app/surface.ts +384 -0
- package/src/app/threads.ts +99 -0
- package/src/app/uploads.ts +363 -0
- package/src/assets.ts +1 -3
- package/src/auth/authenticator.ts +167 -0
- package/src/auth/login-page.ts +44 -0
- package/src/auth/none.ts +38 -0
- package/src/auth/password.ts +250 -0
- package/src/auth/session-key.ts +65 -0
- package/src/cleanup-api.ts +1 -1
- package/src/cloudflare-access.ts +5 -2
- package/src/console-api.ts +13 -15
- package/src/console-server.ts +77 -49
- package/src/features.ts +6 -1
- package/src/http.ts +8 -0
- package/src/i18n/index.ts +13 -0
- package/src/i18n/locale.ts +11 -0
- package/src/i18n/zh-tw.ts +696 -0
- package/src/index.ts +1 -1
- package/src/options.ts +308 -12
- package/src/page-colors.ts +9 -0
- package/src/page-presentation.ts +17 -12
- package/src/push/api.ts +190 -0
- package/src/push/channel.ts +50 -0
- package/src/push/encrypt.ts +136 -0
- package/src/push/endpoint.ts +54 -0
- package/src/push/sender.ts +109 -0
- package/src/push/subscriptions.ts +140 -0
- package/src/push/test-fetch.ts +17 -0
- package/src/push/triggers.ts +155 -0
- package/src/push/vapid.ts +124 -0
- package/src/pwa.ts +130 -0
- package/src/request-body.ts +20 -0
- package/src/verifier.ts +7 -1
- package/src/web-plugin.ts +123 -11
- package/dist/chunk-82frzte9.js +0 -36
- package/dist/chunk-e8z9q554.css +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,59 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
-
## [
|
|
6
|
+
## [0.23.1] - 2026-10-29
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- A built-in Traditional Chinese (Taiwan) catalog, used when `presentation.locale` is `zh-TW`, `zh-Hant-TW` or `zh-Hant` (any case, `_` or `-`). It covers every string the page translates, the character creator's classes (`character.class.<id>`), what each suits (`character.jobs.<id>`), parts and palettes, the sign-in page and the fixed wording of the server's refusals. A key in `presentation.messages` wins over the catalog, and every other locale stays in English. A test fails when a string in the page's code has no entry.
|
|
11
|
+
- The round table words its screen-reader labels (the table and each seat's state) through the page's dictionary instead of in English only.
|
|
12
|
+
- The page translates the app API's refusals (`There is no such room, agent or file.` and the like) through the same dictionary.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- The header's brand mark shows the served install icon (`<mount>/icons/192.png`, so `icons.dir` changes it too) instead of a fixed sparkle.
|
|
17
|
+
|
|
18
|
+
## [0.23.0] - 2026-10-29
|
|
19
|
+
|
|
20
|
+
The package becomes the owner app of a host, and keeps the console that came first.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- The owner app (`rooms` pane, on by default): the same agent team as on Discord, in rooms of its own, one per active agent and group, with Home the coordinator's. `surface` (default `app`) is the rooms' key prefix and `cardGraceSeconds` (default 120) how long a turn waits on a card. A WebSocket at `<mount>/api/socket` (subprotocol `roundtable.app.v1`) carries messages, Stop, cards (approval, question, questionnaire, time limit), progress, typing, modes and yolo approvals to every open page of every owner; its upgrade passes the console's authenticator and origin, closes with 4401 when the credential lapses (an hour at most under `password`, so a logout or a new password ends it) and with 4403 when its person is no owner, and every frame that changes something reads the owner's tier again. REST under `api/`: `session`, `rooms`, `rooms/<key>/messages`, `rooms/<key>/mode`, `rooms/<key>/start-over`, `uploads`, `files/<id>`, `schedules`, `agents/<name>`, `agents/<name>/character`, `models`, and `avatars/<hash>.png`. Messages are kept in `web_room_messages`, uploads and reply files under `<dataDir>/web/`, and files are served as downloads with `content-security-policy: sandbox` and `nosniff`. A room key a client names must be one of the app's rooms whose agent or group exists, and a message from a visitor a verifier admitted without an actor is refused with `forbidden`. `PANES` gains `rooms`, `schedules` and `table`; `ConfigView.app` tells the page the surface. See the README's "App protocol".
|
|
25
|
+
- The owner app's page (`rooms` pane), mobile first: a bottom tab bar under 720 px and a side rail above, with the older panes under "More" on a phone. **Rooms** lists Home (the coordinator's room) first, then agents, groups and dispatch threads, with badges for working, cards waiting, yolo and long-running. A **room** shows each message with its author's picture and name, Markdown with raw HTML left as text, files, a live progress strip (streamed text, tool chips, Stop), and inline cards: approvals with their details and a copy button, questions with options, Other and Cancel, questionnaires, a turn's time-limit card in its own `wording`, and late cards. The composer sticks above the keyboard, takes files (button, paste or drop) and shows Stop while a turn runs. The room's menu turns yolo mode on (after a confirmation that says what it approves) and off, turns long-running mode on and off, starts over (after a confirmation) and opens the agent's **profile** (name, prompt, model, thinking); a banner stays at the top of a room while a mode is on, and yolo approvals appear in the conversation. **Schedules** lists and cancels; **Settings** has the color scheme, an install hint or button, and Log out under `password` auth (`SessionView.auth` tells the page the mode). Ctrl or ⌘ + K switches rooms. Every string is an English key for `presentation.messages`.
|
|
26
|
+
- Dispatch threads on the owner app: the app's surface implements the core's `SurfaceThreads`. A coding or background dispatch, or a `message_agent` between rooms, started in a room opens its thread there: one start line in the room, edited to link the thread once it exists (the new `edited` frame), then the dispatch's posts, its cards and its report in the thread's room, where agents speak under their own names and a coding worker's progress is edited in place. A thread's room is read only (`send` is refused with `read_only`) but its cards are answered there. Closing marks the thread closed and keeps it readable; after a restart the host closes the threads the last run left open, with a restart notice. The room list nests each thread under its room with the finished ones folded away (`RoomView.closed`; the list keeps the 30 newest), the start line links the thread's room, and a room list that does not know a thread yet is fetched again before the page says the room is missing. `RoomLog.edit` and `WebThreads.list` are new; `WebThreads.listOpen` is gone.
|
|
27
|
+
- Above 1024 px the room list stands beside the open room, with the open one marked. A failed update of the page (code that a release replaced while it was open) reloads once by itself and otherwise shows a card with Reload; any other failure shows the same card instead of a blank page. A message that cannot go says "Not sent" with Try again and Discard, a lost connection shows a banner under the room's header, and answered cards show the answer given. Opening or reloading a room in place (a link, a notification, an installed page restoring its route) works, and Enter that commits an input method's text does not send the message in Safari.
|
|
28
|
+
- Ended cards stay in a room's history. An approval, a question, a questionnaire or a time-limit card is kept in the new table `web_room_cards` when it ends, with its outcome (`approved`, `declined`, `answered`, `expired`, `cancelled`, `withdrawn`), who answered, what was answered (the choices and text, or each answer and the note) and when, and `GET rooms/<key>/messages` returns `cards` beside `messages` and `more`: the cards that opened within the page's span, so every card is on exactly one page and stands among the messages by `PromptFrame.at`, as a card sent again after a reload does. A reload, a new tab or an automatic reload no longer loses them, and the page no longer keeps only the newest 30 ended cards: it keeps those of the messages it holds. `prompt_closed` carries `by`, `answer` and `closedAt` too, so a page that watched the card shows what one that reloaded shows; a card that is shown already approved keeps its wax seal still. `ClosedCard`, `CardAnswer`, `MessagesView.cards` and `CardLog` are new; the owner app lists `CardLog.migration` (add only).
|
|
29
|
+
- `ServerFrame` `refused` carries `prompt` when the refused frame answered a card, and `PromptFrame` carries `at` (when the card opened), which places a card sent again after a reload among the messages by time.
|
|
30
|
+
- Files of any kind, 100 MiB each (the `uploadMaxBytes` option), streamed to disk as they arrive instead of held whole. An image is checked by its first bytes, a UTF-8 text is kept as text (`text/markdown`, `text/csv`, `application/json` or `text/plain`), and anything else as `application/octet-stream` with its own name; the type the browser reported no longer decides, and `ready.uploads.types` is gone. A person may have 16 files and 400 MiB waiting, and a file over the limit is refused with 413 before it is read when its length says so. Stored files are streamed back, not read whole. See the README's "Uploads".
|
|
31
|
+
- A paste of 4,000 characters or more becomes a `pasted-text-<n>.txt` chip (with its size and first line; a click puts it back in the box when it fits), and a message over 32,000 characters is sent as `message.txt` with the line "(long text attached)". A full set of file slots says so instead of dropping the file.
|
|
32
|
+
- The `auth` option: `{ mode: "none" }`, `{ mode: "password" }` or `{ mode: "verifier", verifier }`. The `verifier` option stays as shorthand for the last, giving both throws, and giving neither still refuses to start. One authenticator now checks every request, page, API and event stream, in place of the server's own admission step, and the owner app's WebSocket upgrade will use it too.
|
|
33
|
+
- `none` must be written out. It serves only on a unix socket or TCP on `127.0.0.1`, `::1` or `localhost`, unless `trustNetwork: true`; it warns at every start, and speaks for the primary owner (or `principal`) through the plugin identity `token:web-console`.
|
|
34
|
+
- `password` reads a secret of at least 12 characters from the environment (`ROUNDTABLE_WEB_PASSWORD` by default), serves a login page with no script, and sets a signed `HttpOnly`, `SameSite=Strict` cookie (`Secure` over https) keyed from `<dataDir>/web/session.key` and the password, so a new password signs everyone out. A failed login waits one second, ten failures in 15 minutes lock every login with `429` and `Retry-After`, and `POST <mount>/auth/logout` clears the session.
|
|
35
|
+
- `Verdict.admitted.expiresAt`: when a verifier's assertion lapses. `cloudflareAccess` fills it from the token's `exp`, and `admitAs(actor, expiresAt?)` takes it.
|
|
36
|
+
- An installable app (PWA). `<mount>/manifest.webmanifest` (`id`, `start_url` and `scope` are `<mount>/`, `display: standalone`, `title` as the name, colors from `appearance.look`), `<mount>/sw.js` and `<mount>/icons/*`; the page links the manifest with `crossorigin="use-credentials"` so a proxy cookie reaches it. The service worker precaches the hashed assets and `index.html`, serves assets cache-first and navigations network-first with the cached shell as the offline fallback, never touches `api/`, `auth/`, `login`, `files/` or `avatars/`, and returns redirects untouched, so a proxy's login still works and a shared device keeps no data after logout. The package ships the five icons (`192.png`, `512.png`, `maskable-512.png`, `apple-180.png`, `badge-96.png`); `icons.dir` replaces the set, and the release preflight requires `dist/sw.js` and the icons. Settings shows an install button where the browser offers one and the iPhone hint otherwise.
|
|
37
|
+
- Web Push for the owner app (`rooms` pane). An owner turns it on per device in Settings, where every device is listed and can be removed; logging out unsubscribes the device. The server pushes only when no page of that owner is in view: when a card opens in an app room ("Card waiting", or "Long-running cap reached" for a time-limit card in a long-running room), when an agent posts in a room that no message from a page started (a schedule's or report's reply, or a late one), and for a `notify` notice, through a new `DirectChannelProvider` named `web:<surface>` (`deliver` writes the notice in the Home room and pushes it, `mirror` only pushes when another channel such as Discord delivered). The payload is sealed for the device with RFC 8291 `aes128gcm` and posted with an RFC 8292 VAPID token, using WebCrypto and the existing `jose`, with no new dependency; it carries a title, the room key and, unless `push.preview` is `false`, the first 120 characters. The VAPID key is made on first start in `<dataDir>/web/vapid.json` (0600) and a file that cannot be read stops startup. A subscription's endpoint must be `https` on a known push service (Google, Mozilla, Apple, Microsoft, or `push.endpointHosts`), or the request answers 400; a 404 or 410, or five failures in a row, drops the device. New routes `GET api/push/key`, `GET/POST/DELETE api/push/subscriptions` and `DELETE api/push/subscriptions/<id>`, table `web_push_subscriptions`, option `push` (`preview`, `endpointHosts` and `subject`, the VAPID contact, which defaults to an https `origin`; push stays off, and devices cannot subscribe, until one is set), and the service worker's `push` and `notificationclick` handlers. `SessionView.push`, `PushKeyView`, `PushDevicesView` and `PushDeviceView` are new.
|
|
38
|
+
- Characters in the page (`rooms` pane). Agents are drawn as their composed characters on the room list, in the room's header, in messages, in the room switcher and on the profile, in place of the drawn pictures. Each character moves with its state (`thinking`, `working`, `waiting` while a card is open, `done`, `error`, `sleepy`), which the page works out from `activity`, `presence` and the cards, and stands still under reduced motion; an agent's accent color (`--agent-accent`) colors its row, header, progress strip and composer focus ring. The profile gains a creator: a live preview that cycles the states, the 24 classes, eight palettes and custom colors, strips for the parts, "Suggest from role", "Randomize", and Save through `PUT api/agents/<name>/character` with a refused field shown under its strip. Settings picks the Modern or Camelot look per device, and `ConfigView.look` goes on the page as `data-look`. The page checks every spec with `parseCharacter` before it draws one and injects only `composeSvg`'s output. The character kit loads as a chunk of its own, so the first load grows by under 4 KB gzipped. `RoomState` gains `phase`, `agent`, `outcome` and `presence`, and the labels are the message keys `character.class.<id>`, `character.<kind>.<id>` and `character.palette.<name>`. See the README's "Characters in the page".
|
|
39
|
+
- Characters on the server (`rooms` pane). An agent room's `RoomView.character` and `AgentProfileView.character` carry its composed character, and `PUT api/agents/<name>/character` changes it (owner only, at most 4 KiB; `400 {error, field}` for a look that is not a character). `activity` frames gain `phase`, `agent` and `outcome`, a `presence` frame says which agents are busy, even in Discord, and `appearance: { look: "modern" | "camelot" }` sets the default look and the manifest colors (`ConfigView.look`).
|
|
40
|
+
- The Camelot look (`rooms` pane). Under `appearance.look: "camelot"`, or a device's pick in Settings, the page takes parchment and night-castle colors with gold, serif headings from the system fonts, gold rules and header corners, the shield frame on characters, a wax seal in the agent's accent on an approved approval card (stamped in 450 ms when the card turns approved while the page shows it, still on a card shown already approved, hanging over the card's lower corner), and a candle and rune strip on the progress strip. The `theme-color` tags and the pre-paint `theme-init.js` follow the look and the scheme, and the script puts `data-look` on the page before the first paint. Settings shows each look as a swatch with a line about it. Everything is CSS and transform or opacity animation, static under reduced motion, with no new font or image. `PAGE_COLORS` (`src/page-colors.ts`) holds the toolbar colors, `themeInitScript(look)` and `presentPage`'s fourth argument carry the look, and `bun run fixture:app` takes `LOOK=camelot`. See the README's "The Camelot look".
|
|
41
|
+
- The round table (`table` pane, on by default after `rooms`, and needing it). Every active agent sits at one round table, the coordinator first, in every look: the seat shows the character in the shield, the agent's accent and its state (`sleepy` as `idle`, and `speaking` for the member a group round names), and a click opens the agent's room. A change of face redraws that seat's picture after 300 ms. A WebGL scene (the `three` chunk is fetched when the view first opens, never precached) or an SVG table where there is no WebGL; the camera starts high enough for the ring to fill a phone's canvas, more than ten seats on a phone are also listed as buttons, and under reduced motion the table is one still frame. A group room's header gains a "Table" button (a sheet on a phone, a panel elsewhere) and, under Camelot, a compact strip of its members with the speaker lit. Seat names are drawn in front of the table and of every badge, at 11 px or more (the plates grow with the canvas as far as their neighbours leave room, and where that is not enough the plates carry only their color bar and the seats are listed as buttons under the table), and cut with an ellipsis only when longer than their plate; the seat across the table stands a little higher so the far edge does not cut its head; `?nogl` on the page address draws the SVG table. `PANES` gains `table`; the default `panes` list becomes `rooms`, `table`, `overview`, `schedules`, `conversations`, `notes`, and a host that lists its panes keeps them as written. See the README's "The round table".
|
|
42
|
+
- Light and dark color schemes, from one token file (`web/tokens.css`): the page follows the system until the person picks one in Settings or with the header button, which is kept in `localStorage` and applied before the first paint; the switch grows as a circle from the button where the browser has view transitions. Motion is limited to `transform` and `opacity` and stops under `prefers-reduced-motion`.
|
|
43
|
+
- `bun run fixture:app`: a host over a scratch PostgreSQL database with scripted agents (`approve`, `ask`, `quiz`, `timeout`, `late`, `stream`, `dispatch`, `exchange`) for trying the app in a browser.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- **Breaking:** the default `panes` are `rooms`, `table`, `overview`, `schedules`, `conversations`, `notes` (they were `overview`, `conversations`, `notes`). An explicit list is kept as written, so a host that listed its panes adds the new ones by hand; `schedules` and `table` need `rooms`.
|
|
48
|
+
- The `verifier` option is shorthand for `auth: { mode: "verifier", verifier }`; giving both `auth` and `verifier` throws, and giving neither still refuses to start. One authenticator now checks every request, in place of the server's own admission step.
|
|
49
|
+
- The agent reads an upload up to `uploadMaxBytes`, not 25 MiB: the app's surface declares `maxAttachmentBytes` as that limit, and `readAttachment` answers `{ path }` of the stored file instead of its bytes, so the host copies a large file from disk without holding it. A file over 25 MiB reaches the turn as a file for tools (not shown to the model, not read by `read_attachment`). The upload route asks the listener for `uploadMaxBytes` plus the form's framing (`maxRequestBodyBytes`), so `uploadMaxBytes` is no longer capped at 120 MiB; any whole number of bytes the listener can be asked for is accepted. `ConsoleServer.routes` takes that limit as its second argument, and `Uploads.read` answers an `AttachmentFile`.
|
|
50
|
+
- The theme colour follows the page's scheme (a `<meta name="theme-color">` for each, and `<script src="theme-init.js">` in the head applies a stored scheme before the first paint, as a file because the page's policy allows scripts from itself only); the manifest's colours are the light page's. Buttons, inputs and switches are at least 44 px high on a phone or with a coarse pointer, and control borders and the off switch meet 3:1 contrast. The progress strip has no Stop of its own (the composer's replaces Send while a turn runs), and a time-limit card without its own wording says "Stop turn".
|
|
51
|
+
- The fixture host (`bun run fixture`) takes `FIXTURE_AUTH=cloudflare|none|password`, and serves the `overview`, `conversations` and `notes` panes only.
|
|
52
|
+
- The Discord link and Cleanup are not offered for an agent group that has no channel: `GroupView.channelId` is optional and the overview card shows neither without it.
|
|
53
|
+
- The push subscription routes cap the body at 8 KiB of bytes (the declared length and what streams in) and refuse a body that starts with a byte order mark.
|
|
54
|
+
|
|
55
|
+
### Security
|
|
56
|
+
|
|
57
|
+
- `none` and `password` resist the network, not an agent whose shell runs as the same user: that agent can reach the listener's socket, the password variable and `<dataDir>/web/session.key`, and so act as the owner. Only a `verifier` resists it. The README's Authentication section describes the modes, and its threat model says what each does not resist.
|
|
58
|
+
- Every write needs an authenticated owner whose tier is read again, the owner app's JSON bodies are counted as they stream against a 64 KiB limit, and a download name's `'`, `(`, `)` and `*` are percent-encoded and bidirectional override characters dropped. The owner app's uploads are streamed to disk and counted, and a form cut short leaves no file behind.
|
|
7
59
|
|
|
8
60
|
## [0.22.5] - 2026-10-25
|
|
9
61
|
|