Nach 11 Stunden zurückgerollt: Warum Hermes Approval-Abfragen eine Frist behalten


Wenn du gestern gelesen hast, dass Hermes Approval-Abfragen jetzt für immer auf dich warten, ist dir vielleicht aufgefallen, dass sie wieder herunterzählen und ablehnen, wenn die Zeit abläuft. Das sieht aus, als hätte ein Maintainer seine Meinung geändert, ist aber in Wahrheit eine schnell gezogene Design-Linie: Die Änderung beseitigte ein echtes Ärgernis und erzeugte einen Deadlock an der einen Stelle, an der niemand antworten kann. Sie wurde 11 Stunden und 32 Minuten nach dem Merge zurückgerollt.

Dieser Beitrag bezieht keine Seite. Er legt die Zeitleiste dar, die Gründe und – am wichtigsten – das Verhalten, das jetzt tatsächlich gilt, denn das entscheidet, wie du approvals konfigurieren solltest.

Wie das Verhalten heute aussieht

Halten wir zuerst das Fazit fest, denn es entscheidet, ob du deine Konfiguration überhaupt anfassen musst. Stand 3. Oktober 2026:

Oberfläche Wie lange die Abfrage lebt
Interaktive CLI, TUI, Desktop-App Zählt bis approvals.timeout herunter (Standard 300 s), wertet es dann als nicht genehmigt und blockiert den Befehl
Telegram / Discord / Slack und andere Messaging-Plattformen Nutzt ebenfalls approvals.timeout; eine Push-Benachrichtigung kann minutenlang ungelesen liegen
Clarify-Fragen (agent.clarify_timeout) Standard 3600 s (1 Stunde); beim Ablauf wird das Warten mit „user did not respond within Xm“ entblockiert

Der Kernvertrag ist unverändert und wichtig: Ein Timeout ist keine Genehmigung. Es heißt nicht „Niemand hat geantwortet, also nur zu“ – es heißt „Niemand hat geantwortet, also nicht ausführen.“ Der Upstream-Text für eine abgebrochene Abfrage sagt es in einem Satz: Schweigen ist keine Zustimmung. Eine Frist ist der sichere Standard, und der Preis dafür ist, dass du eine Operation still verlieren kannst.

Was die zurückgerollte Änderung tat

Am 1. Oktober um 16:50 CST wurde PR #130167 gemergt. Seine Prämisse war, die Oberflächen danach aufzuteilen, ob ein Mensch vor dem Bildschirm sitzt:

Approval- und Clarify-Abfragen in der CLI, der TUI und der Desktop-App sollten nicht mehr auslaufen. Sie blieben stehen, bis geantwortet wurde, bis der Turn unterbrochen wurde oder bis die Session geschlossen wurde. Messaging-Plattformen, ACP-Editoren und Approval-Transport-Plugins behielten approvals.timeout und agent.clarify_timeout, weil eine Push-Benachrichtigung tatsächlich ungelesen liegen bleiben kann und Plattform-Buttons ablaufen.

Mechanisch war es ein ContextVar-Paar pro Turn – set_prompts_wait_for_answer() / reset_prompts_wait_for_answer() – plus ein Resolver, approval_wait_seconds(), der das plattformsichere Maximum zurückgab, wenn der Schalter gebunden war, und ansonsten approvals.timeout. Die CLI-Abfrage, die Approval-Poll-Schleife des TUI/Desktop-Gateways und die Obergrenze für das Warten auf den Menschen lasen alle diesen einen Einstiegspunkt.

Als Produktinstinkt ist das verlockend: Du stehst auf, um Wasser zu holen, kommst zurück, die Abfrage steht noch da, du verlierst die Operation nicht.

Warum es in dieser Nacht zurückgerollt wurde

Am 2. Oktober um 04:23 CST hat PR #130826 das Ganze zurückgerollt. Der Rücksetzungs-Text nennt drei Gründe, zwei davon harte Blocker.

Erstens: Die Richtung war schon vor dem Merge abgelehnt worden. Die erste Zeile der Rücksetzung ist unverblümt: „Wait-forever prompts were rejected as a direction before #130167 merged.“ Das ist ein Prozessfehler, kein technischer.

Zweitens: Es brach hermes chat -q. Das ist der, der wirklich zählte. Der Single-Query-Modus ist unbeaufsichtigt: Niemand sitzt da, um zu antworten. Eine Frist aus einer Approval-Abfrage zu entfernen bedeutet, dass die Session unbegrenzt auf eine Antwort wartet, die nie kommt – sie hing einfach. Upstream musste einen Folge-Patch (82c63c505a9) ausliefern, um chat -q seine Frist zurückzugeben, und diese Rücksetzung nimmt den Patch mit, weil das Beheben des Hängens sein einziger Daseinsgrund war.

Drittens: Ein End-to-End-Test begann, bei jedem Lauf fehlzuschlagen. tests/e2e/core/providers/test_native_codex_app_server_faults.py scheiterte bei jedem e2e-Lauf auf main, bis derselbe Folge-Patch die eine Oberfläche flickte, die er berührte.

