fix(ai): contexte applicatif de l'assistant, dossier vide et liens fiables (#88, BUG-041, BUG-042)
CI / lint (push) Successful in 1m16s
CI / security (push) Successful in 50s
CI / test (push) Successful in 3m0s
CI / build (push) Successful in 47s
CI / e2e (push) Successful in 10m58s

- BUG-041 : le contexte d'un dossier vide ne renvoie plus 404 ; il degrade
  vers le prompt General enrichi d'un bloc « Dossier vide ».
- BUG-042 : liens de fichiers deterministes (nom -> presse-papiers,
  dossier -> arborescence, chemin -> viewer) et resolution du chemin contre
  l'index du vault (exact -> suffixe -> basename) avant ouverture, fin des
  'File not found'.
- #88 : app_context (documents ouverts, repertoire/vault courants, recherche
  + resultats affiches) et fichiers recemment modifies injectes dans le
  prompt General.

Tests : pytest 963 passed / 6 skipped, ruff 0, mypy 0, frontend 52/52 +
validate-imports 36 modules.
This commit is contained in:
2026-09-14 11:28:30 -04:00
parent 05ed36240b
commit 314e37c433
11 changed files with 464 additions and 32 deletions
+4 -1
View File
@@ -14,7 +14,7 @@
- **Projet** : ObsiGate — Porte d'entrée web pour vaults Obsidian
- **Stack** : Python 3.11+ (backend FastAPI) · JavaScript/Vanilla (frontend) · Tauri/Rust (desktop)
- **Dernière mise à jour** : 2026-09-13
- **Dernière mise à jour** : 2026-09-14
---
@@ -150,6 +150,8 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| *BUG-038* | [🔵 MINEUR] Argon2 à 64 MB par vérification : risque d'épuisement mémoire | 🔴 ouvert | P2 | 🔐 sécurité | IA | `backend/auth/password.py:8` | Lancer de nombreux `POST /api/auth/login` simultanés | Recalibrer (~19 MB, t=2, p=1, norme OWASP actuelle) + maintien du rate-limit | DoS mémoire possible sur les petites instances |
| *BUG-039* | [🔵 MINEUR] Enumération de comptes : 429 (verrouillé) vs 401 (inconnu) | 🔴 ouvert | P3 | 🔐 sécurité | IA | `backend/auth/router.py:120` | Tenter un login sur un compte verrouillé puis un nom inconnu | Répondre 401 uniforme avec un timing équivalent | Le statut HTTP distingue l'existence d'un compte |
| *BUG-040* | [🔵 MINEUR] Extraction PDF intégrale (100 ko) au scan de démarrage | 🔴 ouvert | P2 | ⚙️ backend | IA | `backend/indexer.py:465` | Démarrer sur un vault contenant de nombreux PDF | Analyser les PDF en tâche de fond / à la demande (lazy) | Ralentit fortement le démarrage et le rebuild d'index |
| *BUG-041* | [🟡 IMPORTANT] Assistant IA : échec sur un répertoire vide (« Aucun fichier markdown trouvé dans ce dossier ») au lieu de répondre | 🟢 corrigé | P1 | 📱 frontend + ⚙️ backend | IA | `backend/bookslm_routes.py`, `backend/bookslm.py`, `frontend/js/bookslm.js` | Ouvrir l'assistant sur un dossier vide puis envoyer une question | `_resolve_system_prompt` dégrade vers le prompt Général + bloc « Dossier vide » (plus de 404) ; contexte applicatif `app_context` enrichi (documents ouverts, répertoire, recherche, fichiers récents) | Le 404 bloquait toute la requête. Feature #88, fiche `docs/features/ai-app-context.md`. Tests : `tests/test_bookslm.py` (+3), `tests/frontend/ai.test.mjs` |
| *BUG-042* | [🟡 IMPORTANT] Assistant IA : liens de fichiers non fiables (« File not found: ») — pas de règle déterministe nom / dossier / chemin | 🟢 corrigé | P1 | 📱 frontend | IA | `frontend/js/bookslm.js` | Cliquer les liens de fichiers/dossiers dans une réponse de l'assistant | `_classifyPath` distingue `name` (copie presse-papiers) / `dir` (révélation arborescence) / `file` (ouverture) ; `_activatePath()` résout le chemin contre l'index du vault (exact → suffixe → basename unique) avant d'agir | Les liens morts ouvraient un fichier inexistant. Feature #88. Tests : `tests/frontend/ai.test.mjs` (+3) |
### TODOs techniques (améliorations / nouvelles tâches)
@@ -186,6 +188,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| 2026-09-13 | Revue sécurité statique (BUG-021 → BUG-040, roadmap #84 → #87) | Enregistrement | `docs/ISSUES_TODOLIST.md`, `docs/ROADMAP.md` | Revue statique 2026-09-13 consignée : XSS markdown (`escape=False`) + page de partage publique, brute-force MFA, traversal par préfixe `paths.py`, ReDoS, SSRF webhooks, cycle de vie des sessions, politique de mot de passe, races `users.json`, audits IP, rate-limit, indexation symlinks, recherche O(N), CSP. BUG-021→BUG-040 ouverts au registre ; roadmap #84→#87 (consolidation/sécurité, refonte architecturale, performance, CI/CD). | 🔴 ouvert (à traiter) |
| 2026-09-13 | BUG-021 → BUG-034 (#84 phase 1) | Correction | `backend/services/sanitizer.py`, `backend/services/paths.py`, `backend/services/net.py`, `backend/services/regex_safety.py`, `backend/main.py`, `backend/search.py`, `backend/webhooks.py`, `backend/indexer.py`, `backend/ratelimit.py`, `backend/auth/{router,user_store,password,jwt_handler,middleware}.py`, `frontend/js/{auth,admin,sync}.js`, `tests/test_security_hardening.py`, `tests/test_auth_api.py` | Sanitizer XSS serveur (markdown + page de partage), rate-limit/lockout MFA, isolation vaults par segments, caps regex (ReDoS), SSRF webhooks + secrets externalisés, rotation/révocation des jetons, politique de mot de passe + invalidation des sessions, verrous `users.json`, IP réelle dans les audits, rate-limit par compte, symlinks d'index ignorés, recherche simple via inverted index, token en mémoire + cookie HttpOnly + CSP durcie. Vérifié : pytest 961 passed / 6 skipped, ruff 0, mypy 0, tests frontend 36 modules + 9 JSDOM suites verts. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-13 | BUG-017 → BUG-020, #83 | Correction + feature | `frontend/js/mobile-editor.js`, `frontend/style.css`, `frontend/index.html`, `frontend/locales/{fr,en}.json`, `tests/frontend/mobile-editor.test.mjs`, `tests/e2e/mobile-editor.spec.js` | BUG-017 : cartes de tableau de bord compressibles (`min-width:0`) ; BUG-018 : en-tête éditeur mobile compact + barre IA tactile ; BUG-019 : ancrage du ruban via `visualViewport` ; BUG-020 : suppression de la double scrollbar. #83 : ruban d'édition horizontal défilable style Obsidian (commandes étendues + personnalisation ⚙ persistée). Vérifié : tests frontend 35/35 (mobile) + 50/50 (IA) + 9 JSDOM suites. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-14 | BUG-041, BUG-042, #88 | Correction + feature | `frontend/js/bookslm.js`, `backend/bookslm.py`, `backend/bookslm_routes.py`, `frontend/locales/{fr,en}.json`, `tests/test_bookslm.py`, `tests/frontend/ai.test.mjs`, `docs/features/ai-app-context.md` | BUG-041 : contexte de dossier vide → dégradation gracieuse vers le prompt Général + bloc « Dossier vide » (fin du 404). BUG-042 : liens de fichiers déterministes (nom → presse-papiers, dossier → arborescence, chemin → viewer) + résolution du chemin contre l'index du vault avant ouverture. #88 : `app_context` (documents ouverts, répertoire/vault courants, recherche + résultats affichés) et fichiers récemment modifiés injectés dans le prompt Général. Vérifié : pytest 963 passed / 6 skipped, ruff 0, mypy 0, tests frontend 52/52 + validate-imports 36 modules. | 🟢 corrigé (en attente vérif utilisateur) |
---
+3 -2
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.3.0-dev | **Dernière mise à jour :** 2026-09-13
> **Version :** 2.3.0-dev | **Dernière mise à jour :** 2026-09-14
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
> vers les fonctionnalités livrées.
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
@@ -168,6 +168,7 @@
| 77 | Application Desktop native — Tauri | 🔵 en cours | [features/desktop-tauri.md](./features/desktop-tauri.md) |
| 84 | Consolidation & sécurité — revue statique 2026-09-13 (phase 1, BUG-021→034) | 2.3.0 | [archive](./archive/COMPLETED_v1-v2.md) |
| 83 | Barre d'outils d'édition mobile — ruban style Obsidian Android | 2.3.0 | [archive](./archive/COMPLETED_v1-v2.md) |
| 88 | Assistant IA — contexte applicatif (documents ouverts, répertoire, recherche, fichiers récents) & liens de fichiers fiables (BUG-041, BUG-042) | 2.3.0 | [features/ai-app-context.md](./features/ai-app-context.md) |
---
@@ -175,7 +176,7 @@
| Priorité | Items | Effort total estimé |
|---|---|---|
| ✅ Complété | #1 → #59, #61–72, #74–76, #78–82 | ~107 jours réalisés |
| ✅ Complété | #1 → #59, #61–72, #74–76, #78–84, #88 | ~107 jours réalisés |
| 🔵 P2 restant | #77 Desktop : signature de code (non retenue), 6 tests E2E **manuels** ([protocole](./DESKTOP_E2E_CHECKLIST.md)) | ~0,5-1 jour |
| ⚪ P4 restant | #73 Sync (6-8j) | 6-8 jours |
| ⚪ P0/P1 restant | #85-87 Refonte architecturale, performance, CI/CD (issues BUG-035 → BUG-040) | ~15-23 jours |
+73
View File
@@ -0,0 +1,73 @@
# #88 — Assistant IA : contexte applicatif & fiabilité des liens
> **Statut :** 🟢 corrigé / livré (en attente de vérification utilisateur)
> **Version :** 2.3.0
> **Composants :** `frontend/js/bookslm.js`, `backend/bookslm.py`, `backend/bookslm_routes.py`
> **Bugs liés :** BUG-041, BUG-042
## Contexte
Trois troubles d'usage signalés sur l'Assistant IA (BooksLM) :
1. **Répertoire vide bloquant** — ouvrir l'assistant sur un dossier ne contenant
aucun fichier markdown renvoyait `⚠ Error: Aucun fichier markdown trouvé dans ce dossier`
(HTTP 404) au lieu de répondre à la question de l'utilisateur.
2. **Assistant Général aveugle** — en mode Général, l'assistant ne savait que
lister les vaults : il ne connaissait ni les documents ouverts, ni le répertoire
courant, ni la page de résultats de recherche, ni les fichiers récemment modifiés.
3. **Liens de fichiers peu fiables** — l'assistant produisait des liens vers des
noms de fichiers qui échouaient avec `File not found: …`, sans règle claire
(nom simple, dossier, chemin complet).
## Conception
### 1. Dégradation gracieuse du dossier vide (BUG-041)
`_resolve_system_prompt` (`backend/bookslm_routes.py`) ne lève plus de 404 quand
le contexte d'un dossier est vide. Il construit le prompt Général (aide
applicative + actions), enrichi d'un bloc **« Dossier vide »**, et prévient le
modèle qu'aucun contexte documentaire n'est disponible — il répond quand même et
propose une création de fichier si pertinent.
Le prompt Général reçoit en outre les **fichiers récemment modifiés** de
l'utilisateur (`backend/services/recent.list_recent`, best-effort).
### 2. Contexte applicatif de l'assistant Général (#88)
Le frontend envoie un nouveau champ `app_context` dans les requêtes `/chat` et
`/agent` (`_buildAppContext()`), contenant l'état vivant de l'interface :
| Champ | Contenu |
|---|---|
| `open_documents` | Documents ouverts dans les onglets/panneaux (via `collectOpenDocuments()`) |
| `current_path` | Document affiché dans le viewer |
| `directory` | Répertoire courant de l'assistant |
| `vault` | Vault sélectionné |
| `search_query` / `search_total` | Dernière recherche et nombre de résultats |
| `search_results` | Fichiers affichés sur la page de résultats (max 20) |
Côté backend, `build_general_system_prompt(vaults, app_context, recent_files)`
rend un bloc **« Contexte applicatif actuel »** ajouté au prompt Général.
### 3. Liens de fichiers déterministes (BUG-042)
Classification des liens (`_classifyPath`) :
| Forme | `data-kind` | Action au clic |
|---|---|---|
| Nom de fichier seul (`readme.md`) | `name` | Copie le nom dans le presse-papiers |
| Chemin de dossier (`projets/2026`) | `dir` | Révèle le dossier dans l'arborescence |
| Chemin de fichier (`notes/a.md`) | `file` | Ouvre le fichier dans le viewer |
Avant d'agir, `_activatePath()` **résout** le chemin contre l'index du vault
(`_resolveExistingPath`) : correspondance exacte, puis suffixe
(segment de tête omis par le modèle), puis basename unique. Les chemins non
résolus copient le nom au lieu d'ouvrir un lien mort — plus de `File not found`.
## Tests
- `tests/frontend/ai.test.mjs` : classification (`name`/`file`/`dir`), routage
`_activatePath` (ouvre/révèle/copie), clic d'un lien « nom » → presse-papiers.
- `tests/test_bookslm.py` : `build_general_system_prompt` avec `app_context` +
`recent_files` ; prompt inchangé sans contexte ; chat sur dossier vide ne
renvoie plus 404.