Referenz: gateway/platforms/base.py aus dem Hermes-Checkout (HEAD 7b25941,
identisch mit github.com/nousresearch/hermes-agent main).
Jeder Punkt: was die Basis anbietet, was chattolib kann, was implementiert wird.
edit_message — base.py:4035, Default SendResult(success=False, "Not supported")chattolib: client.update_message(room_id, event_id, body=...).
Aufrufer: gateway/stream_consumer.py:412 (Zeilen 1451, 1824, 2233) und
gateway/run.py:4530/28592/29493. Solange False zurückkommt, wird jedes
Streaming-Update als neue Nachricht gesendet.
finalize: bool = False (für Chatto ein
No-op — kein Rich-Card-Lifecycle, REQUIRES_EDIT_FINALIZE bleibt aus).MAX_MESSAGE_LENGTH → success=False zurückgeben statt
still zu kürzen, damit der Aufrufer auf send() (das splittet) zurückfällt.success=False, retryable=True._mark_seen(), damit das eigene message_edited-Event
nicht zurückläuft.delete_message — base.py:4064, Default Falsechattolib: client.delete_message(room_id, event_id) -> bool.
Genutzt vom Fresh-Final-Cleanup des Stream-Consumers und von
_schedule_ephemeral_delete (EphemeralReply-TTL). Durchreichen, Exceptions zu
False.
MessageEvent.media_urls / media_typesKein Override, sondern eine Lücke: _dispatch_message_posted verwirft
Nachrichten mit leerem body — ein reines Bild/PDF an Hermes verschwindet.
Message.attachments (list[MessageAttachment]) trägt asset_url.url
(vorsignierte URL), filename, content_type — kein get_asset-Roundtrip
nötig.validate_inbound_media_size prüfen,
durch cache_media_bytes(data, filename=…, mime_type=…) schicken — das ist
der geteilte Funnel aller Adapter (base.py:2212).media_urls = lokale Cache-Pfade, media_types = MIME, message_type nach
derselben Präzedenz wie Teams/Signal: DOCUMENT > PHOTO > VIDEO > AUDIO > TEXT.create_handoff_thread — base.py:4008, Default NoneSeed-Message in den Parent-Room posten, deren ID ist der Thread-Root
(dasselbe Muster wie Slack, adapter.py:2278 dort). Best-effort
follow_thread. Bei DM-Räumen None (Chatto-DMs können keine Threads).
send_document / send_video / send_voice — base.py:4718 / 4691 / 4545Defaults schicken „⚠️ Couldn't deliver …" als Text. Die Upload-Mechanik gibt es
schon (_upload_asset, chunked), sie hängt nur an send_image_file.
_send_local_attachment() aus send_image_file
herausziehen; die vier Methoden werden dünne Wrapper.validate_media_delivery_path() bleibt der Gate vor jedem Upload.send_multiple_images — base.py:4398Default sendet einzeln. Chatto-Nachrichten tragen mehrere Attachments:
alle hochladen, ein post_message mit attachment_asset_ids=[…].
Bei ≤1 Bild oder Upload-Fehler auf super() zurückfallen.
file://-URIs entquoten, http(s):// vorher herunterladen.
_reaction_handler (set_reaction_handler, base.py:3691)reaction_added/reaction_removed werden in _handle_realtime_event heute
explizit verworfen. ReactionPayload (room_id, message_event_id, emoji)
ist fertig dekodiert.
platform, event_name
(reaction:added / reaction:removed), reaction, user_id,
item_user_id, item_type, channel_id, message_ts, event_ts,
raw_event.getattr-Guard wie bei Slack.message_edited/message_retracted in die Known-Kinds-Liste,
damit sie nicht als unknown event kind geloggt werden.format_message — base.py:7235, Default = IdentitätBewusst minimal: Chatto rendert Markdown nativ, es gibt nichts zu escapen.
Implementiert wird nur, was messbar schiefgeht — CRLF-Normalisierung und das
Kappen von >2 Leerzeilen am Stück. Zusätzlich der hasattr-Aufruf in send()
(adapter.py:681) durch einen direkten Aufruf ersetzt, der heute effektiv ein
No-op ist. Dünnster Punkt der Liste — mehr wäre Spekulation über
Chatto-Renderer-Details, die sich real-testen lässt.
Alle in test_adapter.py, gleicher Stil (AsyncMock-Client, keine Netzwerk-IO):
TestMessageEditing: die beiden xfail(strict=True)-Marker entfernen,
ergänzen: Overlong-Content → success=False, chattolib-Fehler → retryable.TestInboundAttachments: Bild-Attachment → media_urls/media_types
gesetzt, message_type == PHOTO; Body leer + Attachment → nicht verworfen;
Body leer + kein Attachment → weiterhin verworfen; Download-Fehler → Event
trotzdem zugestellt.TestHandoffThread: Room → Seed-ID; DM → None.TestNativeSends: send_document/send_video/send_voice laden hoch und
posten mit attachment_asset_ids; unsicherer Pfad → Fallback-Text.TestSendMultipleImages: zwei lokale Dateien → ein post_message mit zwei
Asset-IDs.TestReactionForwarding: Fremd-Reaktion → Handler mit korrektem Dict;
Selbst-Reaktion → kein Handler-Aufruf; Handler-Exception → kein Absturz.TestFormatMessage: CRLF und Leerzeilen.Lauf: HERMES_ROOT=/tmp/hermes uv run --group dev --with pyyaml pytest -q
(das Plugin hängt nicht am Agent; HERMES_ROOT zeigt auf ein Checkout).
send_draft/supports_draft_streaming (Telegram-Draft-API, kein Chatto-Pendant),
Streaming-TTS, enforces_own_access_policy (Security-Entscheidung, gehört
separat besprochen — siehe Punkt 9 der Analyse).