Ihr Alarm landete im Gruppenchat — jetzt funktioniert eine Rückfrage dort tatsächlich


Es ist 2 Uhr nachts, die Deploy-Pipeline wird rot, und der Webhook schiebt den Alarm in Ihr Telegram. Sie sehen ihn am nächsten Morgen, antworten „ist das derselbe Fehler wie gestern?“ — und bekommen zurück: „who are you referring to?“ Sie hatten es ihm doch gerade gesagt. Es hat keine Ahnung. Das liegt nicht an einem langsamen Modell; so sind Webhook-Zustellungen konstruiert: Jedes Event läuft vollständig in seiner eigenen Session ab, und die zugestellte Nachricht existiert im Transkript des Ziel-Chats überhaupt nicht. Dieser Beitrag handelt von dem in v0.21.5 hinzugefügten Schalter — und davon, was er behebt und was nicht.

Woher die Lücke kommt

Eine Webhook-Route funktioniert so: Ein externer Dienst POSTet ein Event, die Payload wird über Ihr Template in einen Prompt gerendert, ein Agent-Run findet statt, und das Ergebnis wird in einen Chat zugestellt. Währenddessen ist die Session webhook:<route>:<delivery_id> ephemer — sie existiert für den Run und ist danach weg.

Wenn Sie in diesem Telegram-Chat mit dem Agenten sprechen, lädt er die Session dieses Chats. Die beiden Transkripte schneiden sich nie, deshalb:

  • weiß er nicht, dass er Ihnen gerade einen Alarm geschickt hat;
  • zeigt „der Fehler von gestern“ in seiner Historie auf nichts;
  • bleibt Ihnen nur, den Alarm erneut zu schildern oder die Rückfrage aufzugeben.

Den Schalter einschalten

Eine Route kann sich jetzt für das Mirroring entscheiden. Fügen Sie ein Flag dort hinzu, wo Sie das Abo anlegen oder aktualisieren:

hermes webhook subscribe ci-alerts \
  --prompt "build {route} failed: {payload}" \
  --deliver telegram \
  --mirror-to-session

Die Ausgabe des Subscribe bekommt eine Zeile:

  Created webhook subscription: ci-alerts
  URL:    https://…/webhooks/ci-alerts
  Profile: default
  Events: (all)
  Deliver: telegram
  Replies: each delivery is mirrored into the target chat's session

Bestehende Routen lassen sich stattdessen in der Konfiguration ändern:

platforms:
  webhook:
    extra:
      routes:
        ci-alerts:
          prompt: "build {route} failed: {payload}"
          deliver: telegram
          mirror_to_session: true

Die Wertprüfung ist hier bewusst streng: Nur ein echtes boolesches true schaltet ein. Der String "true" genügt nicht — das Mirroring schreibt in Ihre Unterhaltungshistorie, der Autor der Route muss es also ausdrücklich verlangen.

Was tatsächlich gespiegelt wird

Nicht die rohe HTTP-Anfrage. Die zugestellte Nachricht selbst, mit einem Label:

[Webhook delivery: ci-alerts]
build 4821 failed on main

Sie wird als User-Turn mit User-Autorität an die Session des Ziel-Chats angehängt — über denselben Codepfad und dieselbe Rollenkonvention wie fortsetzbare Cron-Briefs. Warum User und nicht Assistant? Weil dieser Text nicht der Agent der Ziel-Session ist, der spricht — es ist Ihnen etwas mitgeteilt worden. Als Assistant-Turn gespeichert entstünde daraus Assistant → Assistant in der Historie, was Provider mit strikter Alternierung schlicht bricht; als User gespeichert verschmelzen zwei aufeinanderfolgende User-Turns sicher, und die Form der Unterhaltung überlebt.

Der Gewinn: Sie antworten „ist das derselbe Fehler wie gestern?“, und der Agent sieht das Label und den zugestellten Text in seiner eigenen Historie — er kann also tatsächlich antworten.

Drei Grenzen, die Sie vor dem Aktivieren kennen sollten

1. Es ist Opt-in und standardmäßig aus

Routen ohne mirror_to_session verhalten sich exakt wie zuvor. Dieselbe Denkweise wie bei mirror_delivery im Cron: Es wird nichts in eine Session geschrieben, wenn es nicht verlangt wurde.

2. Es ist profilgebunden

