Hermes Agent v0.21.5
Aperçu
v0.21.5 — encore un tag qui existe pour les consommateurs en aval. Publié le 24 septembre 2026. Depuis v0.21.4 : 1 610 commits non fusionnés · 4 828 fichiers modifiés (+164 132 / −149 440) · 460 PR fusionnées · 475 issues fermées, mesurés au commit f97608f178d1ffeca59860195ab7da295f7c8e5f.
Le tag est là pour la même raison que v0.21.4 : les images Docker, Hermes Cloud et les déploiements hébergés se construisent tous depuis le tag de release le plus récent, donc les correctifs de cette fenêtre doivent atterrir dans une version stable avant que les utilisateurs du cloud puissent en profiter. Le corps de la release le dit sans détour — la synthèse complète est reportée à v0.22.0, qui documentera tout depuis v0.21.0 : points forts, domaines fonctionnels et crédits complets des contributeurs. Rien dans cette fenêtre n’est omis.
D’ici là, cette page indique à qui ce tag s’adresse, et quels changements de la fenêtre tournent déjà sur votre machine.
Points forts
1. Desktop reçoit des catalogues complets en français, allemand et espagnol, plus un réglage de direction du texte
Desktop embarque désormais des catalogues d’interface complets pour le français, l’allemand et l’espagnol, listés sous Settings → Appearance → Language. Les trois catalogues traduisent chaque clé de en.ts, y compris tous les libellés et descriptions de réglages defineFieldCopy et la rotation d’introduction purement affichée (intro-{fr,de,es}.ts). Ils s’enregistrent comme des surcouches defineLocale(), à la manière de ja, ar et ru, donc une clé ajoutée plus tard en anglais retombe sur l’anglais au lieu de faire échouer le typecheck de chaque contributeur. Le backend embarque déjà locales/{fr,de,es}.yaml ; display.language: fr|de|es, ainsi que les alias régionaux comme fr-CA, de-AT et es-419, se résolvent maintenant vers le catalogue correspondant au lieu de retomber sur l’anglais. Le français utilise vous, l’allemand Sie, l’espagnol l’impératif neutre tú. Les noms propres et les identifiants techniques (Hermes, MCP, API, OAuth, identifiants de modèles, variables d’environnement, noms de fichiers) restent non traduits. PRs #120829.
À côté de cela, Appearance → Chat display → Text direction propose trois options : Auto / Right-to-left / Left-to-right. Jusqu’ici, chaque bloc de prose de chat et chaque ligne du composer tiraient leur direction de base de leur premier caractère fort (UAX#9 plaintext), ce qui ne permet pas de distinguer un paragraphe arabe qui commence par un nom latin d’un paragraphe anglais citant de l’arabe. Auto correspond exactement à l’ancien comportement — aucun nouvel attribut, aucune clé de stockage ; choisir RTL ou LTR applique un dir à la prose du chat et aux deux composers, chaque ligne d’un brouillon multiligne comprise. Le code reste LTR (le code en bloc et en ligne ainsi que KaTeX restent isolés) et l’habillage de l’application (barre latérale, barre d’outils, boutons du composer) ne bascule pas. C’est purement de la présentation : aucun caractère de contrôle bidi n’entre dans les brouillons, les messages envoyés ou le texte copié. La préférence appartient au renderer, ce n’est pas une clé de config.yaml. PRs #120822.
2. Les livraisons webhook peuvent se refléter dans la session du chat cible
Par défaut, une livraison est fire-and-forget : chaque événement tourne dans sa propre session, donc répondre « il est donc sorti ? » dans ce chat vous vaut « à qui faites-vous référence ? ». Une route peut désormais l’activer avec mirror_to_session: true (ou hermes webhook subscribe … --mirror-to-session), et après un envoi réussi le texte livré est ajouté à la session du chat cible sous la forme [Webhook delivery: <route>] suivi du message, en tant que tour user étiqueté disposant de l’autorité user — le même chemin et la même convention de rôle que les cron briefs reprenables (gateway.mirror.mirror_to_session). Désactivé par défaut ; seul un vrai booléen true l’active. Le miroir s’exécute à l’intérieur de _profile_scope(profile), donc une route /p/<profile>/ écrit dans le state.db de ce profile et jamais dans celui d’un autre. C’est du best-effort : cela ne fait jamais échouer la livraison, et c’est ignoré quand le chat n’a pas encore de session gateway. Sur une route deliver_only, le texte mis en miroir est la charge utile brute rendue, donc ne l’activez que pour des sources dont vous acceptez qu’elles figurent dans votre conversation. PRs #120825.
3. Bot Screen atteint les images hébergées via les tags -desktop
Les déploiements hébergés et immuables obtiennent désormais Bot Screen en tirant un tag d’image -desktop. L’image publiée est livrée sans Xvnc/Xfce et ne permet aucune installation à l’exécution (hermes non privilégié, pas de sudo, /opt/hermes scellé), donc la couche d’image est le seul chemin de livraison. :latest / :main / :v* restent l’image qu’ils sont aujourd’hui ; :latest-desktop / :main-desktop / :v*-desktop ajoutent les paquets et le Chromium headed de Playwright. Un manifest par variante, donc un échec de publication desktop ne peut pas faire sauter le :latest du slim. Les conteneurs n’ont pas de logind, donc XDG_RUNTIME_DIR=/tmp/hermes-runtime est créé en 0700 sous l’utilisateur hermes au démarrage — l’ancien repli sur $HOME/.cache atterrissait sur le volume partagé /opt/data, où deux instances se disputaient un même verrou d’affichage — et un répertoire appartenant à un autre utilisateur ou symbolique est refusé plutôt que chowné. PRs #120914 ; la doc de la fonctionnalité est website/docs/user-guide/features/bot-screen.md.
4. Ajouter un modèle par slug depuis chaque sélecteur de modèles
Le menu de modèles du composer, la boîte de dialogue de changement de modèle, Settings → Model et la liste d’édition des modèles permettent tous d’ajouter un modèle par slug : une nouvelle ligne de pied de menu Add custom model… transforme le champ de recherche en saisie de slug, en proposant une ligne par provider configuré au fil de la frappe, et le slug mémorisé reste dans la liste comme une ligne normale avec un × de suppression. PRs #120693.
5. Les voix locales ne se rechargent plus à chaque réveil
tts.keep_warm_seconds (défaut 60) est nouveau : après la libération du dernier lease, le déchargement attend ce délai, et toute bascule qui réactive la parole dans cette fenêtre l’annule et réutilise le modèle chargé. 0 décharge immédiatement. Cela compte surtout pour les conversations vocales en wake-only, qui se terminent juste après leur préchauffage Piper d’environ 2 s — la voix était auparavant évincée quelques millisecondes après son chargement, donc chaque réveil payait le rechargement complet. PRs #121080.
6. La boucle vocale du desktop commence à lire votre config
Read Aloud ne prononce plus de textes de remplacement anglais comme « code block omitted » et « link », et ne fait plus disparaître les tableaux Markdown : les blocs de code et les URL sont muets, les tokens de lien de fichier MEDIA: n’atteignent jamais la voix (la ponctuation qui suit immédiatement un token en ligne est conservée), un deux-points en fin de ligne laissé en suspens par un bloc supprimé devient un point, et les tableaux prononcent leur ligne d’en-tête (« Item, Value, Notes. ») tout en sautant les lignes du corps. PRs #120752. L’arrêt vocal lit désormais voice.stop_phrases (défaut ["stop"]) — il correspondait auparavant à une liste anglaise codée en dur, donc un utilisateur russophone qui disait « отбой » comme l’y invitait le message se le voyait envoyer à l’agent comme prompt — et l’énoncé entier doit être égal à une phrase, [] désactivant l’arrêt vocal. La lecture de la barge-in n’utilise plus un déclencheur codé en dur à 0.14, qui se situait au-dessus du niveau de parole d’un casque Bluetooth HFP pendant qu’une réponse est jouée, si bien que parler par-dessus la réponse ne l’interrompait jamais ; elle utilise désormais le voice.barge_in_threshold_multiplier existant (défaut 3.0). PRs #120751.
7. Les raccourcis vocaux par touches de fonction sont réassignables
La dictée ponctuelle du composer est désormais une action clavier composer.dictate non liée, distincte de la conversation vocale, réutilisant le chemin de capture canonique de Desktop pour les F-keys, Caps Lock, Delete, Space et les combinaisons de modificateurs. L’ancien champ voice.record_key de Desktop est supprimé, avec une note indiquant que la config reste réservée au CLI/TUI. L’arbitrage Electron fait qu’une F12 assignée atteint le renderer tandis qu’une F12 non assignée conserve le comportement DevTools. PRs #120928. Dans la même fenêtre, les workflows par touches de fonction sous macOS ont été corrigés : l’analyseur du TUI préserve intacts les séquences de terminal F1–F12 (y compris les chemins du protocole modified-key), et Ctrl+R a été ajouté comme repli portable de F7 pour le dock de travail en direct, sur le TUI comme sur le CLI classique. PRs #120666.
8. Une série de pannes qui ne frappaient que les Windows localisés
Quatre problèmes indépendants, spécifiques à Windows :
- Le crash du nom de fuseau. Windows renvoie les noms de fuseaux dans la page de code ANSI (
Paris, Madrid (heure d'été)en octets cp1252). QuandLC_CTYPEest en UTF-8, ces octets sont décodés avecsurrogateescape, donctzname()finit par contenir des surrogates isolés ;datetime.strftimeinsèretzname()dans le format en UTF-8, donc%Zlève uneUnicodeEncodeErrorà l’intérieur destrftime— avant qu’aucune chaîne n’existe que la passe existante_sanitize_surrogatespourrait nettoyer. Le system prompt est reconstruit à chaque début de conversation et à chaque compaction, donc la panne revenait sans cesse. Le nouveauhermes_time.safe_strftime()est identique octet pour octet àstrftimesur les locales saines (l’invariant du cache de préfixe tient) et décode autrement les octets échappés en surrogates via la page de code ANSI. PRs #120748. - Décodage des processus enfants sous Windows chinois. Le
_readerthreaddesubprocess.communicate()sous Windows décode la sortie de l’enfant avecsubprocess._text_encoding()—locale.getencoding(), cp936/GBK sous Windows chinois — dès qu’une capture passetext=Truesansencoding=. Les enfants concernés écrivent en UTF-8, donc tout octet illégal en GBK lève uneUnicodeDecodeErrordans ce thread, que lethreading.excepthookdu processus serve affiche comme une ligne[gateway-crash].hermes_cli/web_routers/files.py::_fs_git_branchétait l’un d’eux (ses kwargs se trouvent dans un littéral de dict, donc la règle ligne par ligne surtext=Truene le matchait jamais) et Desktop l’interroge en continu. PRs #120749. - Chemins absolus Windows.
C:\...\report.htmlet les chemins UNC\\server\share\...n’étaient ni reconnus comme chemins (donc aucun tag d’artefact n’apparaissait) ni laissés intacts par le repli du renderer, qui traitait tout ce qui ne commence pas par/comme relatif et le concaténait au cwd, produisant des chemins comme/repo/C:\...; l’étiquette d’aperçu interprétait ensuiteC:comme un schéma d’URL. Le nouveauisWindowsAbsolutePathaccepte une lettre de lecteur avec l’un ou l’autre slash et un préfixe UNC. PRs #121074. - La sonde de démarrage à froid. Sur un démarrage à froid Windows, le backend local répond en HTTP, puis sa boucle d’événements se bloque pendant 12–28 s pendant que le GIL est tenu par l’import des modules de plateforme du gateway. La sonde WebSocket au démarrage abandonnait après un délai fixe de 10 s, ce qui figeait un échec de démarrage, tuait l’enfant et déclenchait la cascade « Ignoring stale Hermes backend exit (1) ». Un backend que cette application a lancé continue désormais d’attendre au-delà du budget de base de 10 s tant que l’enfant est vivant, jusqu’à un plafond de 90 s — le même budget que l’étape d’annonce de port autorise déjà pour un démarrage à froid. Le signal de progression est la vivacité, pas la fraîcheur de la sortie, parce que le blocage est silencieux. Les gateways distants, le bouton « Test remote » et l’attachement d’un backend hôte conservent le budget fixe de 10 s. PRs #120750.
Améliorations
- La vague du SDK de plugins Desktop (API de brouillon du composer, slots de liste de sessions et de décoration de lignes, préférences de navigation de la barre latérale, libellés des model-pill, ponts typés settings/skills/toolsets/profiles, une primitive d’embed sandboxée, un slot de réglages d’apparence, un pont d’événements public pour les backends de plugins) est entièrement arrivée dans cette fenêtre ; sa synthèse prévue est elle aussi reportée.
- La page Connectors a remplacé l’onglet MCP, avec « Connect now » pour les serveurs MCP d’un plugin fraîchement installé, et les outils et skills des plugins installés deviennent actifs dans chaque chat ouvert ; l’onboarding propose les plugins du catalogue à côté des connectors.
- Kanban a eu droit à une passe de design : une modale de ticket sur deux colonnes et du texte de tâche en markdown.
- La fenêtre contient aussi une longue série de travaux de performance sur les chemins critiques, à travers le chargement de config, le registre d’outils, le traitement des messages du gateway et le sélecteur de modèles.
Mise à niveau
hermes update # installations via git
# ou relancez le one-liner d'installation
Docker / Hermes Cloud : les images se construisent depuis ce tag, nousresearch/hermes-agent:v2026.9.24.
Changelog complet : v2026.9.21…v2026.9.24
← Hermes Agent ChangelogCorrespondance des versions : le tag est daté (
v2026.9.24) et la version sémantique estv0.21.5— même release.