# Changelog - Lyra ## [Unreleased] ## [1.4.0] - 2026-11-06 ### Breaking - `scripts/async_mcp_wrapper.py` no longer falls back to `~/dev/fedora-setup/scripts`: background MCP scripts are resolved only from the root-owned copy (`/usr/local/lib/lyra/scripts`, `LYRA_SCRIPTS_DIR` or `paths.scripts`). The old path was user-writable and covered by no sudoers rule; run the installer (or set `LYRA_SCRIPTS_DIR`) if you relied on it. ### Added - Releases: `sbom.json` (CycloneDX inventory of the base and `gpu` dependencies, exact versions from `uv.lock`) attached to each GitHub release; wheel and sdist carry a SLSA provenance attestation and an SBOM attestation, both verifiable with `gh attestation verify`. The release refuses to build when `uv.lock` is stale. Procedure in `docs/dev/release-process.md`. ### Security - Background tasks: their `.log`, `.pid`, `.progress` and `.done` files move from `/tmp` (predictable names, readable by every account) to a private 0700 directory, `$XDG_RUNTIME_DIR/lyra/tasks` (`LYRA_TASK_DIR` to override). The Discord webhook reaches the task wrapper through the environment, no longer as a command-line argument visible to every account in the process list. - Daemon socket: created with mode 0600 directly (umask around `bind`) instead of being chmod-ed right after, and `~/.lyra` is created 0700. - The MCP `destructiveHint` annotation now requires a strict confirmation in the daemon and in standalone mode, like `DANGEROUS_TOOLS`. A third-party server that marked a tool destructive only got the soft `[O/n]` prompt (Enter = yes), and `-y` ran it without asking: the annotation was read by `Pipeline.is_dangerous_action`, which nothing called. Bundled servers are unaffected (fedora-agents annotates exactly the tools already listed). - Graphical installer (`install.sh --app`, 127.0.0.1:9877): a web page open in the browser could start the install and answer its confirmations (no Host, Origin or Content-Type check; a `text/plain` POST needs no CORS preflight; question ids were `ask-1`, `ask-2`...), and `options.ollama_host` was written unquoted into `~/.bashrc`, so a crafted value ran shell code at the next login. The backend now refuses a non-local Host (DNS rebinding), a foreign Origin and any non-JSON POST; question ids are random; `ollama_host` must be a host name or an IP (all frontends: app, TUI, headless) and is quoted in `.bashrc`. ### Changed - CI: ruff lint is now blocking (240 findings cleared: 139 were the documented `from manim import *` of the intro animations, now a per-file ignore; `main.py` keeps its CUDA-before-imports order under a justified E402 ignore). The test job also runs `scenes/ironman`. ### Fixed - Discord notifications: the webhook is read from `config.yaml`, then from the neighbouring `secrets.yaml` where `config.yaml.example` and the data-flow doc put it; only `config.yaml` was read, so a webhook stored as documented sent nothing (daemon and standalone mode). - `scripts/async_mcp_wrapper.py` ran `sudo -n` on scripts under `~/dev/fedora-setup/scripts`: no sudoers rule covers that user-writable path, so system clone, backup creation and backup restore failed when run in the background. It now uses the root-owned copy resolved by `lyra.core.paths` (`/usr/local/lib/lyra/scripts`, `LYRA_SCRIPTS_DIR`, `paths.scripts`). - `main_rag.py`: the module docstring sat after `from __future__ import annotations`, so it was not the docstring (and flagged every import); it is first again. - Bare `except:` in ephaistos and the MCP client no longer swallow Ctrl+C. - Tests: the Iron Man red-flash test now checks the colour sent to the lamp (it computed it without asserting); the phase 0 timing test mocks the parallel state reads added with the refactor (it made real requests and measured their 2 s timeout). - Dead code removed: an unused Zod pattern in the indexer, an unused score colour in the step display. ## [1.3.1] - 2026-09-24 ### Added - Daemon: `tool` message on the socket for clients that only need one MCP call (name, arguments, `confirmed` option). The daemon checks the HESTIA catalogue, asks for confirmation when the tool is dangerous and not confirmed, runs it under the busy lock and streams `tool_result` then `result`. No model in the loop; used by neutroncore (roadmap issue 73). ### Changed - MCP_TOOLS.md regenerated: `tv.get_state` now reports volume, mute and ambilight; `denon.get_status` reports mute, source and reachability (pylips-mcp 0.2.0, denon-mcp 0.2.0). - First release built and published by the shared release workflow (PyPI Trusted Publishing); 1.3.0 was tagged before the workflow existed. ## [1.3.0] - 2026-09-24 ### Added - `lyra --version` prints the installed `lyra-assistant` version, or the pyproject version when run from sources (#15). - `scripts/gen_mcp_tools_md.py` regenerates docs/user/MCP_TOOLS.md from the live MCP servers, with a confirmation column derived from `lyra/core/constants.py`. - EPHAISTOS variants `inventaire_vm` (real VM names from vm_status or `LYRA_VMS`), `lexique_courant` and `verbes_courants` (everyday French synonyms and verbs, written outside any test set). - Fourth held-out set `tests/cases_hors_regles_3.py` (50 phrasings, real machine names, ten tools never measured before); fifth set `tests/cases_hors_regles_4.py`, sealed before iteration 16. - `scripts/bench_recall.py`: mechanical recall of a set (rank of the expected tool, precision of the "net" criterion) without calling the model, the measurement to run before any bench. - EPHAISTOS variants `outil_par_machine`, `mots_courants_2`, `nombres_tri`, `mots_courants_3` (iterations 15-16); `net_assoupli` measured and refuted. - Iterations 17-21 ("99 % on every set"): variants `cartes_17`, `lumiere_sans_verbe`, `nom_de_vm` (replayed), `force_definitif`; the five development sets reach 21/21, 51/51, 100/100, 50/50, 50/50 with qwen2.5-coder:0.5b; a sixth set (`tests/cases_hors_regles_5.py`) sealed first and measured once. ### Changed - EPHAISTOS: the 17 refuted or neutral variants are removed from the code (module 1581 -> ~810 lines); the 42 retained ones are the default configuration, still switchable one by one through `LYRA_EXP` for ablation. Word tables and lexicons move to `lyra/models/cartes.py` (data only). Mechanical recall and the benches are identical before and after (21/21, 51/51, 100/100). - `docs/articles/2026-09-boucle-ephaistos.fr.md`: the improvement loop written up as a research report (abstract, method, results, limits, reproduction), with five SVG figures generated from `benchmarks/results/` by `scripts/gen_figures_boucle.py`. ### Fixed - **The daemon did not run the path the bench measured.** `EnhancedPipeline` sent EPHAISTOS a single spec, without the `tool_name:` prefix, then let a session-context injection and the intent classifier override the result: "reveille fedora-base" gave `tv.power_on`, "archive test-vm" gave `tv.mute`, "de la lumiere dans le salon" was answered as small talk (manual acceptance test, 2026-09-23). The pipeline now builds the specs exactly like the bench (`specs_pour_ephaistos`, shared), passes all of them, skips the context injection when the variants are active, and treats a query as a command when the spec sort is unambiguous (`Ephaistos.est_net`). `tests/test_campaign_llm.py --reel` measures this real path (one session per case, correct refusals of unknown VMs counted apart): 21/21, 43/43 and 91/92 on the three development sets. - One-shot calls: a pending clarification or choice left in the shared session polluted the next call ("mets staging-03 au repos" was read as an answer about preprod-10). It is cleared when nobody can answer. - Argument names invented by the 0.5b in `missing_args` ("chromecast") no longer trigger a clarification; only real parameters of the tool count. - Hue: a `group_id` that does not exist on the bridge is replaced by the default group (real groups read at startup). - Performance mode (`-p`) and `-y`: LYRA printed "Tu confirmes ?" then executed without waiting; it now says what it does. - Slang normalizer: URLs are no longer lowercased (a YouTube id is case-sensitive). - A matching rule, or a spec sort that recognises a tool word, is a command whatever the intent classifier says ("silence sur l'ampli" and "les leds de la tele, tu me les enleves" got no action); a tool imposed by a net sort no longer triggers the disambiguation question. - Rules: "affiche les taches" lists the running tasks; the Denon rule also answers to "ampli", "AVR" and "home cinema" ("l'ampli sur game"), a state question gives `get_status`, "bascule ... sur le bluray" is a source change. - Security: `main_rag.py` compared a prefixed tool name (`fedora.vm_destroy`) to the short names of `DANGEROUS_TOOLS`, so `-y` skipped the confirmation of destructive tools; it now uses `is_dangerous_tool()`. - Security: an empty answer (Enter) no longer confirms a sensitive or destructive action in the daemon UI and the terminal UI; `o`/`oui` is required, as in `actions.py`. - Security: `vm_snapshot` (revert/delete), `vm_import` and `vm_copy` are now in `DANGEROUS_TOOLS`; `PERFORMANCE_TOOLS` entries are prefixed (the eight `cast_*` never matched), Denon and read-only tools added; `main.py` (legacy) no longer keeps its own diverging lists. - Rules: `tv.sound_only` and `tv.ambilight_color` do not exist in pylips-mcp; "son seul / mode musique" now maps to `tv.screen_off`, a colour request to `ambilight_mode(manual)` (pylips-mcp#7); `tv.youtube_video` sends `video` (schema name) instead of `video_id`. - Rules: "lance CMD sur VM" is an exec, "l'etat de la lampe" is not a VM status, "il est tard" is not a state question (found by the fourth set control). - `scripts/index_rag_3tier.py` ignores documents without `server_name` (a local `ironman.run_scene` would have created an UNKNOWN server); `config.yaml.example` declares the catt server. - EPHAISTOS: a tool imposed by a net sort was overridden by the second pass (`double_passe`) and applied after the argument completion; the net criterion ignored the machine bonus. Three fixes with unit tests. - Rules: "eteins les leds de la tele" was a power_off (sixth set control). - Config: the dead `security.destructive_tools` list (never read, listed a non-existent `vm_delete`) is removed from config.yaml.example; `lyra/core/constants.py` is the only source. `docs/dev/INDEX_RAG.md` documents the v2 -> v3 regeneration order. - Rules: the screen-manager module is out of the registry (server absent from disk and config, lyra#24); a room name is not a VM; "arrete la synchro des lampes" is not a task kill; a question about the chromecast is `cast_status`; "l'ambilight qui suit la musique" sets the mode. `tracking.delete` and `tracking.kill_task` require an explicit confirmation. ## [1.2.0] - 2026-09-10 ### Added - docs/user/DATA_FLOWS.md: what Lyra captures, keeps, and the optional network outputs with their switches. - VOICES.md and docs/licenses/: Piper voice licences, Llama 3.2 Community License and use policy ("Built with Llama"). - THIRD_PARTY_LICENSES.md generated by `make licenses` and checked in CI. - README: English summary, prerequisites table, security guarantees mapped to code and tests, clickable metrics, ecosystem block. - ROADMAP.md. ### Changed - Packaging: hatchling, distribution name `lyra-assistant`, dependencies in pyproject with `gpu`/`dev` extras and a uv lockfile; CUDA libraries are no longer imposed. - CI: reusable workflow (unit + installer tests on 3.11/3.12, ruff, personal-path guard), Dependabot. - systemd units shipped with `@LYRA_DIR@` / `@HOME@` placeholders; no personal path left in code, scripts or tests. - Tracking client defaults come from `TRACKING_SERVER_SCRIPT` / `TRACKING_VENV_PYTHON`; RAG spec sources from `LYRA_MCP_SPECS_DIR`. ## [1.1.0] - 2026-09-09 ### Changed - License: AGPL-3.0-or-later with a commercial license option and a CLA (versions up to 1.0.0 remain MIT). - README: HTTPS clone URL, status badges, dated claims, no more reference to a web domain. - CLAUDE.md condensed to what an agent needs. ### Removed - Generated artifacts (empty ChromaDB databases, test reports, Manim caches), the self-referencing `lyra/run.sh` symlink that broke CI, and workshop files (Claude Code prompts, session notes, n8n exports, divergent documentation copy), now archived privately. ### Fixed - CI: the pip cache step no longer fails on a symlink loop. ## [v1.1.0 - Pipeline modulaire complet] - 2026-03-10 ### Refactoring - Decomposition de pipeline.py en modules separes (7 missions) `pipeline.py` passe de 2899 lignes a 919 lignes (~68% de reduction). 164/164 tests MCP conserves a 100% apres chaque mission. **Mission 1 - Types et constantes (`core/types.py`, `core/config.py`)** - Extraction de `QueryType`, `PipelineResult`, constantes MCP (`CATEGORY_KEYWORDS`, `TOOL_TYPE_KEYWORDS`, etc.) **Mission 2 - Retrievers (`core/retrieval.py`)** - Classe `Retriever` : encapsule SemanticRetriever + KeywordRetriever + RRFFusion - API : `initialize()`, `retrieve(query)`, `detect_query_type(query)` - Pipeline utilise `self._retriever` au lieu de 3 composants separes **Mission 3 - Menus outils (`core/menus.py`)** - `is_list_tools_query()`, `process_tools_query_step1()`, `handle_server_selection()` - Constantes `LIST_VERBS`, `LIST_SUBJECTS` **Mission 4 - Workflows base (`core/workflows/`)** - `context.py` : `WorkflowContext` avec callables injectes (`prepare_execution`, `route_query`) - `vm_start.py` : `handle_vm_start_confirm()` (4 cas oui/non/clarification/autre) - `vm_stop.py` : `handle_vm_stop_choice()` (choix 1/2/3 arret VM avant clone) - `vm_snapshot.py` : `handle_vm_snapshot_create_workflow()`, `handle_vm_snapshot_list_workflow()`, `handle_vm_snapshot_pending()` **Mission 5 - Workflows clone/export (`core/workflows/vm_clone.py`, `vm_export.py`)** - `vm_clone.py` : `suggest_vm_name()`, `handle_cow_choice()`, `handle_vm_clone_workflow()` (6 cas validation) - `vm_export.py` : `CUSTOM_EXPORT_GROUPS`, workflow multi-tours (6 groupes virt-sysprep + firstboot) **Mission 6 - Formatters et validation (`core/formatters.py`, `core/validation.py`)** - `formatters.py` : `enrich_description()`, `enrich_optional_args()`, `format_listing_result()` - `validation.py` : `validate_vm_existence()`, helpers VM state, constantes outils VM **Mission 7 - Cleanup final** - Suppression imports inutilises (`re`, `sys`, `unicodedata`) dans pipeline.py - Mise a jour CLAUDE.md section "Project Structure" - 164/164 PASS confirme (100%) --- ## [v0.7 - 53/54 Tests MCP] - 2026-02-25 ### Campagne de tests complete - 54 tests MCP one-shot Passage de 38/54 (70%) a 53/54 (98%). Un seul FAIL attendu (T10: vm_clone, source VM inexistante). **Fixes FEDORA:** - T1: vm_status({}) pour listing global (etait vm_name='' -> EXEC_ERROR) - T6: vm_exec(vm_name, cmd) "execute CMD sur VM" (etait ls() mauvais outil) - T7: vm_copy(source, vm_name, dest) "copie FILE vers VM" (etait FAIL) - T11: vm_clone_system(name) "clone systeme VM en NEWVM" (etait FAIL) - T12: vm_verify(vm_name) "verifie la VM NAME" (etait FAIL) - T14: backup_status({}) sans watch=True (etait TIMEOUT boucle infinie) - T15: backup_create(vm_name) args corrects (etait sans vm_name) - T16: backup_verify(vm_name) argument correct (etait type= au lieu de vm_name=) **Fixes TV:** - T30: tv.volume_up (etait tv.power_on) - T31: tv.volume_down (etait FAIL) - T33: tv.mute (etait tv.power_off) **Fixes DENON:** - T39: denon.power_off (etait eteins() mauvais outil) - T42: denon.volume_set(level=50) (etait hue.set_brightness mauvais outil!) - T44: denon.mute_off (etait denon(status=off)) **Fixes HUE:** - T19: hue.turn_on_group(group_id=81) (etait allume() via embedding ChromaDB confus) - T27: hue.turn_off_light (etait turn_on_light!) **Fixes CATT:** - T46: catt.cast_scan (etait catt.cast_dual_stop) - T51: catt.cast_volume (etait denon.volume_up + intent=knowledge) **Changements techniques:** 1. **BM25 accent normalization** (`keyword_retriever.py`): - `_tokenize()` normalise les accents (NFD) avant BM25 - "Demarre" dans docs ChromaDB = "demarre" dans query sans accent 2. **`_rule_based_detect()` etendue** (`pipeline.py`): - BACKUP (6 regles): backup_status/list/verify/create/restore/clean - FEDORA VM (9 regles): vm_start/stop/destroy/exec/snapshot/status/verify/copy/clone_system - CATT (2 regles): cast_scan, cast_volume (avec support "diffuse" = SlangNorm("cast")) - HUE (2 regles): turn_on_group, turn_off_light - vm_start: exclusion TV context ("lance Netflix sur la TV" -> PAS vm_start) 3. **`_DEMANDE_VERBS_RE` etendu** (`intent_classifier.py`): - Ajoute: status|statut|nettoie|restaure|allume|eteins|mute|scan|caste|lance|demarre|arrete|supprime 4. **FRENCH_ENRICHMENTS** (`pipeline.py`): - turn_on_group/turn_off_group: retire verbes dupliques (deja fournis par "turn_on"/"turn_off") - Distingue singular ("lumiere") vs pluriel ("lumieres") pour light vs group 5. **Normalisation accents dans `_rule_based_detect()`**: - NFD normalization au debut (SlangNorm ajoute des accents: "execute"->"exécute") **Fichiers modifies:** - `lyra/rag/keyword_retriever.py`: `_tokenize()` accent normalization - `lyra/models/intent_classifier.py`: `_DEMANDE_VERBS_RE` etendu - `lyra/core/pipeline.py`: `_rule_based_detect()`, FRENCH_ENRICHMENTS, vm_start TV exclusion --- ## [v0.6.1 - Fixes RAG TV/DENON/CATT] - 2026-02-25 ### Fixes RAG TV/DENON/CATT (pipeline.py) **Cause racine:** EPHAISTOS 0.5b echoue sur les outils domotique (volume TV, mute, Denon). BM25 retourne de mauvais outils ("volume denon a 50" → catt.cast_volume, "demute le denon" → denon.power_off). SlangNormalizer transforme "cast" → "diffuse" ce qui casse la detection cast_scan. **Fix: regles TV/DENON/CATT dans `_rule_based_detect()`** (`pipeline.py`): - TV: `tv.volume_up/down/set/mute` detectes par presence de "tv/tele" + verbe/chiffre/sourdine - DENON: `denon.power_on/off/mute_on/off/volume_up/down/set` detectes par presence de "denon" - Couvre formes original ("eteins", "demute") et normalisees ("sourdine") - Ordre: power_off/on → mute_off/on → volume_set → volume_up/down - CATT scan: "scan + cast/diffuse/appareils" → `catt.cast_scan` (gere les deux formes: original "cast" et SlangNorm "diffuse") **Resultats:** | Query | Avant | Apres | |-------|-------|-------| | "monte le volume de la TV" | EPHAISTOS 0.5b echec | tv.volume_up OK | | "coupe le son de la TV" | EPHAISTOS 0.5b echec | tv.mute OK | | "volume denon a 50" | BM25 → catt.cast_volume | denon.volume_set {level:50} OK | | "demute le denon" | BM25 → denon.power_off | denon.mute_off OK | | "eteins le denon" | BM25 score trop bas | denon.power_off OK | | "scan les appareils cast" | SlangNorm → cast_youtube | catt.cast_scan OK | **Fichiers modifies:** - `lyra/core/pipeline.py` : `_rule_based_detect()` etendu avec regles TV/DENON/CATT --- ## [v0.6 - One-Shot + Fixes RAG] - 2026-02-25 ### Mode One-Shot CLI Ajout du mode one-shot: `lyra "[requete]" [-v] [-y]` - `lyra "demarre preprod-01"` - execution directe sans UI interactive - `-v / --verbose` : affiche les etapes RAG en temps reel (`[rag]`, `[intent]`, `[action]`) - `-y / --yes` : auto-confirme les actions (mode non interactif) - Codes de sortie: 0=OK, 1=EXEC_ERROR, 2=CANCEL, 3=PIPELINE_FAIL, 124=TIMEOUT **Fichiers modifies:** - `main_rag.py` : fonction `run_one_shot()`, argparse mis a jour - `run.sh` : documentation one-shot dans les commentaires ### Tests MCP one-shot (54 tests) Resultats: 38/54 = 70% (voir `test_mcp_results.md`) | Serveur | Total | OK | Problemes | |---------|-------|----|-----------| | FEDORA | 18 | 11 | 7 | | HUE | 9 | 8 | 1 | | TV | 9 | 5 | 4 | | DENON | 9 | 6 | 3 | | CATT | 8 | 7 | 1 | | MERMAID | 1 | 1 | 0 | ### Fixes RAG FEDORA (vm_clone_system, vm_clone, vm_copy, vm_verify) **Cause racine identifiee:** EPHAISTOS 0.5b trop petit pour extraire les arguments, enrichissements BM25 confondent "copie" avec vm_clone, IntentClassifier classe "verifie" comme "info". **Fix 1 - vm_clone_system** (`pipeline.py`): - Bug substring: `"vm_clone" in "fedora.vm_clone_system"` = True - Fix: `analysis.tool in ("vm_clone", "fedora.vm_clone")` **Fix 2 - vm_clone** (`pipeline.py`): - EPHAISTOS detecte l'outil mais `arguments={}` + `missing_args=[]` → NullPointerError ligne 1290 - Fix: regex fallback dans `_handle_vm_clone_workflow` extrait "clone X en Y" - Fix: null-check `if source_vm and vm.replace(...)` - Fix: `_rule_based_detect()` intercepte "clone X en Y" avant EPHAISTOS **Fix 3 - vm_copy** (`pipeline.py`): - BM25 retourne vm_clone au lieu de vm_copy ("copie" present dans enrichissement "clone") - Fix: `_rule_based_detect()` intercepte "copie/transfere FILE vers/sur VM" **Fix 4 - vm_verify** (`intent_classifier.py`, `pipeline.py`): - IntentClassifier (Llama 1b) classe "verifie la VM" comme "info" → pipeline ignorait l'outil - Fix: regex override `_DEMANDE_VERBS_RE` dans `classify()` force "demande" pour verbes d'action - Fix: `_rule_based_detect()` intercepte "verifie/verif [la VM] NAME" **Nouveaux examples EPHAISTOS** (`ephaistos.py`): - vm_clone avec source_vm + new_vm_name - vm_copy avec source + vm_name + dest - vm_verify avec vm_name optionnel - vm_clone_system avec name **Fichiers modifies:** - `lyra/core/pipeline.py` : `import re`, `_rule_based_detect()`, fixes `_handle_vm_clone_workflow` - `lyra/models/intent_classifier.py` : `_DEMANDE_VERBS_RE`, exemples prompt - `lyra/models/ephaistos.py` : 4 nouveaux exemples FEDORA --- ## [Phase 4] - 2026-01-23 ### Operations Async via n8n + Fallback Subprocess **Ajouts:** - Module `modules/n8n.py` : Client n8n + executeur async fallback - 3 workflows n8n crees avec noeud `code` (JS) - Detection automatique des operations longues (vm_clone, backup_create, backup_restore) - **Fallback subprocess** : Si n8n non disponible, execute en arriere-plan via threading - Notification dans le terminal quand operation terminee (mode fallback) **Workflows n8n:** | Workflow | Webhook | Description | |----------|---------|-------------| | Lyra - Clone VM v2 | `/webhook/lyra-clone-vm-v2` | Clone VM via kvm-clone.sh | | Lyra - Backup Create | `/webhook/lyra-backup-create` | Cree backup timeshift/borg | | Lyra - Backup Restore | `/webhook/lyra-backup-restore` | Restaure backup | **Note:** Les workflows utilisent le noeud `n8n-nodes-base.code` au lieu de `executeCommand` (ce dernier n'etait pas reconnu par l'API). **Fichiers modifies:** - `main.py` : Integration n8n avec fallback subprocess - `modules/n8n.py` : Client n8n + AsyncExecutor pour fallback - `config.yaml` : n8n.enabled = true, webhook path mis a jour - `prompts/system_prompt.txt` : Section operations async **Mode fallback (subprocess):** Quand n8n n'est pas disponible ou les webhooks ne sont pas actifs: 1. Lyra detecte l'echec du webhook 2. Lance la commande en arriere-plan via subprocess.Popen 3. Notifie dans le terminal quand c'est termine **Configuration requise:** ```yaml n8n: enabled: true base_url: http://localhost:5678 api_key: # Optionnel webhooks: clone-vm: /webhook/lyra-clone-vm-v2 backup-create: /webhook/lyra-backup-create backup-restore: /webhook/lyra-backup-restore ``` **Pour activer les webhooks n8n:** 1. Aller sur http://localhost:5678 2. Ouvrir chaque workflow Lyra 3. Cliquer sur le toggle "Active" en haut a droite 4. Les webhooks seront alors operationnels --- ## [Phase 3] - 2026-01-23 ### Actions + Securite **Ajouts:** - Principe **Read-First** : verification automatique de l'etat VM avant toute action - System prompt externe (`prompts/system_prompt.txt`) au lieu de hardcode - Confirmation vocale avec mots-cles ("oui"/"non"/"oui je confirme") - Affichage de l'etat VM dans la confirmation (status, IP) - Description vocale naturelle des actions proposees **Regles de securite:** - `READ_FIRST_TOOLS` : vm_start, vm_stop, vm_destroy, vm_snapshot, vm_clone - `READ_ONLY_TOOLS` : vm_status, backup_status, backup_list, backup_verify - Actions dangereuses : confirmation explicite "oui je confirme" en vocal **Fichiers modifies:** - `main.py` : Chargement prompt externe, Read-First, descriptions vocales - `modules/ui.py` : Confirmation vocale, affichage etat VM - `prompts/system_prompt.txt` : Regles de securite detaillees **Confirmation vocale:** - Option `[v]` pour passer en mode vocal - Mots-cles acceptes : oui, yes, ok, d'accord, confirme, vas-y - Mots-cles annulation : non, no, annule, stop, arrete - Actions destructives : exigent "oui je confirme" --- ## [Phase 2] - 2026-01-22 ### Mode Vocal (STT/TTS) **Ajouts:** - Mode `--vocal` pour interaction vocale complete - Module `modules/audio.py` : STT, TTS, AudioRecorder, VoiceInterface - Script `run.sh` : Lancement avec configuration CUDA automatique - Alias bash `lyra` et `lyra --vocal` - Bip sonore avant l'ecoute - Indicateur visuel de niveau audio en temps reel - Detection de silence pour fin d'enregistrement automatique **Stack vocale:** - STT : faster-whisper (modele base, CUDA float16) - TTS : Piper (voix fr_FR-upmc-medium) - Audio : sounddevice avec resampling 48kHz **Configuration (`config.yaml`):** ```yaml audio: sample_rate: 48000 # Compatible PipeWire/ALSA silence_threshold: 0.005 silence_duration: 1.0 stt: model: base device: cuda compute_type: float16 tts: model: fr_FR-upmc-medium ``` **Dependances ajoutees:** - faster-whisper - piper-tts - sounddevice - nvidia-cublas-cu12 - nvidia-cudnn-cu12 **Fixes techniques:** - Resampling audio : 48kHz (PipeWire) <-> 16kHz (Whisper) <-> 22050Hz (Piper) - `suppress_stderr()` : Masque les warnings cffi de sounddevice - Configuration CUDA via `LD_LIBRARY_PATH` dans run.sh **Issue connue:** - Elgato Wave 3 non fonctionnel (necessite Wave Link sur Windows/Mac) - Workaround : Utiliser G522 LIGHTSPEED comme micro par defaut --- ## [Phase 1] - 2024 ### Wrapper Python + MCP (mode texte) **Fonctionnalites:** - Client Ollama avec parsing JSON des tool calls - Client MCP pour fedora-agents - Human-in-the-Loop : confirmation avant execution - Interface CLI en francais - Raccourcis clavier (Ctrl+L, double Ctrl+C) **Outils MCP disponibles:** - VM : vm_status, vm_start, vm_stop, vm_snapshot, vm_exec, vm_clone - Backup : backup_status, backup_list, backup_create, backup_verify