Das Mirroring läuft innerhalb von _profile_scope(profile), es schreibt also in die state.db designigen Profils, dem die Route gehört. Dieses Detail ist nicht kosmetisch: Eine Telegram-DM-chat_id ist bei jedem Bot die User-ID derselben Person, ein nicht profilgebundenes Mirroring würde also in der DM eines anderen Profils mit derselben Person landen. Besonders bei Routen unter /p/<profile>/ sollte man das im Kopf behalten.

3. Es ist Best-Effort

Ein Mirroring-Fehler lässt niemals eine Zustellung scheitern — die Implementierung wirft nie —, und das Mirroring wird übersprungen, wenn der Ziel-Chat noch keine Gateway-Session hat (dort hat noch niemand mit dem Agenten gesprochen). Anders gesagt: Der Schalter garantiert keine Rückfrage. Wenn die Gruppe noch nie eine Unterhaltung mit dem Agenten hatte, gibt es keine Session, in die geschrieben werden könnte.

Wann Sie es nicht aktivieren sollten

Seien Sie vorsichtig bei deliver_only-Routen. Diese überspringen den Agenten vollständig — das gerenderte Template ist der wörtliche Nachrichteninhalt, bei null Token-Kosten —, der gespiegelte Text ist daher die roh gerenderte Payload.

Alles, was diesen Text beeinflussen kann, landet also als User-Turn in Ihrer Unterhaltungshistorie:

  • der Webhook eines öffentlichen Issue-Trackers: Was ein Fremder in einen Issue-Titel schreiben kann, kann in Ihrer Unterhaltungshistorie erscheinen;
  • rohe Alarmtexte eines Monitoring-Systems: Und das gilt, wenn sie nutzergesteuerte Felder tragen (Fehlermeldungen, Request-Pfade, User-Agent-Strings).

Die Regel ist einfach: Spiegeln Sie nur Quellen, deren Inhalt Sie vertrauen — Ihre eigenen Dienste, Ihre eigenen Pipelines. Bei allem, in das Dritte schreiben können, überlegen Sie zweimal.

Bestätigen, dass es greift

hermes webhook list

Die Liste zeigt die Details jeder Route, und beim Anlegen oder Aktualisieren eines Abos wird die Zeile Replies: each delivery is mirrored into the target chat's session ausgegeben.

Der günstigste Test für das echte Verhalten ist ein hermes webhook test-Event an die Route und anschließend die Frage im Ziel-Chat „Was hast du mir gerade geschickt?“ — antwortet er, statt zurückzufragen, funktioniert das Mirroring.

Wie es zum Rest passt

Dieser Fix und das Delivery-Ledger für doppelte Antworten sind zwei Seiten desselben Problems auf Gateway-Ebene: Ersteres verhindert, dass eine Antwort zweimal zugestellt wird, letzteres verhindert, dass eine Zustellung im Nachhinein für die Unterhaltung unsichtbar ist.

Wenn Sie bereits regelmäßige Prüfungen über Cron laufen lassen: Cron-Briefs haben das entsprechende Flag mirror_delivery schon länger. Webhook-Zustellungen ziehen jetzt nach, und beide Pfade verwenden dieselbe Konvention (ein Label plus ein User-Turn), sodass sich eine Rückfrage in beiden Fällen identisch anfühlt.

Die Kurzfassung

  • Das Standardverhalten ist unverändert: Webhook-Zustellungen sind fire-and-forget, und der Ziel-Chat weiß von nichts.
  • Mit mirror_to_session: true (oder --mirror-to-session) wird der zugestellte Inhalt als [Webhook delivery: <route>] plus ein User-Turn in die Session des Ziel-Chats geschrieben.
  • Drei Grenzen: standardmäßig aus, profilgebunden, Best-Effort (übersprungen, wenn keine Session existiert).
  • Bei deliver_only-Routen ist der gespiegelte Text die rohe Payload — aktivieren Sie es nur für Quellen, denen Sie vertrauen.

Dies ist in v0.21.5 (Tag v2026.9.24) ausgeliefert worden, als Teil jenes Patch-Fensters ohne Release Notes — welche anderen Dinge daraus bereits nutzbar sind, steht im Selbstcheck-Leitfaden zum v0.21.5-Fenster und auf unserer Release-Seite zu v0.21.5. Die vollständige Flag-Tabelle steht in der Befehlsreferenz.