feat(forge): assistant IA partage et plein ecran Forge/Editer (#101)
CI / lint (push) Successful in 1m33s
CI / security (push) Successful in 1m1s
CI / test (push) Successful in 2m50s
CI / build (push) Successful in 1m6s
CI / e2e (push) Successful in 10m48s

This commit is contained in:
2026-09-17 08:30:32 -04:00
parent 23a3c147cd
commit 3131277b19
20 changed files with 354 additions and 103 deletions
+1
View File
@@ -219,6 +219,7 @@ Avant de corriger quoi que ce soit, un agent IA doit :
| 2026-09-16 | BUG-052 | Correction | `backend/agent/loop.py`, `tests/test_agent_loop.py`, `CHANGELOG.md` | **BUG-052** : la boucle d'agent ne rendait plus jamais de réponse vide. `_finalize_answer` : à l'épuisement du budget d'itérations (`STOP_MAX_ITERATIONS`) ou du quota d'appels (`STOP_QUOTA_EXCEEDED`), un dernier appel LLM **sans outil** reçoit une instruction de synthèse (« N'appelle plus aucun outil. Réponds maintenant… ») et son texte devient la réponse ; si l'appel échoue ou reste vide, `_fallback_summary` compose une liste déterministe des sources (`web_search`/`fetch_url`) pour ne jamais renvoyer un tour vide. Les `tool_calls` non atteints lors d'un arrêt sur quota reçoivent un résultat `deferred` (conversation valide pour la synthèse). Vérifié : `tests/test_agent_loop.py` 16/16 (+2 : synthèse finale, repli sources), suite complète 1084 passed / 6 skipped (14 erreurs MCP préexistantes), ruff 0 (backend), mypy 0 (`backend/agent/loop.py`). | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-16 | BUG-053 | Correction | `backend/bookslm.py`, `backend/bookslm_routes.py`, `tests/test_bookslm.py`, `CHANGELOG.md` | **BUG-053** : en mode agent, le prompt Général (et dossier vide) enseignait le protocole texte `obsigate-action` ; le modèle décrivait donc l'action au lieu d'appeler l'outil natif `create_file` (bloc volumineux de surcroît tronqué avant fermeture → aucun fichier créé). Le prompt est scindé : `GENERAL_ACTION_TOOL_PROTOCOL` (appel direct des outils natifs, interdiction explicite des blocs `obsigate-action`) pour `build_general_system_prompt(agent=True)`, le protocole texte restant utilisé par le chat classique ; `_resolve_system_prompt` propage `agent` et ajoute une règle « Mode agent » aux prompts dossier/documents ; `max_tokens` de l'agent porté à 8192 pour un contenu de fichier complet. Vérifié : `tests/test_bookslm.py` 68/68 (+3 : prompt agent sans protocole texte, prompt classique inchangé, prompt système de l'endpoint agent), suite complète 1087 passed / 6 skipped (14 erreurs MCP préexistantes), ruff 0 (backend), mypy 0 (`backend/bookslm.py`, `backend/bookslm_routes.py`). | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-17 | BUG-054 | Correction | `frontend/js/utils.js`, `tests/frontend/editor-inline.test.mjs`, `CHANGELOG.md`, `docs/ISSUES_TODOLIST.md` | **BUG-054** : le bouton `#editor-save` (nœud partagé entre toutes les sessions d'édition) restait bloqué sur le spinner de chargement et désactivé — une sauvegarde manuelle (clic ou Ctrl+S) remplaçait le crochet par le loader et ne le restaurait jamais : succès (l'éditeur se ferme, la réouverture réaffichait le spinner), sauvegarde Forge, ou échec réseau (le `catch` ne restaurait ni l'icône ni l'état). Nouveau helper `resetSaveButton()` (crochet `✓`, `disabled=false`, styles en ligne nettoyés) appelé à l'ouverture (`openEditor`), à la fermeture (`closeEditor`) et en cas d'échec (`saveFile`). Vérifié : `tests/frontend/editor-inline.test.mjs` 29/29 (+4), validate-imports 38 modules / 0 erreur. | 🟢 corrigé (en attente vérif utilisateur) |
| 2026-09-17 | #101 | Feature | `frontend/editor-poc.html`, `frontend/js/sync.js`, `frontend/js/viewer.js`, `frontend/js/utils.js`, `frontend/index.html`, `frontend/style.css`, `frontend/locales/{fr,en}.json`, `tests/frontend/editor-inline.test.mjs`, `docs/features/forge-assistant.md` (nouvelle), `docs/ROADMAP.md`, `CHANGELOG.md` | **#101** : le bouton « AI Panel » de Forge ouvre désormais l'**Assistant IA** partagé (`postMessage forge-open-ai` → `bookslm.openForCurrentContext()`) au lieu du mini-chat isolé (supprimé) ; Forge lit `localStorage['obsigate_ai_picker']` (`aiPickerSelection()`) pour ses appels `/api/ai/*` et sa complétion fantôme (repli `ollama`), endpoints corrigés (`make-longer`/`make-shorter`, `target_lang`) ; bouton **plein écran** natif ajouté à Forge (iframe `allow="fullscreen"`) et à Editer (`#editor-fullscreen`, conteneur `#editor-container`, sortie à la fermeture, Échap laissé au navigateur) ; i18n `editor.fullscreen`/`editor.exit_fullscreen`. Vérifié : `tests/frontend/editor-inline.test.mjs` 40/40 (+10), unit 9/9, validate-imports 38 modules, 13 suites JSDOM vertes. | 🟢 livré (en attente vérif utilisateur) |
---
+2 -1
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.7.5 | **Dernière mise à jour :** 2026-09-17
> **Version :** 2.8.0 | **Dernière mise à jour :** 2026-09-17
> **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)**
@@ -198,6 +198,7 @@
| 98 | Assistant IA — Filtre de recherche dans la sidebar « Historique IA » | 2.5.0 | [features/ai-assistant-history.md](./features/ai-assistant-history.md) |
| 99 | Sidebar — Filtrage des vues « Récents » et « Sauvegardes » | 2.6.0 | [features/sidebar-filters.md](./features/sidebar-filters.md) |
| 100 | Assistant IA — Deep Research en pastille (au lieu du texte injecté) | 2.6.0 | [features/ai-assistant-history.md](./features/ai-assistant-history.md) |
| 101 | Forge — Assistant IA partagé (bouton AI Panel = assistant, fournisseur/modèle configuré, autocomplétion) + plein écran Forge/Editer | 2.8.0 | [features/forge-assistant.md](./features/forge-assistant.md) |
---
+86
View File
@@ -0,0 +1,86 @@
# #101 - Forge : Assistant IA partagé + plein écran (Forge & Editer)
> **Statut :** 🟢 livré (en attente de vérification utilisateur)
> **Version :** 2.8.0
> **Composants :** `frontend/editor-poc.html`, `frontend/js/sync.js`,
> `frontend/js/viewer.js`, `frontend/js/utils.js`, `frontend/index.html`,
> `frontend/style.css`, `frontend/locales/{fr,en}.json`
> **Tests :** `tests/frontend/editor-inline.test.mjs` (+10)
## Contexte
L'éditeur **Forge** (`frontend/editor-poc.html`, iframe même origine) embarque son propre
mini-panneau « AI Panel » : un chat rudimentaire qui appelait `/api/ai/improve` **sans
fournisseur ni modèle** (donc toujours le défaut serveur), sans historique, sans streaming,
sans commandes `/` ni contexte `@`. L'éditeur **Editer** (CodeMirror) n'avait aucun bouton
plein écran, et Forge non plus.
L'utilisateur veut :
1. que le bouton « AI Panel » de Forge affiche **le même contenu** que le panneau
**Assistant IA** (fournisseur/modèle, historique, skills `/`, contexte `@`) ;
2. que Forge utilise le **fournisseur et le modèle configurés** dans l'Assistant IA
(actions IA, autocomplétion fantôme) ;
3. un bouton **plein écran** dans Forge **et** dans Editer.
## Conception
### 1. Forge ouvre l'Assistant IA existant (pas de duplication)
`bookslm.js` est un singleton monté sur le document parent, couplé à `state`, `TabManager`,
`AuthManager` et au CSS global : le porter dans l'iframe serait une duplication lourde à
maintenir. Forge étant une **iframe même origine**, son bouton AI se contente de demander au
parent d'ouvrir l'assistant :
| Côté | Mécanisme |
|---|---|
| Iframe | `openAssistant()` → `parent.postMessage({ type: 'forge-open-ai' }, '*')` (`#btn-ai`, `Ctrl+J`) |
| Parent (`sync.js`) | sur `forge-open-ai` : `import('./bookslm.js')` → `openForCurrentContext()` |
Le mini-panneau Forge (`#ai-panel`, `sendAIChat`, suggestions) est **supprimé** : le contenu,
le fournisseur/modèle, l'historique, les menus `/` et `@` sont ceux de l'assistant, sans
double maintenance.
### 2. Fournisseur/modèle partagé
Le sélecteur de l'assistant persiste son choix dans `localStorage['obsigate_ai_picker']`
(`{provider, model}`), clé **partagée** par l'iframe (même origine). Forge la lit via
`aiPickerSelection()` et l'injecte :
- dans `aiCall()` (actions IA du menu `/` et de la bulle de sélection) ;
- dans la **complétion fantôme** (`/api/ai/inline-complete`) — repli `ollama` si aucun
fournisseur n'est sélectionné (comportement local conservé).
Les noms d'endpoints erronés sont corrigés au passage : `make-longer` / `make-shorter`
(au lieu de `lengthen` / `simplify`) et `target_lang` pour la traduction.
### 3. Plein écran natif
- **Forge** : bouton `#btn-fullscreen` dans la barre, `document.documentElement.requestFullscreen()`
(l'iframe reçoit `allow="fullscreen"` côté parent, `viewer.js`), icône basculée sur
`fullscreenchange`.
- **Editer** : bouton `#editor-fullscreen` dans l'en-tête ; plein écran sur le conteneur
`#editor-container` (`getEditorContainer()`, fonctionne en mode modale **et** inline),
styles `:fullscreen` (`width/height: 100vw/100vh`), icône/label basculés, sortie du plein
écran à la fermeture (`closeEditor`) et Échap laissé au navigateur pendant le plein écran.
Libellés i18n `editor.fullscreen` / `editor.exit_fullscreen` (FR + EN).
## Fichiers
| Fichier | Modification |
|---|---|
| `frontend/editor-poc.html` | suppression du mini-panneau AI ; `openAssistant()` (postMessage) ; `aiPickerSelection()` injecté dans `aiCall`/complétion fantôme ; `AI_MAP` corrigé ; bouton + logique plein écran |
| `frontend/js/sync.js` | routage `forge-open-ai` → `bookslm.openForCurrentContext()` |
| `frontend/js/viewer.js` | `allow="fullscreen"` sur `#forge-iframe` |
| `frontend/index.html` | bouton `#editor-fullscreen` (titre i18n) |
| `frontend/js/utils.js` | `toggleEditorFullscreen` / `updateFullscreenButton` / `isEditorFullscreen` ; sortie du plein écran dans `closeEditor` |
| `frontend/style.css` | `.editor-container:fullscreen` |
| `frontend/locales/{fr,en}.json` | `editor.fullscreen`, `editor.exit_fullscreen` |
## Tests
`tests/frontend/editor-inline.test.mjs` (+10) : suppression du mini-panneau, postMessage
`forge-open-ai`, routage `sync.js`, lecture de `obsigate_ai_picker`, endpoints corrigés,
complétion fantôme, boutons plein écran (Forge + Editer), `allow="fullscreen"`, CSS
`:fullscreen`, clés i18n.