Der Umfang der Rücksetzung: 11 Commits plus der abhängige Folge-Patch, über 57 Dateien, jede davon byte-identisch zu ihrem Zustand vor #130167 (5 Dateien tragen spätere, unabhängige Commits, die intakt bleiben). Was zurückkam, ist das Verhalten, das du heute siehst – CLI- und TUI/Desktop-Approval-Panels zählen herunter und lehnen am konfigurierten Timeout ab, Clarify-Anfragen tragen wieder ihre Frist, ein auf einen Nutzer wartender Bot-Mode-Gruppen-Turn behält seine harte 3-Stunden-Obergrenze, und die Dokumente, der Einstellungstext und die Locale-Strings wurden wiederhergestellt.

Ein Detail ist es wert, im Kopf zu behalten: Die Änderung wurde nie in einem Release ausgeliefert. Der neueste Tag damals war v2026.9.24 (Version v0.21.5, veröffentlicht am 24. September), und #130167 wurde am 1. Oktober gemergt. Nur main-tracking-Installationen sahen die Wait-forever-Abfrage überhaupt; Release-Nutzer nie.

Was die 11 Stunden tatsächlich lehren

Nimmt man die Mechanik weg, bleiben drei Regeln nützlich.

Eine Frist ist ein Sicherheitsmechanismus, kein UX-Schönheitsfehler. Der Countdown trägt eine Verantwortung: Wenn niemand bestätigt, muss das System von selbst anhalten können. Entfernst du ihn, wird aus „unbeaufsichtigt“ „für immer hängend“, was schlimmer ist als eine Ablehnung – eine Ablehnung drängt den Agenten, einen anderen Weg zu finden, während ein Hänger sich einfach aufstaut. Wenn dich Abfragen nerven, sind die richtigen Stellschrauben die Timeout-Länge und der Approval-Modus, nicht das Fehlen eines Timeouts.

„Kann überhaupt jemand antworten?“ gehört zur Absicht, nicht zur Wartedauer. Eine -q-Session, ein Cron-Job und ein Webhook-Trigger unterscheiden sich alle grundlegend von dir, der an einem Terminal sitzt. Hermes gibt jedem einen expliziten Schalter: approvals.single_query_mode, approvals.cron_mode und approvals.unattended_mode, jeweils deny oder approve, jeweils standardmäßig deny – denn auf diesen Oberflächen ist es besser, sofort zu blockieren und den Agenten den Block umgehen zu lassen, als einen vollen Timeout auszusitzen und fail-closed zu scheitern.

Versuche nicht, es durch Hochdrehen des Timeouts zu lösen. Die Obergrenze im Code ist nicht dekorativ: approvals.timeout wird auf das plattformsichere Maximum begrenzt (agent.deadline.MAX_SAFE_TIMEOUT_S, 365 Tage außerhalb von Windows), weil ein größerer Wert time_t innerhalb von Thread.join / Lock.acquire überläuft und beim Live-Test jeden parallelen Tool-Batch unter macOS zum Absturz brachte. Der Hinweistext ist außerdem für Menschen formatiert – 300 s werden als „5 Minuten“ dargestellt, 90 s als „90 Sekunden“ –, sodass CLI, Tool-Ergebnis und Gateway-Karte alle dasselbe sagen.

Was du stattdessen tun solltest

Lies die aktuellen Werte statt zu raten:

hermes config get approvals.timeout
hermes config get approvals.mode

Wenn 300 s für deine Messaging-Plattformen zu knapp ist, erhöhe es – deaktiviere es nicht:

hermes config set approvals.timeout 1800

Leite unbeaufsichtigte Oberflächen nach Absicht, sodass sie sofort entscheiden, statt ein Zeitfenster auszusitzen, das niemand füllen wird:

hermes config set approvals.single_query_mode approve   # du vertraust deinen -q-Läufen
hermes config set approvals.unattended_mode deny        # Webhooks / API sollten blockieren

Blockiere die Befehle, die nie laufen sollten, mit einer Deny-Liste – es ist eine case-insensitive fnmatch-Glob-Liste, und ein Treffer wird auch im Yolo-Modus blockiert:

approvals:
  deny:
    - "rm -rf *"
    - "DROP DATABASE *"

Für die vollständige Semantik und eine Dry-Run-Möglichkeit, deine Regeln zu testen, siehe unseren Leitfaden für unbeaufsichtigte Approvals und den Approvals-Dry-Run-Walkthrough. Wenn du den standardmäßigen Smart-Modus fährst, hängt approvals.smart_policy deine eigenen Regeln an den System-Prompt des Prüfers an.

Das Fazit

In einem Satz: Diese Rücksetzung ist keine Regression – sie ist ein korrektes Problem, das dem falschen Mechanismus übergeben wurde. Ein Approval-Timeout ist ein Sicherheitsventil. Wenn dich die Abfragen stören, stimme die Dauer und den Modus ab; entferne nicht das Ventil.

Eine Korrektur unsererseits: Der gestrige Beitrag, Approval-Abfragen, die bis zur Antwort warten, beschrieb das Verhalten von #130167 zu jenem Zeitpunkt. Wir haben ihm eine Aktualisierungsnotiz hinzugefügt. Dass Upstream innerhalb eines Tages die Richtung umkehrt, ist das Ergebnis, das jener Beitrag hätte vorhersehen sollen und es nicht tat – genau deshalb versuchen wir, „gerade gemergt“ nicht als „endgültige Entscheidung“ zu behandeln.