fix(chat): the review's gaps — owner hand-over, live group lines, names, scroll, one tab's sound, uploads on disk, a chat button without the pet
- A group whose creator left or was deleted passes to its longest-standing member (ensureOwner), so it can still be managed. - Group changes (added / removed / renamed / left) are sent live as chat_message and never count as unread, here or on the server. - A disabled account is still named in old chats (people carries `disabled`) and never offered for a new one; a deleted one reads 'Deleted user' instead of '?'. - Load older keeps the message being read in place. - With several tabs open only the first to see a message plays its sound / system notification (agenthub.chat.notified). - The people list is read again on New chat and on every room change. - Uploads go to <data dir>/chat/.incoming/ (multer disk storage) and are renamed into place — never held in memory; a refused one is removed. - With the pet hidden, a chat button in the bottom-right corner (ChatDock, server mode) opens the same window, unread count and new-message card included.
This commit is contained in:
+2
-2
@@ -3502,7 +3502,7 @@ A **Codex pet** is the `.codex-pet.zip` format Codex Desktop uses (the bundles s
|
||||
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}]} — every enabled account but you
|
||||
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)
|
||||
@@ -3516,7 +3516,7 @@ POST /api/chat/rooms/:id/read {upTo} — read mark, never move
|
||||
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). **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.
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user