---
title: "Feuille de route"
sidebarTitle: "Feuille de route"
description: "Direction produit et justifications ; consultez le changelog pour les changements livrés."
---

# Feuille de route Eliza

Direction de haut niveau et justifications. Non exhaustif ; consultez le **[Changelog](changelog.mdx)** publié (ou `docs/changelog.mdx` dans le dépôt) pour les changements livrés avec les justifications.

<div id="principles-energy-and-experience-desktop">

## Principes : énergie et expérience (desktop)

</div>

**Objectif :** Surpasser le coût *ressenti* des **shells de développement toujours actifs** sur la batterie d'un portable tout en restant **plus visuellement distinctif** qu'une surface d'éditeur plate — **une meilleure UX et DX**, pas une prouesse de fiche technique.

- **Comparaison honnête :** **Cursor** (et similaires) propose une **grande surface persistante** : shell Chromium/Electron, éditeur, extensions, LSP, indexation, souvent plusieurs contextes web. L'interface desktop de Eliza est **plus étroite et orientée tâche** : compagnon, chat, paramètres et ponts — sur **Electrobun / WKWebView** plus **3D** optionnel. **Le total J/s dépend de la charge de travail** ; une comparaison équitable nécessite le même scénario et les mêmes outils (Activity Monitor, `powermetrics`, Instruments). Notre critère est une **excellente expérience par watt** pour un **compagnon IA local**, pas de revendiquer la victoire dans chaque comparaison directe avec un IDE complet.
- **Ce que nous optimisons :** **Le travail inutile** — GPU et timers pour des documents **cachés**, des canvas **hors écran** et du **polling HTTP redondant**. **Qualité adaptée à la batterie** lorsque débranché (plafond DPR, Spark splats plus serrés, pas d'ombres directionnelles, moins de ticks API en arrière-plan). **Riche par défaut** quand l'utilisateur **regarde l'application** et est sur secteur.
- **Leviers livrés (voir changelog + docs desktop) :** pause de visibilité `VrmViewer` ; `desktop:getPowerState` → `VrmEngine.setLowPowerRenderMode` ; intervalles conditionnés par la visibilité (dashboard, stream, game logs, fine-tuning, cloud credits) ; pause `rAF` du graphe 3D vectoriel quand caché ; hooks de dev en **opt-out** pour que l'outillage DX ne brûle pas accidentellement des watts (screenshot proxy, console agrégée).
- **Prochaines directions UX/DX :** Profil **Efficacité** / **Performance** visible par l'utilisateur (bascule unique), **`prefers-reduced-motion`**, **plafond de frames au repos** optionnel pour l'avatar quand la fidélité du mouvement compte moins que la batterie, et **textes in-app plus clairs** quand les économies de batterie sont actives (pour que les utilisateurs aient confiance dans le compromis).

<div id="done-this-cycle">

## Terminé (ce cycle)

</div>

- **Dashboard SSE : les callbacks d'action remplacent sur place** — Dans `generateChatResponse`, le `onStreamChunk` du LLM **ajoute** toujours les deltas de tokens ; le texte `HandlerCallback` des actions utilise **`replaceCallbackText`** : le premier callback gèle `preCallbackText` (sortie streamée du modèle), chaque callback suivant remplace uniquement le **suffixe** après cette base via `emitSnapshot` / `onSnapshot`. **Pourquoi :** Correspond à l'UX de **message progressif** de Discord/Telegram (éditer une bulle) sans modifier le contrat de callback elizaOS ni ajouter un protocole WebSocket parallèle. **Docs :** `docs/runtime/action-callback-streaming.md`, `docs/changelog.mdx` (2026-04-05). **Code :** `packages/agent/src/api/chat-routes.ts`.
- **Provenance de chargement des plugins + découverte stagehand** — `collectPluginNames()` enregistre la première **raison** pour laquelle chaque plugin est entré dans l'ensemble de chargement (`plugins.allow`, auto-activation par env, features, etc.) ; `resolvePlugins()` inclut **`(added by: …)`** quand des plugins optionnels échouent à s'installer pour que les opérateurs corrigent la **config/env** au lieu de chasser des bugs runtime fantômes. **Stagehand :** `findPluginBrowserStagehandDir()` remonte les **parents** depuis le fichier runtime pour trouver `plugins/plugin-browser/stagehand-server` — **pourquoi** la profondeur fixe `../` échouait pour les layouts de sous-modules `eliza/`. **Docs :** `docs/plugin-resolution-and-node-path.md` (section plugins optionnels), `docs/guides/developer-diagnostics-and-workspace.md`.
- **Migrations PGlite life-ops** — Les instructions **`CREATE INDEX`** principales s'exécutent **après** les backfills de colonnes / **`ALTER TABLE`** de propriété pour que les BDD legacy sans `domain` / `subject_*` n'échouent pas lors des mises à jour ; **`runMigrationWithSavepoint`** utilise des **`BEGIN`/`COMMIT`** explicites pour que **SAVEPOINT** soit valide sous PGlite. **Pourquoi :** les vraies bases de données rencontraient des erreurs de migration durant l'évolution du schéma life-ops. **Tests :** `packages/agent/test/lifeops-pglite-schema.test.ts`.
- **Scripts de dépendances workspace** — `fix-workspace-deps.mjs`, `replace-workspace-versions.mjs`, `restore-workspace-refs.mjs`, `workspace-prepare.mjs` et `workspace-discovery.mjs` réduisent la chirurgie manuelle du workspace ; le `package.json` racine expose les alias `workspace:*` / `fix-deps`. **Pourquoi :** les checkouts locaux `./eliza` et `plugins/*` dérivent fréquemment entre `workspace:*` et les limites semver. **Docs :** `docs/guides/developer-diagnostics-and-workspace.md`.
- **Bannières dev terminal (TTY)** — Tableaux de paramètres encadrés + titres figlet optionnels + ANSI quand stdout est un TTY (`NO_COLOR` / `FORCE_COLOR` respectés). **Pourquoi :** le dev desktop à quatre processus a besoin d'un env effectif **scannable** pour les humains/agents — **pas** de l'UI produit. **Docs :** `docs/apps/desktop-local-development.md`, `docs/guides/developer-diagnostics-and-workspace.md`.
- **Gitignore : `cache/audio/`, `scripts/bin/*`** — Garde les gros caches de médias locaux et les binaires optionnels (par ex. `yt-dlp`) hors de git ; **`scripts/bin/.gitkeep`** préserve le répertoire pour le PATH. **Pourquoi :** les clones ne devraient pas hériter d'artefacts de plusieurs centaines de Mo.
- **Electrobun / Vite : un seul `three` pour Spark + VRM** — `apps/app/vite.config.ts` **`sparkPatchPlugin`** (`resolveId` + hoist de splatDefines) et **`optimizeDeps.include`** pour `three` + `three/examples/jsm/*` afin que `@sparkjsdev/spark` et la pile avatar partagent **un seul** `THREE.ShaderChunk`. **Pourquoi :** un `three` imbriqué (par ex. sous Electrobun) causait des échecs de résolution **`splatDefines`** et des avertissements "multiple Three.js instances" ; **`resolve.alias`** seul cassait Rollup en prod. **Docs :** `docs/apps/desktop-vrm-three-and-spark.md`, `docs/changelog.mdx`.
- **Résilience VRM** — Chemins VRM / DRACO par défaut paresseux, fallback **`eliza-1`** au lieu d'assets **`default`** manquants, échecs Spark/world isolés pour que **VRM** se charge quand même. **Pourquoi :** le timing d'initialisation des modules intégrés et les arrière-plans splat optionnels ne doivent pas bloquer l'avatar compagnon. **Code :** `VrmViewer.tsx`, `VrmEngine.ts`, `state/vrm.ts`.
- **Persistance de la connexion Cloud** — `cloud-routes.ts` utilise **`cloudDisconnectEpoch`** (incrémentation à la déconnexion, snapshot avant le poll) au lieu de **`cloud.enabled === false`** pour ignorer la persistance. **Pourquoi :** l'ancien garde bloquait la **première** connexion quand le cloud n'avait jamais été activé. **Docs :** `docs/apps/desktop-vrm-three-and-spark.md` (section API).
- **Plugin OpenRouter : épinglage du npm cassé `alpha.12`** — Le `package.json` racine épingle **`@elizaos/plugin-openrouter`** à une version exacte fonctionnelle connue (actuellement **`2.0.0-alpha.13`**). **Pourquoi :** **`2.0.0-alpha.12`** a publié des fichiers ESM `dist/node` et `dist/browser` **tronqués** : seul `utils/config` est intégré, mais les exports référencent toujours **`openrouterPlugin`** / default — Bun échoue au chargement (*symbol not declared*). **Pourquoi pas patcher dist en postinstall :** le chunk d'implémentation du plugin est absent, pas une erreur d'export d'une ligne ; l'épinglage est la bonne atténuation jusqu'à ce que l'upstream republish. **Docs :** `docs/plugin-resolution-and-node-path.md` (section *Pinned: @elizaos/plugin-openrouter*), `docs/plugin-registry/llm/openrouter.md`, `docs/changelog.mdx`, `README.md`. **Note code :** `scripts/patch-deps.mjs` (bloc de commentaires à côté des autres contournements upstream).
- **Collisions de ports (dev + desktop embarqué)** — **`dev:desktop` / `dev:desktop:watch`** pré-allouent des ports loopback libres pour **`ELIZA_API_PORT`** et **`ELIZA_PORT`** (Vite) avant de lancer API, Vite et Electrobun pour que l'env, le proxy et l'URL du renderer restent alignés. **Agent embarqué :** Electrobun choisit le prochain port libre à partir du **`ELIZA_PORT`** préféré au lieu du **`lsof` + SIGKILL** par défaut ; l'optionnel **`ELIZA_AGENT_RECLAIM_STALE_PORT=1`** restaure la récupération. **Runtime :** `eliza.ts` / `dev-server.ts` synchronisent **`process.env`** avec le port de liaison réel de l'API quand c'est sûr. **UI :** **`injectApiBase`** sur le statut de l'agent pour la fenêtre principale + toutes les fenêtres de surface. **Pourquoi :** deux piles Eliza ou des processus errants ne devraient pas nécessiter de recherche manuelle de ports ou de tuer des processus non liés ; les liaisons dynamiques doivent se propager au renderer et à l'outillage dev. **Docs :** `docs/apps/desktop-local-development.md`, `docs/apps/desktop.md` (sections ports). **Code :** `scripts/lib/allocate-loopback-port.mjs`, `apps/app/electrobun/src/native/loopback-port.ts`, `agent.ts`, `index.ts`, `surface-windows.ts`, `vite.config.ts`, `dev-server.ts`, `eliza.ts`.
- **Observabilité dev desktop (IDEs / agents)** — **`GET /api/dev/stack`**, **`desktop:stack-status`**, **screenshot proxy** activé par défaut (`/api/dev/cursor-screenshot`, loopback + token), **console agrégée** activée par défaut (`.eliza/desktop-dev-console.log` + tail `/api/dev/console-log` avec liste d'autorisation de noms de base). **Pourquoi :** le dev multi-processus est opaque pour les outils qui ne peuvent pas voir la fenêtre native ; les hooks HTTP + fichier explicites évitent de deviner les ports et gardent le loopback/tokens bornés. Variables d'environnement d'opt-out documentées. **Docs :** `docs/apps/desktop-local-development.md` (section *IDE and agent observability*). **Rules :** `.cursor/rules/eliza-desktop-dev-observability.mdc`.
- **Electrobun : correspondance Darwin → macOS (WebGPU)** — **`getMacOSMajorVersion()`** utilise **`Darwin − 9`** pour **20–24** (macOS 11–15) et **`Darwin + 1`** pour **≥ 25** (macOS 26+ Tahoe). **Pourquoi :** `os.release()` est Darwin ; Tahoe est **macOS 26** sur **Darwin 25** — l'ancienne formule unique rapportait **16** et cassait la messagerie WebGPU de WKWebView et le gating. **Docs :** `docs/apps/electrobun-darwin-macos-webgpu-version.md`. **Tests :** `webgpu-browser-support.test.ts`.
- **Reset desktop via menu (processus principal)** — Confirmation + reset API + redémarrage + poll de statut s'exécutent dans le **main** Electrobun ; le renderer synchronise via **`menu-reset-eliza-applied`** et le **`completeResetLocalStateAfterServerWipe`** partagé. **Pourquoi :** le réseau différé du renderer WKWebView après les dialogues natifs ; les utilisateurs voyaient "il ne se passe rien" après la confirmation. La sonde de base accessible utilise uniquement **`res.ok`**. **Docs :** `docs/apps/desktop-main-process-reset.md`. **Tests :** `menu-reset-from-main.test.ts`, `reset-main-process.test.ts`.
- **Divulgation Edge TTS** — Documenter et exposer **`ELIZA_DISABLE_EDGE_TTS`** (registre + `docs/cli/environment.md` + doc TTS). **Pourquoi :** l'orchestrateur charge automatiquement Edge TTS → `node-edge-tts` → Microsoft ; "pas de clé API" ne signifie pas "hors ligne".
- **Couverture Vitest app-core** — La config racine glob **`packages/app-core/test/**/*.test.ts(x)`** et **`src/**/*.test.tsx`** ; exclut le e2e app-core sous `test/` du job unitaire par défaut. **Pourquoi :** les nouveaux tests sous `test/state` et `test/runtime` étaient ignorés ; un seul chemin TSX codé en dur était fragile.
- **Timeouts CI Node.js** — Utiliser `actions/setup-node@v4` avec `check-latest: false` partout ; ajouter le cache global Bun et `timeout-minutes` aux jobs test, release, nightly, benchmark-tests, publish-npm. **Pourquoi :** éviter les téléchargements nodejs.org et borner la durée des jobs. Voir `docs/build-and-release.md` "Node.js and Bun in CI: WHYs".
- **Durcissement du workflow release** — Shell strict (`bash -euo pipefail`) pour les étapes fail-fast ; boucles de retry pour `bun install` avec une exécution finale pour que l'étape échoue si tous les retries échouent ; le crash dump utilise le CLI ASAR maintenu ; `find -print0` / `while IFS= read -r -d ''` pour les chemins sûrs ; chemin DMG via find+stat ; suppression des artefacts node-gyp avant le pack ; rapport de taille incluant eliza-dist ; étape unique de build Capacitor ; le E2E du DMG packagé utilise un timeout CDP de 240s en CI et dump stdout/stderr au timeout. **Pourquoi :** Des builds reproductibles, des échecs clairs et une CI débogable ; voir `docs/build-and-release.md` "Release workflow: design and WHYs".
- **Résolution de plugins (NODE_PATH)** — Définir `NODE_PATH` à trois endroits pour que le `import("@elizaos/plugin-*")` dynamique se résolve depuis le CLI (`run-node.mjs` child), le chargement direct eliza (`eliza.ts` au chargement) et Electrobun (dev : remonter pour trouver `node_modules` ; packagé : ASAR `node_modules`). **Pourquoi :** Node ne cherche pas la racine du dépôt quand l'entrée est sous `dist/` ou que cwd est un sous-répertoire ; sans cela, "Cannot find module" cassait coding-agent et d'autres. Voir `docs/plugin-resolution-and-node-path.md`.
- **Résilience de démarrage Electrobun** — Garder le serveur API actif quand le runtime échoue à se charger pour que l'UI puisse afficher une erreur au lieu de "Failed to fetch". **Pourquoi :** Un seul module natif manquant (par ex. onnxruntime sur Mac Intel) rendait la fenêtre entière morte sans explication.
- **DMG Intel Mac x64** — Le workflow release exécute l'installation et le build desktop sous `arch -x86_64` pour l'artefact macos-x64 afin que les binaires natifs `.node` soient en x64. **Pourquoi :** la CI tourne sur arm64 ; sans Rosetta nous livrions des binaires arm64 et les utilisateurs Intel avaient "Cannot find module .../darwin/x64/...".
- **Dépendances de plugins auto-dérivées** — `copy-electrobun-plugins-and-deps.mjs` parcourt les dépendances du `package.json` de chaque paquet @elizaos au lieu d'une liste curatée. **Pourquoi :** Les listes curatées manquaient de nouvelles dépendances de plugins et causaient des échecs silencieux dans l'app packagée ; le parcours automatique reste correct quand les plugins changent.
- **Tests de régression pour le démarrage** — Les tests E2E vérifient le comportement keep-server-alive et d'échec de chargement de eliza.js. **Pourquoi :** Un test qui échoue empêche la suppression des gardes de gestion d'exceptions mieux que la documentation seule.
- **Correctif de résolution de plugins** — `NODE_PATH` défini sur les `node_modules` de la racine du dépôt dans `eliza.ts`, `run-node.mjs` et `agent.ts` (dev Electrobun). **Pourquoi :** Le `import("@elizaos/plugin-*")` dynamique depuis le `eliza.js` intégré ne pouvait pas résoudre les paquets à la racine ; `NODE_PATH` indique à Node où chercher. No-op dans l'app packagée (garde existsSync). Voir `docs/plugin-resolution-and-node-path.md`.
- **Patch des exports Bun** — Le postinstall dans `patch-deps.mjs` réécrit les plugins `@elizaos` affectés (et tout paquet similaire) pour que `exports["."]` n'ait plus `"bun": "./src/index.ts"` quand ce fichier n'existe pas. **Pourquoi :** Le tarball publié ne livre que `dist/` ; Bun choisit la condition `"bun"` en premier et échoue. Supprimer la condition morte permet à Bun d'utiliser `"import"` → `./dist/index.js`. Voir "Bun and published package exports" dans `docs/plugin-resolution-and-node-path.md`.
- **Release size-report : SIGPIPE 141** — Les pipelines `du | sort | head` dans l'étape "Report packaged app size" s'exécutent dans un sous-shell avec `|| r=$?` et autorisent l'exit 141 ; le stderr de `sort` est réduit au silence. **Pourquoi :** Sous `-euo pipefail`, 141 quitterait l'étape avant qu'on puisse l'autoriser ; le sous-shell le capture. Voir `docs/build-and-release.md`.
- **Routes NFA : plugin optionnel** — `/api/nfa/status` et `/api/nfa/learnings` chargent paresseusement `@elizaos/plugin-bnb-identity` et se rabattent quand il est manquant. **Pourquoi :** Le core et les tests fonctionnent sans le plugin ; la déclaration de type ambient garde le typecheck satisfait.

<div id="short-term--follow-ups">

## Court terme / suivis

</div>

- **Callbacks d'action :** Si un plugin a vraiment besoin de **plusieurs segments assistant indépendants** depuis un seul tour d'action (pas du remplacement progressif), nous pourrions ajouter un flag de callback optionnel ou une API séparée — aucun besoin aujourd'hui. **Pourquoi différer :** Le défaut correspond à Discord/Telegram ; YAGNI jusqu'à ce qu'un plugin concret le demande.
- **OpenRouter : dé-épingler quand l'upstream corrige** — Quand `@elizaos/plugin-openrouter` publie une release après **`alpha.12`** avec des bundles `dist/node/index.node.js` (et browser) complets vérifiés, assouplir l'épinglage exact. Actuellement épinglé à **`alpha.13`**. **Pourquoi :** Rester sur un épinglage dur indéfiniment rate de vrais correctifs ; nous évitons seulement les tarballs cassés jusqu'à ce que npm ait un bon artefact.
- **Hygiène des plugins upstream** — Certains plugins (par ex. `@elizaos/plugin-discord`) listent `typescript` dans `dependencies` au lieu de `devDependencies` ; nous l'ignorons via `DEP_SKIP` pour éviter le gonflement du bundle. **Pourquoi :** Corriger l'upstream réduirait notre liste d'exclusions et garderait le package.json des plugins correct.
- **Optionnel : filtrer les dépendances intégrées** — Nous copions intentionnellement toutes les dépendances transitives (y compris celles que tsdown a pu inliner) car les plugins peuvent faire des require dynamiques à l'exécution. **Pourquoi :** Exclure les dépendances "probablement intégrées" risquerait un "Cannot find module" dans l'app packagée. Si nous obtenons un jour une analyse statique du dist/ des plugins pour savoir ce qui n'est jamais requis à l'exécution, nous pourrions réduire la copie ; pas une priorité.

<div id="longer-term">

## Plus long terme

</div>

- **Desktop :** Un binaire macOS universel/fat (un seul .app avec arm64+x64) est possible via `lipo` ou des cibles de packaging desktop mais ajoute du temps de build et de la complexité ; des DMG séparés sont acceptables pour le moment.
- **CI :** Envisager la mise en cache des rebuilds natifs desktop par architecture pour accélérer la matrice de release.
