revert: remove user chat (direct + groups) from the pet window

Reverts 21a042d, 0d3e92c, 0016b6a, c797e98, 03ebea1. Keeps the pet's
own-sessions scope, status bar mine=1, scroll speed and the health hover card.
The chat_* tables and <data dir>/chat/ files are left in place (unused).
This commit is contained in:
2026-11-03 10:06:12 +07:00
parent db2e61a74b
commit a3d1dce2ca
24 changed files with 72 additions and 4339 deletions
-21
View File
@@ -3497,27 +3497,6 @@ A **Codex pet** is the `.codex-pet.zip` format Codex Desktop uses (the bundles s
**Featured pets are downloaded, never bundled.** The three offered (Goku, Border Collie, 11) are other people's uploads to codex-pets.net with no licence to redistribute them, so AgentHub ships none of their art: `POST /api/pets/featured/:id` takes only an id from the fixed list in `server/lib/pets.js` (`404 ENOPET` for any other, with no request made) and fetches `https://codex-pets.net/api/pets/<id>` and `/spritesheet` — no URL from the request, no redirects followed. A pet id is always made by the server (`p-<12 hex>` for an upload, `cp-<featured id>`), every write is same-origin (`403 EBADORIGIN`), and at most 50 pets are kept (`409 ETOOMANY`). Other errors: `413 ETOOBIG`, `502 EFETCH` (codex-pets.net unreachable or refusing).
### Chat between users (server mode)
The pet's window has a rail of chats beside the chat with the pet: direct chats (one per pair of users) and groups. Only a room's MEMBERS read or write it — an admin or analyst included; another user's room answers `404 ENOTFOUND` like a missing one. Local mode answers `400 ELOCALMODE`. Code: `server/lib/chat.js`, `server/routes/chat.js`, client `client/src/components/Sagi/ChatHub.tsx` + `useChat.ts`.
```
GET /api/chat/people {people: [{id, username, displayName, avatarAt, disabled}]} — every account but you; a disabled one is named in old chats, never offered for a new one
GET /api/chat/rooms {rooms} — yours, newest first: members, lastMessage, lastReadId, clearedId, reads (every member's read mark — "Seen"), unread
POST /api/chat/rooms {kind: "dm", userId} → the pair's one direct chat (201 new / 200 existing); {kind: "group", name?, userIds} → 201
PATCH /api/chat/rooms/:id {name?, add?, remove?} — the group's creator only (403 EFORBIDDEN)
DELETE /api/chat/rooms/:id Disband a group — its creator only, for every member, files included
POST /api/chat/rooms/:id/clear Delete the chat for YOURSELF: {hidden, purged} — see below
POST /api/chat/rooms/:id/leave Leave a group; the last one out deletes it and its files
GET /api/chat/rooms/:id/messages?before=&limit= {messages (oldest first), hasMore} — 50 by default, ≤ 200
POST /api/chat/rooms/:id/messages JSON {body} or multipart `body` + ≤ 10 `files` of ≤ 25 MB; body ≤ 8000 chars
DELETE /api/chat/rooms/:id/messages/:messageId Delete your OWN text message and its files (403 EFORBIDDEN otherwise, an admin included)
POST /api/chat/rooms/:id/read {upTo} — read mark, never moves back nor past the newest message; a move is sent as `chat_read`
GET /api/chat/rooms/:id/files/:fileId The file, members only, `sandbox` CSP + `nosniff`; PNG/JPEG/GIF/WebP inline, else attachment
```
Files are stored under `<data dir>/chat/<room id>/<32 hex id>` — a name the server makes. Right-click menus in the window: a room (open, mark read, mute notifications — per browser —, copy name, group settings, leave), the rail's pet and **+** (mark all read), a message (copy text, reply, message the sender privately, delete your own), a file (open, download, copy name) and a group member (message privately, remove — the creator). **Deleting a chat** is per person: the messages so far vanish for the caller only (`chat_members.cleared_id`) and a direct chat leaves their list (`hidden`) until a new message arrives; the others keep theirs. What EVERY member has deleted is deleted for real — the messages up to the lowest `cleared_id` and their files on disk, and a direct chat both people deleted, whole (`purge`, also run when a member leaves or is removed). A group whose creator left or was deleted passes to its longest-standing member. Group changes (`system` lines) are sent live as `chat_message` and never count as unread. An upload is written to `<data dir>/chat/.incoming/` and moved into place — never held in memory whole. In the browser, only the first tab to see a message plays its sound or shows a system notification; a message from someone in a room not on screen shows as a card on the pet. With the pet hidden, a 💬 button in the bottom-right corner (`ChatDock.tsx`) opens the same chat window. **Seen**: the room shows under your newest message "Seen" (direct chat), "Seen by …" or "Seen by everyone" (group), from `reads`. A group change adds a `system` message (`created`, `renamed`, `left`, `added:<ids>`, `removed:<ids>`). Every change is sent over `/ws` as `chat_message` (`{roomId, message, to}`), `chat_room` (`{roomId, to}`) or `chat_deleted` (`{roomId, messageId, to}`) or `chat_read` (`{roomId, userId, lastReadId, to}`) to exactly the user ids in `to` (`scope.exactAudience`), whatever their role.
### Session snapshots
```