# Changelog — FlowDeck ## v5.1.0 (2026-09-06) — Database Avancée : inline DB, templates prédéfinis & validation > Livre la **v5.3.0** du roadmap : databases inline dans les pages (slash `/database`), > galerie de **templates prédéfinis** (CRM, Project tracker, …) et **validation des > propriétés** (required / unique / min / max) côté serveur + UI. ### Added — Inline databases (slash `/database`) - Commande slash **`/database`** (groupe DATA) dans l'éditeur : ouvre un **sélecteur de templates**, crée une database inline liée à la page et insère un bloc `embed_type:'collection'` rendu par `FlowDeckDB.renderInto`. - Le bouton « Database » de la barre *Get Started* ouvre désormais le même sélecteur (au lieu de créer une database vide directement). ### Added — Templates de database prédéfinis - **6 templates intégrés** seedés (migration v4) : Project tracker, CRM / Contacts, Task list, Content calendar, Meeting notes, Reading list — avec icône, description et schéma de propriétés. - `app/services/db_templates.py` : `materialize_properties()` (schéma → `collection_properties`) et `create_from_template()` (collection + propriétés + vue par défaut). - `POST /db/api` et `POST /db/inline/api` acceptent `"template": ""` et matérialisent les propriétés (titre exclu — c'est le titre de ligne). Colonne `icon` ajoutée à `database_templates` (migration v4). - `apply_db_template` (`/workspace/templates/database/{id}/apply`) matérialise les propriétés. ### Added — Validation des propriétés (required / unique / min / max) - Nouvelle colonne `collection_properties.validation_json` (migration v4) ; le type/format et les règles de validation sont portés par `validate_property_rule()` dans `property_types.py` (required, unique, min, max, min_length, max_length). - **Serveur** : `create_page_api` et `update_page_api` (`/db/{c}/pages/api`, `/db/pages/{id}/api`) valident les valeurs avant écriture → 400 + message clair. La règle `unique` exclut la page en cours d'édition. Les valeurs sont résolues par id **ou** par nom. - **API propriétés** : `POST/PUT /db/{c}/properties/api` acceptent `validation` (persisté). - **UI** (`_database_table_scripts.html`) : la modale « Add property » expose Required, Unique, Min, Max ; en cas de 400 à l'édition d'une cellule, la cellule est marquée en rouge et un toast affiche le message (valeur restaurée). ### Tests - **271 tests** (+9 dans `tests/test_db_advanced.py`) : templates seedés, création de collection/inline depuis template (propriétés matérialisées), apply template, validation required/unique/min-max (create + update, exclusion de la ligne courante), persistance de la config de validation, payload collection. > Livre deux chantiers du roadmap v5 : la **migration au système de schéma versionné** > (v5.2.0, priorité n°1 — débloque tout le reste) et la **Command Palette + recherche > full-text** (v5.0.0). ### Added — Command palette (v5.0.0) - **Palette de commandes `Ctrl+K` / `Ctrl+P`** dans `base.html` : modale centrée autofocusée, navigation clavier (↑/↓/Enter/Esc), surlignage fuzzy des correspondances, recherche universelle et **quick actions** (Nouvelle page, Library, My Tasks, Trash, Settings, Help). Le bouton Search de la sidebar et `openQuickFind()` ouvrent désormais la palette (fini le toast « Quick Find — Ctrl+K »). - **Endpoint `GET /api/search?q=`** (`app/routers/search.py`) + service `app/services/search.py` : recherche unifiée des **pages** (titre + contenu) et des **databases** (nom + description), scopée aux workspaces accessibles de l'utilisateur. - **Index full-text FTS5** (`pages_fts`, migration v3) avec triggers de synchronisation (insert/update/delete) sur la table `pages` ; repli automatique sur `LIKE` si FTS5 absent. Exclusions automatiques des pages supprimées (corbeille). ### Added — Migrations versionnées (v5.2.0) - **Runner léger `app/migrations.py`** (alternative à Alembic pour ce projet SQLite) : table `schema_version` enregistrant la version appliquée ; le schéma de base existant (`init_db`) devient la **baseline v1**, et chaque changement futur est une étape versionnée appliquée une seule fois, dans l'ordre. - **Migration v2 — indexes manquants** : `users(email)`, `user_oauth_tokens(user_id, provider)`, `collections(workspace_id)`, `pages(workspace_id)`, `pages(deleted_at)`. - **Migration v3 — FTS5** : `pages_fts` + triggers `pages_fts_ai/ad/au` + backfill. - `init_db()` termine désormais par `apply_migrations(conn)` : nouveau schéma et bases existantes convergent sans rejouer les migrations déjà appliquées. ### Tests - **267 tests** (8 nouveaux dans `tests/test_search_migrations.py`) : suivi de la version `schema_version`, indexes créés, table/triggers FTS5, synchronisation de l'index, recherche pages+collections, exclusions de la corbeille, requête vide et sans résultat. ### Corrections - **Test de connexion multi-provider** : le modèle configuré globalement (ex. `deepseek-v4-flash`) n'est plus envoyé lorsqu'on teste un **autre** provider (ex. nvidia) — c'était la cause d'un HTTP 404 « model not found ». Chaque provider utilise désormais son modèle par défaut tant que le modèle global ne le concerne pas. - **Presets NVIDIA actualisés** : les anciens modèles (`nvidia/llama-3.1-70b-instruct`, `nvidia/nemotron-4-340b-instruct`) étaient retirés de la plateforme ou soumis à abonnement ; liste remplacée par des modèles disponibles (`nvidia/nemotron-3-super-120b-a12b`, `meta/llama-3.1-70b-instruct`, `deepseek-ai/deepseek-v4-pro`, `z-ai/glm-5.2`, …). > Note : un 404 persistant après cette correction vient de NVIDIA lui-même (code > « Function … : Not found for account ») — cause fréquente : le droit « Public API Endpoints » > pas activé sur le compte/clé pour le modèle choisi. ## v4.15.4 (2026-09-06) — Correctifs listes agent (@ et /) ### Corrections - **Menus « @ » vides** : le template Alpine du menu de mentions avait deux racines (Alpine n'en accepte qu'une) — seuls les titres de sections s'affichaient, sans éléments. Les éléments sont maintenant bien rendus sous chaque section. - **Sélection clavier visible dans le menu « / »** : le changement de focus (↑/↓) n'était pas visible faute de style `.focus` ; ajout d'un surlignage (fond + barre d'accent à gauche). ## v4.15.3 (2026-09-06) — Agent : mentions @ par espace courant + skills composés en arrière-plan > La commande « @ » propose en priorité les **fichiers de l'espace de travail ouvert**, puis les > autres contextes (collections, pages de collections, autres documents). La navigation clavier > (↑/↓) positionne maintenant visuellement la sélection dans la fenêtre (défilement), et les > fichiers identiques ne sont plus affichés plusieurs fois (déduplication par titre). ### Améliorations - **Mentions @ contextuelles** : nouvelle API `/api/agent/mentions` par **sections** : *Fichiers de l'espace courant* (« `workspace_id` transmis par le frontend »), *Collections / bases*, *Pages de collections*, *Autres documents*. - **Navigation clavier corrigée** : ↑/↓ font défiler la liste ouverte pour garder la sélection visible à l'écran (aussi appliqué au menu `/`). Entrée/Tab sélectionne toujours l'élément focalisé. - **Déduplication** : deux fichiers du même type et même titre (ex. « Sans titre », « Aussi Vide ») n'apparaissent qu'une seule fois dans les propositions. - **Skills composés en arrière-plan** : sélectionner un skill n'injecte plus son texte dans la boîte d'édition. À l'envoi la requête est assemblée : `contexte1 + contexte2 + skill-1 + skill-2 + texte`. Les skills intégrés (templace) et enregistrés (skill_ids) sont tous deux pris en compte ; les contextes @ continuent d'être résolus en contenu côté serveur. ## v4.15.2 (2026-09-06) — Agent : skills multi-épinglés en bulles + chips de contexte cliquables > Les skills s'épinglent désormais au contexte **comme les mentions @** (bulles « / » purples dans > l'éditeur du chat), on peut en associer **plusieurs** à un même post, et chaque bulle de contexte > référencée s'ouvre d'un clic comme document courant. ### Améliorations - **Skills en bulles** : sélectionner un skill (menu `/`, intégré *ou* enregistré) ajoute une vraie bulle dans le composer, comportement identique aux mentions `@`. La bulle d'un skill intégré greffe son modèle de demande dans le texte ; plusieurs skills peuvent coexister. - **Plusieurs skills par post** : l'API `/run` accepte `skill_ids` (tableau) et l'engine injecte tous leurs prompts dans les instructions système (`skill_id` reste supporté pour la rétro-compatibilité). - **Chips de contexte cliquables** : cliquer sur une bulle de contexte (`@ document / page / base`) ouvre l'élément comme document courant — `document:`/`page:` → `/pages/`, `collection:` → `/db/`. Le bouton « ✕ » retire toujours la bulle (sans ouvrir). - Envoi possible avec uniquement des skills (sans texte) : « Applique le(s) skill(s) sélectionné(s)… ». ### Correctifs - L'écriture du chat (`send`) renvoie désormais tous les `skill_id` épinglés via `skill_ids`. ## v4.15.1 (2026-09-06) — Agent : réparation des conversations réelles (protocole tool_calls) > Le chatbot ne « répondait » plus : avec un fournisseur réel, dès qu'un **outil** était appelé la passe > suivante était refusée par l'API (message assistant sans ses `tool_calls`) et l'agent retombait > silencieusement sur le **mock hors-ligne**, qui se contentait de répéter la question. ### Correctifs - **Protocole tool-calling conforme** (`agent_engine.py`) : le message assistant qui annonce un appel d'outil transporte désormais ses `tool_calls` (avec `id`), et chaque résultat d'outil répond avec le `tool_call_id` correspondant (y compris en cas de refus/permission). Le run se poursuit alors correctement et se termine par une vraie réponse du modèle. - **Plus de repli silencieux** (`llm_client.py`) : quand un fournisseur **réel** configuré échoue, l'erreur remonte (événement SSE `error`) au lieu de produire silencieusement un écho hors-ligne. Le mock n'est utilisé que si le fournisseur est réellement non configuré (`offline` / pas de clé). - Les `tool_calls` analysés conservent leur `id` et les arguments bruts (`arguments_raw`). - Test de régression `test_engine_tool_protocol_messages`. ## v4.15.0 (2026-09-06) — Agent : actions sous chaque réponse, mentions @ & skills / > Le panneau FlowDeck Agent gagne l'expérience « type Notion AI » : chaque réponse de l'agent expose > des actions rapides (copier, insérer dans la page, feedback 👍/👎), on peut **épingler au contexte** > plusieurs documents / pages / bases via la commande **@** (ou le bouton **+**), et la commande **/** > propose des **skills** de travail réutilisables en plus des commandes admin existantes. ### Sous chaque réponse de l'agent - **⧉ / ➕ / 👍 / 👎** : boutons **à icônes seules** (infobulle au survol) — copier la réponse, insérer dans la page ouverte, feedback 👍/👎 enregistré en base. - Le bandeau statique « Contexte : … » du haut du panneau est supprimé. ### Contexte épinglé — commande « @ » / bouton « + » (style Notion AI) - Le composeur est maintenant un éditeur **contenteditable** : les références sélectionnées s'insèrent **inline** comme des pastilles cliquables (icône + nom) directement dans le message. - Au-dessus du composeur, une **barre de contexte groupée** rassemble les fichiers épinglés (rangée documents, icônes 📄) et les **skills** (rangée séparée, icône ✨), chacun retirable. - **@** ouvre le sélecteur (icône + titre + chemin) listant documents, pages et bases ; l'élément choisi devient un jeton inline **et** une puce de contexte. **+** épingle un élément au contexte sans l'insérer inline. Les puces persistent jusqu'au retrait (et sont vidées en nouveau chat). - **Contexte d'ouverture** : ouvrir l'agent sur un document (`/pages/:id`) ou une base (`/db/:id`) ajoute automatiquement ce contexte comme **mention** (puce épinglée) — plus de bandeau statique. - À l'envoi, chaque référence est transmise comme **mention** ; le backend résout son **contenu réel**. ### Skills — commande « / » - Le menu **/** liste les **skills intégrés** (research, resume, brainstorm, plan, meeting-note, livrable…), les **skills enregistrés** (table `agent_skills`) et les commandes admin classiques. - Un skill intégré pré-remplit le composeur d'un modèle à compléter ; un skill en base s'épingle en chip et est appliqué via `skill_id` à chaque envoi. - Navigation clavier (↑/↓/Entrée/Échap) dans les menus ; aucun envoi parasite pour « / » seul. ### Infra & API - Nouvelle table `agent_feedback` (rating, conversation/message, extrait, commentaire). - `GET /api/agent/mentions?q=` : recherche des éléments mentionnables du workspace. - `POST /api/agent/feedback` : enregistre un retour 👍/👎. - `VERSION` → 4.15.0 ; `app/main.py` version → 4.15.0. ## v4.14.0 (2026-09-06) — Agent : création de documents dans un espace + nouveaux outils & titre auto > L'agent sait enfin **créer, lire, modifier et supprimer des documents** (pages éditeur) **dans un > espace de travail** — la demande « crée un document « Projet » dans le workspace « TEST WS 1 » » > ne se termine plus par un simple `search_workspace` vide. Il liste aussi les espaces, et **renomme > automatiquement les conversations** avec un titre descriptif au lieu de « Nouvelle conversation ». ### Outils (nouveaux / corrigés) - **`create_document`** : crée une page éditeur (`pages`) dans un espace (par `workspace_id` ou `workspace_name`), avec contenu Markdown optionnel — snapshot d'undo. - **`read_document`** : lit un document (titre + contenu + métadonnées). - **`read_workspaces`** : liste les espaces accessibles (rôle, nb de documents/collections). - **`delete_document`** : suppression → corbeille (`deleted_at`), annulable (`softdelete`). - **`write_blocks`** étendu : renommage de titre et/ou écriture des blocs ; undo restaure titre + contenu. - **`search_workspace`** étendu : recherche désormais aussi les **documents** et les **espaces de travail**. ### Contexte & planification - Contexte de l'agent enrichi : sections « Documents » et « Espaces de travail » (avec ids) pour que le LLM cible le bon espace. Guide mis à jour. Mock hors-ligne : détection « créer un document dans un espace » / « lister les espaces ». ### Conversations - Auto-titre : après la première exécution d'une conversation encore nommée « Nouvelle conversation », l'agent génère un titre descriptif (déterministe, hors-ligne sûr) ; l'historique devient navigable. ### UX - Indication claire dans le composeur du panneau : **Entrée ↵ = envoyer · Maj+Entrée = nouvelle ligne** (le comportement existait déjà, il est maintenant visible + placeholder mis à jour). ### Infra - `VERSION` → 4.14.0 ; `app/main.py` version → 4.14.0. ## v4.13.2 (2026-09-06) — Saisie libre « écrire un texte » → proposition à appliquer au document > Quand un **document est ouvert** et qu'on tape directement une demande de **rédaction/contenu** > dans le chat (« Écrire un texte sur… », « Rédige… », « Résume… », « Traduis… »), la requête est > maintenant routée vers la **génération de texte** (sans outils) et affichée comme **proposition > à appliquer ou rejeter** — au lieu d'un `search_workspace` puis d'un simple « Objectif traité ». > « Traduire » remplace le contenu de la page ; les autres demandes l'insèrent en fin de page. ### Fix - Envoi libre avec contexte document : détection d'intention de contenu (écrire/rédiger/générer/ résumer/traduire/améliorer/note de réunion/texte sur/série…) → `POST /api/agent/generate` + aperçu « ✓ Appliquer au document / ✕ Rejeter ». - Mock : « écrire » (infinitif) reconnu comme verbe de rédaction. ### Infra - `VERSION` → 4.13.2 ; `app/main.py` version → 4.13.2. ## v4.13.1 (2026-09-06) — Corrections actions contenu Agent & aperçu à approuver > Corrige les actions de contenu sur document (« Résumer », « Traduire cette page », « Proposer > des améliorations », « Créer une note de réunion ») qui partaient en recherche (`search_workspace`) > sans retour utile, et ajoute un flux **aperçu des changements → Appliquer / Rejeter**. ### Fix - **Le contenu du document n'est plus interprété comme des intentions d'outil** : le planificateur (mock) n'analyse que l'objectif de l'utilisateur, jamais le texte injecté dans `# Contexte`. Un document contenant « recherche », « collection », etc. ne déclenche plus `search_workspace` / `create_collection` intempestifs. - **Ask AI** n'emprunte plus le canal outil : il utilise la génération de texte directe. ### Added - **Endpoint `POST /api/agent/generate`** : génération de texte **sans outils** (l'IA répond à partir du contexte du document fourni, sans chercher/créer dans le workspace). - **Aperçu & approbation** : les actions de contenu sur document affichent la **proposition** de l'agent dans le panneau avec boutons **« ✓ Appliquer au document » / « ✕ Rejeter »**. Appliquer insère la proposition (mode `insert`) ou **remplace** le contenu de la page (mode `replace` pour « Traduire cette page ») via `window.fdApplyDocument()` ; rejeter la conserve sans modification. En mode hors-ligne, l'action explique que la modification nécessite un modèle connecté (pas d'application accidentelle). ### Infra - `VERSION` → 4.13.1 ; `app/main.py` version → 4.13.1. ## v4.13.0 (2026-09-06) — Agent : panneau « Notion AI », ouverture fiable (Ctrl+J) & contexte universel > Refonte complète du panneau Agent (style Notion AI) : ouverture/fermeture **fiable** (API > `window.fdAgent.open/close/toggle`, bouton flottant + sidebar « Agents » + raccourci `Ctrl/Cmd+J`), > **contexte détecté et épinglé à l'ouverture** (document en cours, bibliothèque, My Tasks, > espaces, base de données, board, corbeille…) et sélecteur **fournisseur + modèle** intégré à la > zone de saisie. ### Added - **Ouverture robuste** : `window.fdAgent.open()/close()/toggle()` appliquent l'état directement (classe `.open` + attribut) ; FAB, entrée « Agents » du sidebar, icône Chat et `fdAgent.ask` ouvrent explicitement le panneau (fini le toggle silencieux). Raccourci **`Ctrl/Cmd+J`** global. - **Contexte universel `window.FlowDeckCtx`** : détection par priorité — (1) marqueur d'écran (`window.fdViewContext` / `data-fd-context`), (2) **document ouvert** (éditeur : titre + contenu des blocs), (3) **route** de l'application (Accueil, Bibliothèque, Corbeille, My Tasks, Espaces, Espace local/Gitea, Base `/db`, Board, Notes, Paramètres…). Le contexte est **épinglé à l'ouverture** du panneau, affiché en chip (« Contexte : … ») et **rafraîchi** automatiquement si l'utilisateur ouvre un autre document avant d'envoyer. - **Fournisseur & modèle affichés** : sous-titre du panneau `Provider · Modèle` (ex. `OpenAI · gpt-4o`) dès qu'un modèle réel fonctionnel est sélectionné ; « hors-ligne (mock) » n'apparaît que lorsqu'aucun fournisseur actif n'existe. Sélecteurs provider → modèle regroupés dans une pastille « Auto · » au-dessus de la zone de saisie. - **Actions rapides contextuelles** (état vide) en français selon la section : Résumer / Traduire / Améliorer un document, Créer une note de réunion, Analyser une base, Mes tâches / planifier, ou guides « Comment utiliser FlowDeck ? » et création de collection/board. ### Changed - **Panneau redessiné façon Notion AI** : largeur 470 px, en-tête « How can I help you today? », icône avatar, ligne de contexte, historique, grande `textarea` arrondie auto-redimensionnable (jusqu'à ~220 px) avec toolbar (`+`-équivalent, contexte, modèle, flèche d'envoi) et sélection de modèle en popover. ### Infra - `VERSION` → 4.13.0 ; `app/main.py` version → 4.13.0. ## v4.12.1 (2026-09-05) — Corrections configuration Agent & IA + AI Meeting Note > Correctifs UI sur la section **Settings → Agent & IA** et remplacement de l'ancien bouton > « AI meeting note » (qui insérait du texte) par une vraie **boîte de réunion interactive**. ### Fix - **Champs « Clé API »** : le `x-text` embarquait le SVG de l'icône œil dans l'attribut HTML, ce qui cassait l'attribut et laissait du texte résiduel (`sk-…': '👁'" style="top:50%;">`). Les boutons d'affichage/masquage utilisent désormais deux `` avec l'icône en contenu. - **Champs « URL API »** : pré-remplis avec l'URL réellement utilisée par l'application (base par défaut du fournisseur, ex. `https://api.openai.com/v1`) quand aucune URL personnalisée n'est stockée — exposée via `base_url` dans `/api/agent/providers` (fournisseur par défaut + au changement de provider). ### Changed - **AI Meeting Note** : le bouton « Get started » ne génère plus du texte via l'agent. Il crée un bloc **`meeting`** interactif dans la page (page renommée « Meeting @date » si vide) : - Étape 1 — consentement (moi-même / message audio auto) + « Start transcribing » ; - Étape 2 — transcription **live** via Web Speech API (fr-FR, horodatée), boutons Pause / Stop, fallback « sans audio » (transcript à saisir/coller) si le navigateur ne supporte pas ; - Étape 3 — onglets Notes / Transcript / Summary, bouton « Générer le résumé » (helper headless `window.fdAgent.generate` sur le FlowDeck Agent) produisant Résumé / Décisions / Action items, copie presse-papiers et « Nouvelle transcription ». - Persistance du bloc et de son état dans la page (champ `meeting` ajouté aux blocs sauvegardés). ### Infra - `VERSION` → 4.12.1 ; `app/main.py` version → 4.12.1. ## v4.12.0 (2026-09-05) — Agent & IA : refonte des réglages, fournisseurs activés/fonctionnels dans le panneau, contexte document & guide > Refonte de la section **Settings → Agent & IA** pour la rendre plus claire, et aligne le panneau > Agent sur la réalité de la configuration : **seuls les fournisseurs configurés ET dont le test de > connexion a réussi** sont proposés (avant, tous les fournisseurs connus apparaissaient, même non > configurés). Le panneau sait aussi sur quel **document** l'utilisateur travaille et peut guider > sur l'utilisation de l'application quand aucun document n'est ouvert. ### Added - **Vérification des fournisseurs** : colonnes `verified` / `verified_at` / `last_error` sur `user_llm_keys` et `llm_config` (migration automatique au démarrage). Un fournisseur est « fonctionnel » après un test de connexion réussi ou un chargement de modèles réussi. - **Endpoint non-admin** `POST /api/agent/keys/{provider}/test` : teste la clé enregistrée de l'utilisateur (sans mock) et met à jour l'état `verified` ; l'endpoint admin `/providers/test` marque le fournisseur par défaut de l'espace. - **`/api/agent/providers` enrichi** : chaque provider expose `configured`, `verified`, `functional`, `last_error` et `source` (user/global/open) ; `default_provider` + `default_verified` en tête de réponse. - **Panneau Agent** : les listes fournisseur/modèle ne contiennent plus que les providers **activés et fonctionnels** (+ mode hors-ligne). Bandeau « Aucun fournisseur activé » avec lien vers les réglages et rappel des clés « à tester » le cas échéant. - **Contexte document** : le panneau joint automatiquement la page ouverte (titre + contenu des blocs) à chaque requête — l'agent peut interagir avec le document courant. - **Guide d'utilisation** : le prompt système intègre un guide FlowDeck concis pour répondre aux questions « comment faire… ? » (pages, collections/vues, Gitea, corbeille, réglages…) lorsqu'aucun document n'est fourni. ### Changed - **Settings → Agent & IA refondu** : carte de statut (prêt / N fournisseurs actifs / mode hors-ligne), cartes de fournisseurs avec badges d'état, erreur du dernier test, boutons « Enregistrer & tester », « Tester », « Modèles », « Supprimer » et encart d'aide. - Le bouton **Save configuration** admin gagne un rappel « Tester la connexion » pour activer le fournisseur pour tous ; changer de clé réinitialise la vérification. ### Fix - Saisie clé : une clé vide conserve désormais l'existante aussi pour la config globale (au lieu de l'effacer), et un changement de clé invalide l'ancien statut `verified`. ### Infra - `VERSION` → 4.12.0 ; `app/main.py` version → 4.12.0. ## v4.11.1 (2026-09-05) — Correction & intégration des points d'entrée IA > Aligne les entrées AI de l'interface sur le FlowDeck Agent (alignement Guide Notion AI) et corrige > les trois boutons qui ne fonctionnaient pas : **Ask AI**, **AI meeting note** (toolbar « Get > started » de l'éditeur de page) et le **bouton rond flottant FlowDeck Agent** (coin bas-droit). ### Fix - **Bouton flottant Agent 🤖 (coin bas-droit)** : le chat restait inerte tant qu'aucune conversation n'existait (`send()` retournait silencieusement sans `currentConv`). Ajout d'une création de conversation **lazy** (`ensureConversation()`) effectuée avant chaque envoi — la 1ère saisie crée la conversation puis l'exécute (mode offline ou LLM réel). - **Mode hors-ligne plus utile** : le mock LLM (`_draft_reply`) génère désormais un **vrai contenu structuré** pour les demandes de rédaction de page (« Ask AI », titre détecté via « intitulée ») et les **AI meeting notes** (squelette Résumé / Décisions / Action items), au lieu de se contenter de renvoyer le prompt tel quel — les boutons produisent donc du contenu même sans clé API. - **« Ask AI » & « AI meeting note »** : ne faisaient qu'insérer du texte placeholder (« Ask AI to write... » / « AI Meeting Note ») sans aucun appel IA. Ils déclenchent désormais une **génération inline réelle** via le FlowDeck Agent : - méthodes `askAI()` / `meetingNote()` / `aiRun()` / `applyAIBlocks()` sur l'éditeur de page ; - un prompt contextuel est envoyé au panneau agent (exposition d'`window.fdAgent.ask`) ; - la réponse (`event final` du flux SSE) est **insérée automatiquement dans la page** sous forme de blocs édités (hook `window.__fdInsertAI`). - Un état `aiBusy` évite les doubles envois pendant la génération. ### Infra - `VERSION` → 4.11.1. ## v4.11.0 (2026-09-05) — Agent IA : clés API par utilisateur & chargement dynamique des modèles > Suite de v4.10.1 : migre la config LLM d'un **global admin** vers des **credentials par > utilisateur**. Chacun peut enregistrer plusieurs provider + clé API dans son profil, les modèles > disponibles sont **chargés automatiquement depuis le fournisseur**, et le provider/modèle actif > d'une conversation se pilote **directement dans le chat** via des commandes slash. ### Added - **Clés par utilisateur** : table `user_llm_keys` (UNIQUE(user_id, provider)) + service `app/services/llm_config.py` : `get_user_llm_key`, `list_user_llm_keys` (masqué), `upsert_user_llm_key`, `delete_user_llm_key`. Une clé vide conserve l'existante. - **Chargement dynamique des modèles** : `fetch_provider_models()` (httpx) — GET `{base}/models` pour OpenAI-compatible (Bearer), headers `x-api-key`/`anthropic-version` (Anthropic), `x-goog-api-key` (Gemini) ; stockés dans `models_json` et fusionnés dans `GET /providers`. - **Endpoints API** (tout utilisateur authentifié) : - `GET /api/agent/keys`, `PUT/DELETE /api/agent/keys/{provider}`, - `POST /api/agent/keys/{provider}/models` (fallback sur la clé stockée, pas de fuite de clé), - `PATCH /api/agent/conversations/{id}` (title / provider / model — commande slash). - **`POST /run`** : utilise la clé API de l'utilisateur pour le provider de la conversation (sinon config globale / `.env` / offline mock). - **UI Settings → Agent & IA** (visible pour tous, plus seulement admin) : carte par provider (clé API masquée, URL API, « Charger les modèles » en live, modèle par défaut, Enregistrer / Supprimer). La config globale reste admin sous « Configuration globale ». - **Commandes slash dans le panel agent** : `/help`, `/providers`, `/keys`, `/provider `, `/model ` — menu de suggestions live, appliquées via `PATCH` et sans appel LLM. ### Infra - `VERSION` → 4.11.0 ; migration idempotente (création table `user_llm_keys` + index). ### Tests - **239 tests verts** (+7 : CRUD clés + masquage des secrets, isolation par utilisateur, provider invalide, fetch modèles (clé stockée / body gagnant / erreur), PATCH conversation, `/run` utilise la clé de l'utilisateur). > Comble le trou UX de v4.10.0 : l'utilisateur pouvait discuter avec l'agent mais **ne pouvait > configurer ni provider ni modèle**. Ajout d'un sélecteur provider/modèle dans le panneau, d'une > config runtime persistée en base (table `llm_config`) et d'un écran admin avec test de connexion. ### Added - **Sélecteur provider/modèle dans le panneau agent** (`agent_panel.html`) : deux `