### Added

- **Session tags** — sessions now support an optional `tags` field (stored as a JSON array in SQLite). Tags are single-value per session; the first tag determines sidebar grouping. A new `tags TEXT NOT NULL DEFAULT '[]'` column is added via backward-compatible migration.
- **`GET /api/sessions/tags` endpoint** — returns all distinct tags for a given workspace (`?workspaceId=...`), powering autocomplete suggestions in the client.
- **Tag updates via `PUT /api/sessions/:id`** — the update route now accepts `{ tags: string[] }` and persists tag changes.
- **Automatic session title generation** — new sessions still start as `New Session`, but after the first user message the server can generate a short topic title using the configured default model. Generation is best-effort and failures do not interrupt chat.
- **Manual session title regeneration** — added a `session.generate_title` WebSocket message and SDK helper for regenerating a session title from the full conversation on demand.

### Changed

- **`Session` SDK type** — added `tags?: string[]` field to the `Session` interface.
- **`UpdateOptions` in SDK REST client** — added `tags?: string[]` to the sessions update payload.
- **`listTagsByWorkspace` store function** — new query returning sorted distinct tags for a workspace.
- **Manual rename preservation** — explicit user renames now mark the session metadata so automatic title generation will not overwrite custom names.
- **Title generation resilience** — generated titles are parsed from strict `<title>...</title>` output, rejected when they look like reasoning/examples/third-person narration, and fall back to a short title derived from the first user message when possible.
- **Title generation diagnostics** — best-effort title generation now logs scoped `[session-title]` skip, failure, rejection, and success messages on the server while remaining silent to clients.
