Changeset 0.24.0 (#156)
This commit is contained in:
73
CHANGELOG.md
73
CHANGELOG.md
@@ -2,6 +2,79 @@
|
||||
|
||||
All notable changes to Chat Switchboard.
|
||||
|
||||
## [0.24.0] — 2026-03-06
|
||||
|
||||
### Added
|
||||
- **Auth provider abstraction.** New `server/auth/` package with `Provider` interface: `Authenticate()`, `Register()`, `SupportsRegistration()`, `Mode()`. Three modes defined: `builtin` (implemented), `mtls` (v0.24.1), `oidc` (v0.24.1). `AUTH_MODE` env var selects provider at startup. mTLS/OIDC fail-fast with clear error if selected before implementation lands.
|
||||
- **Builtin provider.** `auth.BuiltinProvider` extracts login/register logic from `handlers/auth.go` into the `Provider` interface. Identical behavior to v0.23.2 — reads `{login, password}` from request body, validates bcrypt hash, returns user with plaintext password as `VaultHint` for UEK unlock.
|
||||
- **User handles.** `handle` column on `users` table — unique, auto-generated from username via `models.HandleFromName()`. Used as the `@mention` identifier for human users. Editable. `UniqueHandle()` appends `-2`, `-3` suffixes on collision (same pattern as persona handles).
|
||||
- **Auth source tracking.** `auth_source` column on `users` table (`builtin`/`mtls`/`oidc`). `external_id` column for IdP subject IDs or cert fingerprints. Composite unique index on `(auth_source, external_id)`.
|
||||
- **`GetByHandle()`** and **`GetByExternalID()`** on `UserStore` interface — both Postgres and SQLite implementations.
|
||||
- **Design document.** `docs/DESIGN-0.24.0.md` — full spec for v0.24.0–v0.24.3 (auth abstraction, mTLS/OIDC, permissions, anonymous sessions). All open questions resolved.
|
||||
|
||||
### Changed
|
||||
- **Auth handler refactored.** `AuthHandler` now holds a `provider auth.Provider` field. `Login()` delegates to `provider.Authenticate()`, then handles vault unlock and JWT generation. `Register()` delegates to `provider.Register()`. `generateTokens()` response includes `handle` and `auth_source`.
|
||||
- **`BootstrapAdmin()` / `SeedUsers()`** backfill `handle` for existing users on startup (idempotent — skipped if handle already set). New users created with `auth_source=builtin` and auto-generated handle.
|
||||
- **@mention resolution uses handles.** `resolveMention()` steps 3–4 now query `LOWER(handle)` instead of `LOWER(username)`. Backend and frontend aligned.
|
||||
- **User search includes handles.** `GET /api/v1/users/search` returns `handle` field and filters on handle alongside username and display_name.
|
||||
- **Autocomplete matches on handle.** `channel-models.js` filters user candidates by handle, username, and display name. The `@mention` token inserted into the input is the user's handle (not username).
|
||||
- **User store queries updated.** All `SELECT` statements in both Postgres and SQLite user stores include `auth_source`, `external_id`, `handle`. Unified `scanOneUser` helper in Postgres, `scanOne` in SQLite — eliminates per-method scan block duplication.
|
||||
- **User list API.** `List()` response includes `auth_source` and `handle` for all users.
|
||||
|
||||
### Migration Notes
|
||||
- **DB wipe required.** Migration 018 adds three columns to `users`. Existing users backfilled with `auth_source='builtin'` and handle derived from username.
|
||||
- **New columns:** `users.auth_source`, `users.external_id`, `users.handle`.
|
||||
- **New indexes:** `idx_users_handle` (unique), `idx_users_external_id` (unique composite, partial).
|
||||
|
||||
## [0.23.2] — 2026-03-06
|
||||
|
||||
### Added
|
||||
- **Unified active conversation.** `App.activeConversation = { id, type }` replaces `App.currentChatId` and `App.currentChannelId`. All code paths (send, stream, model bar, session restore, sidebar highlight) keyed off `App.activeId`. `setActive(id, type)` method, `activeType` getter, `getActiveChat()` helper.
|
||||
- **Group leader default response.** When a `type=group` channel receives a message without an @mention, the leader persona responds. Leader resolved from `persona_group_members WHERE is_leader` via channel participants.
|
||||
- **`@all` fan-out.** `@all` in message content routes to every persona participant. Depth-1 only — responses from @all do not trigger further chains.
|
||||
- **Human message attribution.** Messages from other human participants show their avatar and display name instead of "You". Persona attribution verified for loaded history in channel context.
|
||||
- **Channel lifecycle.** Archive action via context menu (sets `is_archived`, `archived_at`). Delete gated by `channel_retention.mode` policy (`flexible` allows delete, `retain` forces archive-only). Admin purge with `purge_after_days` floor. Retention config keys in `global_config`.
|
||||
- **Participant mutation guards.** DMs block removal below 2 human participants. Groups block removal of last persona (returns 400 with clear error).
|
||||
- **Channel context banner.** Slim bar below chat header showing ai_mode, topic, and routing hints. Adapts by conversation type (DM: partner name + @mention hint; Group: leader/all routing; Channel: ai_mode + topic).
|
||||
- **`NotifyUserMention` via WebSocket.** Replaced broken gin context interface cast with direct `hub.SendToUser()`. Delivers `user.mentioned` event with channel_id, from_user, content preview.
|
||||
- **User mention notification toast.** `user.mentioned` WebSocket event handler shows toast with content preview and increments unread badge.
|
||||
|
||||
### Fixed
|
||||
- **Channel persistence through refresh.** `loadChannels()` read `resp.channels` but `ListChannels` returns paginated response with `data` key. Channels vanished on reload.
|
||||
- **Folder drag-and-drop.** Three compounding issues: `folderId` never mapped in `loadChats()`, folder groups had no drag handlers, no unfiled drop zone. All fixed.
|
||||
- **DM creation 404.** Route didn't exist. Added `GET /api/v1/users/search?q=` endpoint. Rewrote DM creation UI from text input to lazy search modal.
|
||||
- **Channel ⋯ menu inconsistency.** Replaced ✕ delete button with ⋯ hover button matching folder pattern.
|
||||
- **Folder ⋯ button reflow.** `display:none/block` caused arrow shift on hover. Changed to `visibility:hidden/visible`.
|
||||
- **Folder delete spill.** Now shows three-button modal: "Keep chats" / "Delete chats too" / Cancel.
|
||||
- **Unread subquery dependency.** Query used `last_read_message_id` (migration 017) which may not exist. Rewrote to use `last_read_at` (migration 005).
|
||||
- **Settings Models section.** Template section fell through to generic div. Added dedicated template section.
|
||||
- **`u.avatar` → `u.avatar_url`** in `ListMessages` JOIN and `treepath/path.go` `resolveSenderInfo()`.
|
||||
|
||||
### Migration Notes
|
||||
- **Migration 017:** `last_read_message_id` column on `channel_participants`.
|
||||
|
||||
## [0.23.1] — 2026-03-05
|
||||
|
||||
### Added
|
||||
- **Conversation type taxonomy.** Five types: `direct` (1:1 AI chat), `dm` (human-to-human), `group` (multi-participant), `channel` (named persistent space), `workflow` (staged process, deferred). Channel type constraint extended.
|
||||
- **`ai_mode` on channels.** `auto` (AI responds to every message), `mention_only` (AI silent unless @mentioned), `off` (AI disabled). Completion handler checks `ai_mode` before dispatching.
|
||||
- **Three-section sidebar.** Projects → Channels → Chats. Each section independently collapsible with localStorage persistence. Channels sorted by last activity. Chats keep existing time-grouped behavior.
|
||||
- **Folder system.** `chat_folders` table with full CRUD. Drag chats into/out of folders. Folder context menu: rename, delete. `folder_id` column on channels.
|
||||
- **DM plumbing.** `resolveMention()` extended to resolve users (exact + prefix on username, self-mention blocked). User @mention skips AI, delivers notification. DM creation flow with user search.
|
||||
- **Presence heartbeat.** `user_presence` table. `POST /presence/heartbeat` (30s upsert), `GET /presence?users=...` (90s threshold). Client heartbeat interval with visibility pause/resume. Presence WebSocket events.
|
||||
- **Channel participant CRUD.** `channel_participants` handler: list, add, update role, remove. Auto-created on channel creation. DM participants auto-added from `req.Participants`.
|
||||
- **Persona groups wired.** CRUD endpoints for `persona_groups` and `persona_group_members`. `is_leader` flag on members.
|
||||
- **`topic` column** on channels for header display.
|
||||
- **User search endpoint.** `GET /api/v1/users/search?q=` — returns id, username, display_name for active users (max 20, excludes caller).
|
||||
|
||||
### Changed
|
||||
- **`CreateChannel` accepts `type` field** (default `direct`). `ListChannels` supports `?types=dm,channel` multi-filter.
|
||||
- **Channel sidebar rendering.** `renderChannelsSection()` shows # for channels, person icon for DMs. Online dots from presence data. Unread badges.
|
||||
- **Chat list filtering.** `renderChatList` excludes `type=channel` and `type=dm` — only direct/group chats appear in the Chats section.
|
||||
|
||||
### Migration Notes
|
||||
- **Migration 016:** `ai_mode`, `topic` on channels. `user_presence` table. Channel type constraint extended to include `dm`, `channel`.
|
||||
|
||||
## [0.23.0] — 2026-03-05
|
||||
|
||||
### Added
|
||||
|
||||
Reference in New Issue
Block a user