hermes-agent/tests/plugins/platforms
Teknium 46da6784aa feat(xchat): full feature parity — encrypted media, threaded replies, new-conversation handshake, key-event meta, read receipts
Brings the X Chat adapter to parity with mature gateway platforms:

- Encrypted media, both directions. Inbound attachments are downloaded
  (GET /2/chat/media/{conv}/{hash}), decrypted with the conversation key
  for the EVENT's key version (post-rotation media stays readable),
  size-capped, cached locally, and surfaced on MessageEvent
  (media_urls/media_types + correct MessageType) so vision/file tools
  see them. Outbound send_image/send_image_file/send_voice/send_video/
  send_document encrypt with the latest conversation key
  (encrypt_stream), upload via the 3-step chat-media flow
  (initialize/append/finalize, base64 JSON segments), and attach by
  media_hash_key. Standalone sends (cron/send_message_tool) carry
  media_files the same way.
- Native threaded replies. A bounded per-conversation cache of decrypted
  events lets send(reply_to=...) use encrypt_reply against the real
  target event; unknown targets fall back to a plain send. Inbound
  reply context (reply_to_message_id/text/author,
  reply_to_is_own_message) now propagates on MessageEvent.
- New-conversation initiation. Standalone send to a bare numeric user id
  performs the conversation-key handshake: fetch both parties' public
  keys, verify each identity↔signing binding (verify_key_binding — a
  substituted key must never receive the conversation key), wrap a
  fresh key per participant (prepare_conversation_key_change), POST to
  add-conversation-keys, then encrypt under the returned raw key.
- meta.conversation_key_events. The events endpoint returns KeyChange
  events SEPARATELY in meta — previously they were never decrypted, so
  conversations whose key changes fell outside the data array could
  never seed a key. Both the poll loop and the standalone sender now
  feed them through the batch decrypt path (after signing-key
  registration) before processing messages.
- Read receipts (opt-in, XCHAT_SEND_READ_RECEIPTS, default off) via
  POST /2/chat/conversations/{id}/read.
- Latest-key-version tracking per conversation for media encrypt and
  correct key selection after rotations.

api.py: media_upload (chunked 3-step), media_download, mark_read.
crypto.py: encrypt_reply, encrypt_media/decrypt_media, verify_key_binding,
prepare_conversation_key_change (SDK->API body mapping incl.
action_signatures), attachments/explicit-key support on encrypt_text,
latest_key_version surfaced from decrypt_events, message_attachments,
detect_mime_type/detect_image_dimensions helpers.

Docs: media/replies/handshake/read-receipts documented; stale "text
only" / "reply flows only" limitations removed; capability row added to
the messaging comparison table; media.write scope noted in setup + docs.

Tests: 51 total — inbound attachment decrypt-and-cache, outbound
encrypt-upload-attach (+ no-key failure), threaded-reply cache hit and
fallback, meta key-event absorption order, read-receipt opt-in/default,
reply-context propagation, full handshake happy path (bindings verified,
key change POSTed, explicit key used), chunked upload reassembly,
media download, mark-read body.
2026-08-12 17:16:41 -07:00
..
photon test: prune low-value tests suite-wide (wave 1) — 46,820 → 28,106 test functions 2026-07-29 13:10:23 -07:00
xchat feat(xchat): full feature parity — encrypted media, threaded replies, new-conversation handshake, key-event meta, read receipts 2026-08-12 17:16:41 -07:00
test_discord_gate_isolation.py test: importorskip discord.py in the slash-gate isolation test 2026-08-01 10:51:42 -07:00