Der Genehmigungs-Dialog, der früher beim Kaffeholen verschwand, wartet jetzt auf dich

Du bittest deinen Agent, einen Build-Cache zu leeren, den du seit Monaten nicht angefasst hast. Er fragt, ob er die Command ausführen darf. Du drückst Enter und gehst los, um dir Kaffee nachzufüllen. Als du zurückkommst, ist der Dialog weg, die Command lief nie, und im Transcript steht nur eins: Genehmigung abgelaufen. Du hast nicht falsch geantwortet — die Uhr war einfach schneller als du.
Update (03.10.2026): Die Änderung wurde noch am selben Abend zurückgerollt (PR #130826: unbegrenztes Warten war als Richtung bereits abgelehnt, und sie ließ
hermes chat -qhängen, wo niemand antworten kann). Genehmigungs- und Clarify-Dialoge in CLI, TUI und Desktop folgen wiederapprovals.timeoutundagent.clarify_timeout. Gründe und Alternativen: die Revert-Notiz.
Diese Uhr war approvals.timeout, standardmäßig 300 Sekunden. Jetzt teilt sie sich entlang einer sehr einfachen Linie: Schaut die Person auf den Bildschirm? Die Dialoge in CLI, TUI und Desktop bleiben offen, bis du antwortest, den Turn abbrichst oder die Session schließt. Messaging-Plattformen behalten ihre Frist. Hier steht, was sich geändert hat und was das für dein Setup bedeutet.
Zuerst: die Fünf-Minuten-Mauer
Genehmigungen folgen einer nicht verhandelbaren Regel: Ein Timeout ist kein Ja. Er bedeutet nicht „niemand hat geantwortet, also los“, sondern „niemand hat geantwortet, also nicht“. Das ist der sichere Standard, aber er kostet dich eine still verworfene Aktion, und im Transcript sieht er fast identisch aus wie „die Command ist fehlgeschlagen“.
Warum fünf Minuten? Die Config-Kommentare sind erfrischend deutlich: Genehmigungen auf Messaging-Plattformen kommen als Push-Benachrichtigung an, und 60 Sekunden erwiesen sich als zu knapp — auf Telegram und Discord lief der Dialog oft ab, bevor der Nutzer sein Handy erreichte. Also wurde der Standard auf 300 angehoben.
Das Problem: 300 Sekunden ergeben für eine Push-Benachrichtigung Sinn und überhaupt keinen, wenn der Mensch schon vor dem Dialog sitzt. Du bist kurz weg für ein Glas Wasser, und was dich bei der Rückkehr erwartet, ist nicht „wartet noch“, sondern „du hast es schon verpasst“.
Die neue Regel, nach Oberfläche getrennt
| Oberfläche | Wie lange der Dialog offen bleibt |
|---|---|
Interaktive CLI (hermes chat) |
bis du antwortest oder den Turn abbrichst |
| TUI | wie oben |
| Desktop-App | wie oben |
| Telegram / Discord / Slack und andere Messaging-Plattformen | weiterhin approvals.timeout (standardmäßig 300 s) |
| ACP-Editoren und Approval-Transport-Plugins | weiterhin approvals.timeout |
Die Trennlinie ist nicht, welche Oberfläche schicker ist — sondern ob der Nutzer den Dialog sofort sehen kann. Auf einer Messaging-Plattform ist eine Genehmigung ein Push, der Stunden ungelesen liegen kann, und die Buttons der Plattform laufen selbst ab. Diese Oberflächen brauchen eine Frist. In CLI, TUI und Desktop ist der Dialog auf genau dem Bildschirm gezeichnet, den du ohnehin ansiehst — da gibt es nichts zu verpassen.
Eine Grenze sollte man im Kopf behalten: Während des Wartens läuft nichts. Der Vertrag hat sich nicht geändert — das bleibt fail-closed, kein Tool wird freigegeben, solange der Dialog offen ist, und ein Abbruch zählt als Ablehnung.
Wie es verdrahtet ist
Die Änderung berührt mehrere Module, aber der Kern ist ein Schalter, der einmal pro Turn gebunden wird:
| Stelle | Was sie tut |
|---|---|
tools/approval_context.py |
fügt set_prompts_wait_for_answer() / reset_prompts_wait_for_answer() hinter einer per-Turn-Context-Variable hinzu und macht approval_wait_seconds() zum einzigen Resolver für das eingebaute Genehmigungsfenster |
agent/deadline.py |
MAX_SAFE_TIMEOUT_S: 365 Tage außerhalb von Windows und unter Windows die DWORD-Millisekunden-Obergrenze (etwa 49,7 Tage) — beides semantisch „unbegrenzt“, so dimensioniert, dass die daraus abgeleiteten Grenzen für Lock.acquire / Thread.join nicht überlaufen |
tools/approval_human_wait.py |
human_wait_ceiling() gibt das Genehmigungsfenster plus einen Puffer von 60 Sekunden zurück, damit die Batch-Deadline-Schleife und das Serialisierungs-Lock des Authorization-Gate dieselbe Obergrenze nutzen, statt jeweils eine eigene zu erfinden |
hermes_cli/cli_chat_turn_mixin.py |
bindet den Schalter um jeden Turn herum und setzt ihn im Teardown zurück |
tui_gateway/prompt_turn.py |
TUI und Desktop teilen sich dieses Backend: gebunden, sobald der Input des Turns vorbereitet wird, beim Verlassen zurückgesetzt |
Ein subtiler Bug wurde gleich mit behoben. Die Batch-Deadline-Schleife konnte die Human-Wait-Obergrenze aus einem Context lesen, der das Binding nie gesehen hatte — dadurch hätte ein unbegrenzter Dialog wie ein hängendes Fenster wirken und eine Batch-Deadline immer weiter verlängern können. Jetzt liest sie die echte Obergrenze, und ein unbegrenzter Dialog wird von vornherein ausgeschlossen statt falsch vermessen. Die Messaging-Fenster bleiben exakt wie zuvor begrenzt.
Die Ausnahme, die man sich merken sollte: der Single-Query-Modus
Bei hermes chat -q "..." sitzt niemand daneben, deshalb bindet es den Schalter bewusst nicht und nutzt weiterhin approvals.timeout. Meist wartet es überhaupt nicht: approvals.single_query_mode hat den Standardwert deny, also wird eine gefährliche Command sofort abgelehnt und der Agent wird dazu gebracht, einen anderen Weg zu suchen.
Wenn du Single-Query-Sessions in Skripten oder CI ausführst, verlass dich nicht auf „länger warten“ — dort war nie jemand, der die Taste drückt.
Was du auf deiner Maschine prüfen solltest
- Interaktive Nutzung braucht kein erhöhtes Timeout mehr. Wenn du
approvals.timeoutnur, um dir Luft zu verschaffen, in die Tausende von Sekunden gesetzt hattest, kannst du es zurücksetzen: Es regelt jetzt nur noch Messaging-, ACP- und Plugin-Transports. - Stell Messaging separat ein.
hermes config set approvals.timeout 600betrifft nur diese Oberflächen; der Key ist zusammen mit dem Rest der Config in der hermes-config-Referenz dokumentiert. - Erwarte nicht mehr, dass ein Timeout ein Ja bedeutet. Um Unterbrechungen zu reduzieren, nutze Smart Approvals; um alles freizugeben, gibt es Yolo-Stufen; um ganze Command-Klassen zu verbieten, nutze die Deny-List. Das große Ganze steht im Leitfaden für unbeaufsichtigte Genehmigungen.
- Mach einen Dry-Run, bevor du die Policy änderst. Dry-Runs und Selbstchecks für Genehmigungen zeigen, welche Regel welches Urteil trifft, ohne irgendetwas auszuführen.
- Cron und unbeaufsichtigte Plattformen lehnen weiterhin ab. Ihre Standards sind unverändert, und genau das ist der Punkt.
Das Fazit
Was wie „der Dialog verschwindet nicht mehr“ aussieht, ist in Wahrheit ein Parameter, der in zwei Situationen aufgeteilt wurde: Ein Mensch schaut zu, also gibt es keine Frist; niemand schaut zu, also muss es eine geben. Der erste Fall bewahrt dich davor, eine Aktion an deine eigene Kaffeepause zu verlieren. Der zweite hält den Pfad über die Push-Benachrichtigung ehrlich.
Die Faustregel ist kurz genug zum Auswendiglernen: Wird der Dialog auf dem Bildschirm gezeichnet, den du ansiehst, wartet er auf dich; wurde er auf dein Handy geschickt, muss er ablaufen.