Le prompt d'approbation qui disparaissait pendant la pause café vous attend désormais

Vous demandez à votre agent de vider un cache de build que vous n’avez pas touché depuis des mois. Il vous demande s’il peut exécuter la commande. Vous appuyez sur Entrée et partez vous resservir un café. À votre retour, le prompt a disparu, la commande n’a jamais tourné, et le transcript affiche une seule chose : approbation expirée. Vous n’avez pas mal répondu — c’est simplement l’horloge qui a été plus rapide que vous.
Mise à jour (03/10/2026) : Le changement a été annulé la nuit même (PR #130826 : l’attente illimitée avait déjà été rejetée comme direction, et elle bloquait
hermes chat -q, où personne ne peut répondre). Les prompts d’approbation et de clarification du CLI, du TUI et du Desktop dépendent de nouveau deapprovals.timeoutet d’agent.clarify_timeout. Motifs complets et alternatives : la note d’annulation.
Cette horloge, c’était approvals.timeout, 300 secondes par défaut. Elle se scinde désormais selon une ligne très simple : la personne regarde-t-elle l’écran ? Les prompts du CLI, du TUI et du Desktop restent affichés jusqu’à ce que vous répondiez, interrompiez le tour ou fermiez la session. Les plateformes de messagerie conservent leur délai. Voici ce qui a changé et ce que cela implique pour votre configuration.
D’abord, le mur des cinq minutes
Les approbations obéissent à une règle non négociable : un délai expiré ne vaut pas un oui. Ce n’est pas « personne n’a répondu, donc vas-y » — c’est « personne n’a répondu, donc n’y va pas ». C’est le défaut sûr, mais il vous coûte une action silencieusement abandonnée, et dans le transcript cela ressemble presque trait pour trait à « la commande a échoué ».
Pourquoi cinq minutes ? Les commentaires de configuration sont d’une franchise rafraîchissante : les approbations sur les plateformes de messagerie arrivent sous forme de notification push, et 60 secondes se sont révélées trop justes — sur Telegram et Discord, le prompt expirait souvent avant que l’utilisateur n’atteigne son téléphone. La valeur par défaut a donc été portée à 300.
Le problème, c’est que 300 secondes ont du sens pour une notification push et aucun quand l’humain est déjà assis devant le prompt. Vous vous êtes absenté le temps d’un verre d’eau, et ce que vous avez retrouvé n’était pas « toujours en attente », mais « vous l’avez déjà manqué ».
La nouvelle règle, répartie par surface
| Surface | Combien de temps le prompt reste ouvert |
|---|---|
CLI interactif (hermes chat) |
jusqu’à ce que vous répondiez ou interrompiez le tour |
| TUI | idem |
| Application Desktop | idem |
| Telegram / Discord / Slack et autres plateformes de messagerie | toujours approvals.timeout (300 s par défaut) |
| Éditeurs ACP et plugins de transport d’approbation | toujours approvals.timeout |
La ligne de partage n’est pas de savoir quelle surface est la plus sophistiquée — c’est de savoir si l’utilisateur peut voir le prompt immédiatement. Sur une plateforme de messagerie, une approbation est un push qui peut rester non lu pendant des heures, et les boutons de la plateforme elle-même expirent. Ces surfaces ont besoin d’un délai. Sur le CLI, le TUI et le desktop, le prompt est affiché sur l’écran que vous regardez déjà, il n’y a donc rien à manquer.
Une frontière à garder en tête : rien ne tourne pendant l’attente. Le contrat n’a pas changé — c’est toujours fail-closed, aucun outil n’est libéré tant que le prompt est affiché, et l’interrompre équivaut à un refus.
Comment c’est câblé
Le changement touche plusieurs modules, mais le cœur est un interrupteur lié une fois par tour :
| Où | Ce que ça fait |
|---|---|
tools/approval_context.py |
ajoute set_prompts_wait_for_answer() / reset_prompts_wait_for_answer() derrière une variable de contexte par tour, et fait de approval_wait_seconds() l’unique résolveur de la fenêtre d’approbation intégrée |
agent/deadline.py |
MAX_SAFE_TIMEOUT_S : 365 jours hors Windows, et sous Windows le plafond DWORD-milliseconde (environ 49,7 jours) — les deux sémantiquement « sans limite », dimensionnés pour que les bornes de Lock.acquire / Thread.join qui en dérivent ne puissent pas déborder |
tools/approval_human_wait.py |
human_wait_ceiling() renvoie la fenêtre d’approbation plus une marge de 60 secondes, afin que la boucle de délai par lots et le verrou de sérialisation du portail d’autorisation partagent un seul plafond au lieu d’en inventer chacun le sien |
hermes_cli/cli_chat_turn_mixin.py |
lie l’interrupteur autour de chaque tour, le réinitialise au démontage |
tui_gateway/prompt_turn.py |
le TUI et le Desktop partagent ce backend : lié quand l’entrée du tour est préparée, réinitialisé à la sortie |
Un bug subtil a été corrigé au passage. La boucle de délai par lots peut lire le plafond d’attente humaine depuis un contexte qui n’a jamais vu la liaison, ce qui ferait passer un prompt sans limite pour une fenêtre bloquée et ne cesserait de repousser un délai de lot. Elle lit désormais le vrai plafond, et un prompt sans limite est exclu d’emblée plutôt que mal mesuré. Les fenêtres de messagerie restent bornées exactement comme avant.
L’exception à retenir : le mode requête unique
hermes chat -q "..." n’a personne assis à côté, donc il ne lie délibérément pas l’interrupteur et utilise toujours approvals.timeout. En général, il n’attend même jamais : approvals.single_query_mode vaut deny par défaut, si bien qu’une commande dangereuse est refusée immédiatement et l’agent est poussé à trouver une autre voie.
Si vous lancez des sessions en requête unique dans des scripts ou en CI, ne comptez pas sur le fait d’« attendre plus longtemps » — il n’y a jamais eu personne pour appuyer sur la touche.
Ce qu’il faut vérifier sur votre machine
- L’usage interactif n’a plus besoin d’un délai relevé. Si vous aviez poussé
approvals.timeoutà plusieurs milliers de secondes juste pour vous laisser de la marge, vous pouvez le remettre : il ne régit désormais que la messagerie, l’ACP et les transports de plugins. - Réglez la messagerie séparément.
hermes config set approvals.timeout 600n’affecte que ces surfaces ; la clé est documentée avec le reste de la configuration dans la référence de configuration hermes. - Cessez de croire qu’un délai expiré signifie oui. Pour réduire les interruptions, utilisez les approbations intelligentes ; pour tout ouvrir, il y a les paliers yolo ; pour interdire des classes entières de commandes, utilisez la liste de refus. La vue d’ensemble se trouve dans le guide des approbations non surveillées.
- Faites un dry-run avant de changer de politique. Dry-runs et auto-vérifications des approbations montrent quelle règle correspond à quel verdict sans rien exécuter.
- Le cron et les plateformes non surveillées refusent toujours. Leurs valeurs par défaut sont inchangées, et c’est bien là l’essentiel.
En résumé
Ce qui ressemble à « le prompt cesse de disparaître » est en réalité un seul paramètre scindé en deux situations : un humain regarde, donc pas de délai ; personne ne regarde, donc il en faut un. Le premier cas vous évite de perdre une action à cause de votre propre pause café. Le second garde honnête la voie de la notification push.
La règle de base est assez courte pour être mémorisée : si le prompt est affiché sur l’écran que vous regardez, il vous attend ; s’il a été envoyé sur votre téléphone, il doit expirer.