feat(chat): conversations persistantes + mémoire multi-tour (v0.36.0)

Store SQLite conversations/messages (propriété par actor, fenêtre 20),
historique rejoué au LLM (runAgentTurn history), ChatService persiste et
renvoie conversationId. API GET/DELETE /conversations + chat avec
conversationId. UI Chat: sidebar conversations (drawer mobile), nouvelle,
reprise, suppression. docs/conversations.md. 83 tests verts, build web vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016w5jRe87MGdd6AMvXQcHNi
This commit is contained in:
Kantin-Petit
2026-06-23 23:25:42 +02:00
parent 4f3c85901e
commit 0da5e2aba1
11 changed files with 660 additions and 89 deletions
+54
View File
@@ -0,0 +1,54 @@
# Conversations persistantes (mémoire multi-tour)
CHLOVA stocke chaque conversation et **rejoue l'historique récent** au LLM, pour
qu'il garde le contexte d'un message à l'autre et que l'utilisateur puisse
**reprendre** une conversation plus tard.
## Modèle
SQLite (`node:sqlite`, même fichier que la table assets — `CHLOVA_DB_PATH`).
| Table | Colonnes |
|---|---|
| `conversations` | `id` (uuid), `actor`, `title`, `created_at`, `updated_at` |
| `messages` | `id`, `conversation_id`, `role` (`user`/`assistant`), `content`, `ts` |
- **Propriété** : une conversation appartient à un `actor` (API = `api:owner`,
Telegram = `telegram:<userId>`). Accès à une conversation d'un autre acteur
refusé (`ForbiddenConversationError` → HTTP 403 / 404 selon l'endpoint).
- **Titre** : dérivé du 1er message (tronqué à 60 car.).
- **Fenêtre de contexte** : les `HISTORY_WINDOW` (= 20) derniers messages sont
rejoués au LLM (`ChatService``runAgentTurn({ history })`). Au-delà, l'ancien
n'est pas envoyé (pas de résumé/troncature par tokens en v1 — amélioration
possible si les conversations deviennent longues).
## Flux
1. `POST /api/chat { message, conversationId? }` :
- sans `conversationId` → nouvelle conversation créée, son id renvoyé ;
- avec `conversationId` → historique chargé + rejoué, message ajouté.
- réponse : `{ reply, conversationId }`.
2. `GET /api/conversations` → liste (id, titre, dates) de l'acteur, par récence.
3. `GET /api/conversations/:id` → messages complets (reprise).
4. `DELETE /api/conversations/:id` → suppression (messages + conversation).
## UI
Onglet **Chat** : barre latérale des conversations (drawer sur mobile, fixe en
`md+`), bouton **Nouvelle**, sélection pour reprendre, suppression. L'envoi
transmet le `conversationId` courant ; une nouvelle conversation rafraîchit la
liste.
## Surfaces
- **API/UI** : pleinement câblé (création, reprise, suppression).
- **Telegram** (si activé) : `conversationId` stable = `telegram:<userId>`
mémoire continue par utilisateur.
- **Mobile (Expo)** : chat encore sans état — branchement des conversations à
faire (réutilise les mêmes endpoints).
## Sécurité / vie privée
Les messages sont stockés en clair dans la base (volume `chlova-data`). Pas de
secret n'y transite normalement (l'agent manipule des références). La base n'est
jamais exposée ; seul le backend y accède.