Hermes vous remet désormais les fichiers créés par son agent dans les sandboxes distantes

Imaginez un vendredi soir : vous avez glissé les mains d’Hermes dans une sandbox distante — un environnement cloud Modal, ou une machine de dev SSH de l’entreprise — parce que vous voulez une isolation propre qui ne touche jamais aux fichiers de votre portable. L’agent termine sa tâche, produit un graphique, un rapport, une capture d’écran de page web dans cette sandbox, puis joint le chemin du fichier à sa réponse, tout content. Votre fenêtre de chat n’affiche rien. La seule trace est une ligne de journal : « Skipping unsafe MEDIA directive path ». Le fichier existe pourtant — il ne peut tout simplement pas vous parvenir.
Pourquoi les fichiers disparaissaient
Pour comprendre ce phénomène, il faut d’abord savoir comment Hermes vous transmet ses fichiers. L’agent écrit un tag MEDIA:<path> dans sa réponse ; le gateway le détecte, récupère le fichier et vous l’envoie dans le chat comme image ou pièce jointe. Cette validation de chemin a toujours été limitée à l’hôte par conception — la livraison de médias ne doit jamais devenir un canal permettant de lire des fichiers système arbitraires : ~/.ssh, les répertoires système et les dépôts d’identifiants d’Hermes figurent sur la denylist et sont toujours refusés.
Le problème tient précisément à ce « côté hôte uniquement ». Quand votre terminal backend est ssh, Modal, Daytona, Singularity ou une sandbox Vercel, les commandes de terminal de l’agent s’exécutent sur une autre machine — un autre système de fichiers. Le fichier qui se cache derrière MEDIA: n’est tout simplement pas sur l’hôte du gateway : la validation échoue et la pièce jointe est écartée comme un « chemin non sûr ». Le fichier, lui, a bien été généré ; le gateway ne pouvait simplement pas y accéder.
Le correctif : récupérer le fichier dans la sandbox et le rapatrier
La PR #103600, fusionnée le 5 septembre 2026 (la moitié sortante de l’issue #466), change la donne. L’idée est simple : quand la validation côté hôte échoue, mais que la session en cours s’exécute réellement dans une sandbox distante, le gateway rapatrie désormais le fichier via l’exec channel puis le fait passer par le flux de livraison normal.
Le mécanisme se déploie en trois couches :
- Transport (
tools/environments/base.py) : chaque backend d’environnement a gagné unfetch_file/fetch_realpathuniforme — le fichier est encodé en base64 puis renvoyé par l’exec channel, avec une limite de taille imposée dans la sandbox (head -c max+1, la même astuce qu’utilisetools.image_sourcepour qu’un flux infini comme/dev/zerone puisse pas noyer la mémoire de l’hôte), et des marqueurs de payload qui isolent la charge utile du bruit parasite du shell de connexion. - Déclencheur (nouveau
gateway/media_fetch.py, ~120 lignes) : il ne s’active que lorsqu’un backend distant est actif et que la validation côté hôte a déjà échoué — un backend local n’emprunte jamais ce chemin. - Hook (
gateway/platforms/base.py::_validated_delivery_path) : un ajout de trois lignes au point de passage unique dont dépendent tous les filtrages MEDIA — un seul correctif, efficace partout.
Limites de sécurité : pas de porte dérobée autour de la denylist
Rapatrier des fichiers depuis une sandbox distante est exactement le genre de fonctionnalité qui pourrait devenir un contournement de la denylist : aussi le chemin est-il vérifié deux fois. D’abord par la même denylist que l’hôte (préfixes système, répertoires du type ~/.ssh, dépôts d’identifiants d’Hermes), avant qu’un seul octet ne soit transféré ; puis une nouvelle fois, une fois que readlink -f a résolu le chemin réel — un lien symbolique pointant vers un fichier d’identifiants est refusé d’emblée, sans qu’un seul octet ne soit transmis. Les fichiers qui passent sont copiés dans cache/documents/ (une racine de livraison elle-même autorisée), puis validés et livrés exactement comme n’importe quel fichier de l’hôte. Le plafond est fixé à 50 Mo, en accord avec la limite d’envoi de chaque plateforme.
Envie de retrouver l’ancien comportement ? Sous le strict mode HERMES_MEDIA_DELIVERY_STRICT, les fichiers distants ne sont toujours jamais récupérés : une copie rapatriée atterrirait dans une racine autorisée et pourrait contourner le recency gate (le contrôle de fraîcheur) que le strict mode existe précisément pour faire respecter — le strict mode conserve donc le comportement d’origine.
Quelles configurations y gagnent
Tout terminal backend distant — ssh, Modal, Daytona, Singularity, sandbox Vercel — livre désormais les fichiers que l’agent y crée et référence avec un tag MEDIA: : captures d’écran d’un scraper qui tourne dans la sandbox, graphiques issus d’un script d’analyse de données, rapports rédigés au terme d’une longue tâche. En clair : tout ce que vous avez demandé à l’agent de « sauvegarder et m’envoyer ». Les backends locaux, eux, se comportent exactement comme avant — ils n’ont jamais eu besoin d’être rapatriés.
Une limite, en toute honnêteté : les backends capables de remonter leur répertoire personnel distant (_remote_home, par ex. ssh, Daytona, Vercel) bénéficient d’un support complet ; Modal, Singularity et Docker sans montage depuis l’hôte ne peuvent pas confirmer ce home distant, ils conservent donc la denylist conservatrice « any-component » — les fichiers situés sous des préfixes système comme /root n’y sont toujours pas récupérés par défaut, sauf si l’environnement définit explicitement _remote_home pour activer l’option.
Ce qui manque encore
Cette évolution ne couvre que la moitié sortante de #466. La PR exclut délibérément : un outil cœur autonome send_file (le tag MEDIA: existant fait office d’interface — premier barreau de la footprint ladder), des transports natifs propres à chaque backend, et l’injection entrante pièce jointe → sandbox (toujours ouverte sur #466). Ces éléments suivent la footprint ladder et seront livrés par incréments, plutôt que tous d’un coup.
Comment l’obtenir
Ni nouvelle commande, ni nouvelle configuration — c’est un correctif de comportement du gateway, qui s’active dès la mise à niveau. Le changement est fusionné sur main (2026-09-05) et ne figure encore dans aucun tag de release ; installez une version dev/main pour l’essayer dès maintenant, ou lancez simplement hermes update quand la prochaine release sortira. Un moyen rapide de vérifier ensuite : dans une sandbox distante, faites générer une image à l’agent et demandez-lui de vous l’envoyer — elle devrait arriver ; puis déposez un lien symbolique pointant vers ~/.ssh et confirmez qu’il est toujours refusé. Les deux contrôles font leur travail.
Pour en savoir plus sur les sandboxes et les backends, consultez nos guides précédents sur les conteneurs Docker partagés et les backends de terminal enfichables ; le rythme des releases se trouve sur la page des releases.