Skip to content

feat(matrix): opt-in progressive streaming + Beeper onboarding - #41090

Open
cjroth wants to merge 1 commit into
NousResearch:mainfrom
cjroth:feat/matrix-progressive-streaming-beeper
Open

cjroth wants to merge 1 commit into
NousResearch:mainfrom
cjroth:feat/matrix-progressive-streaming-beeper

Conversation

@cjroth

@cjroth cjroth commented Jun 7, 2026

Copy link
Copy Markdown

What does this PR do?

On Matrix, response streaming is hardcoded to buffer-only mode (run.py sets _buffer_only = True for Platform.MATRIX at
both stream-consumer sites). That means the bot edits its reply only at segment breaks (paragraph boundaries) and on
completion — not on the time/character cadence used by other edit-capable platforms. The result: short,
single-paragraph replies appear all at once, reading as "no streaming," and there's no config knob to change it.

This PR adds a streaming.matrix_progressive option (default false, so existing behavior is unchanged) that lets Matrix
stream progressively via token-cadence m.replace edits. The streaming cursor stays suppressed on Matrix in both modes,
so you get a live-typing feel without the tofu/white-box glyph some Matrix clients render for the cursor.

I made this opt-in rather than flipping the default on purpose: the buffer-only default is deliberate (it keeps
m.replace edit traffic low and avoids the cursor artifact), so opt-in preserves current behavior for everyone while
letting operators who want live streaming turn it on and tune edit_interval / buffer_threshold for their homeserver's
rate limits.

It also documents connecting via Beeper (a hosted Matrix homeserver), since Beeper has no password login and trips
people up: a stdlib-only scripts/beeper_login.py helper that mints an access token through Beeper's
org.matrix.login.jwt flow, plus a "Connect via Beeper" guide and a "Response Streaming" section in the Matrix docs.

Related Issue

No existing issue tracks this specifically. It's adjacent to the Matrix streaming work in #37931. Happy to open a
tracking issue if preferred.

Fixes #

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • gateway/config.py — add StreamingConfig.matrix_progressive (default False); include in to_dict() / from_dict() with
    bool coercion.
  • gateway/run.py — honor the flag at both Matrix stream-consumer sites: _buffer_only = not _scfg.matrix_progressive
    (cursor stays suppressed).
  • hermes_cli/config.py — add matrix_progressive to DEFAULT_CONFIG["streaming"] so it appears in the generated
    dashboard/config schema.
  • cli-config.yaml.example — document the new key under streaming:.
  • website/docs/user-guide/messaging/matrix.md — new "Response Streaming" and "Connect via Beeper" sections.
  • scripts/beeper_login.py — new stdlib-only helper to mint a Beeper Matrix access token.
  • README.md — add Matrix (and a Beeper link) to the messaging-platforms line.
  • tests/gateway/test_config.py — defaults + coercion + round-trip tests for the new field.

How to Test

  1. In ~/.hermes/config.yaml, set:
    streaming:
    enabled: true
    matrix_progressive: true
  2. Run hermes gateway connected to a Matrix homeserver and DM the bot a prompt that yields a multi-sentence reply (e.g.
    "write a short paragraph about otters"). The reply should grow progressively via in-place edits, with no cursor glyph.
    With matrix_progressive: false (default) the same reply updates only at paragraph breaks.
  3. Config round-trip: pytest tests/gateway/test_config.py::TestStreamingConfig -q.

▎ Verified the progressive path live against Beeper (matrix.beeper.com) on Debian: replies stream via m.replace edits
▎ with the cursor suppressed (no tofu artifact).

Checklist

Code

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A (no architecture
    change)
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide
    (https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md#cross-platform-compatibility) — or N/A
    (pure-Python config + stdlib-only urllib helper)
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A (no tool changes)

Screenshots / Logs

  • None

Matrix streams buffer-only (edit only at segment breaks / completion) by
default, so short single-paragraph replies appear all at once. Add a
streaming.matrix_progressive option (default false, preserving current
behavior) that lets Matrix stream progressively via token-cadence m.replace
edits, with the cursor still suppressed to avoid the tofu/white-box glyph
some Matrix clients render.

Also document connecting via Beeper (a hosted Matrix homeserver): a
stdlib-only scripts/beeper_login.py helper that mints an access token via
Beeper's org.matrix.login.jwt flow, plus a 'Connect via Beeper' guide and a
'Response Streaming' section in the Matrix docs, and list Matrix in the
README platform line.

- gateway/config.py: StreamingConfig.matrix_progressive (+ to_dict/from_dict)
- gateway/run.py: honor it at both Matrix streaming sites
- hermes_cli/config.py: surface it in DEFAULT_CONFIG (dashboard schema)
- cli-config.yaml.example: document the new key
- tests/gateway/test_config.py: defaults + coercion + roundtrip

Signed-off-by: Chris Roth <chris@cjroth.com>
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery platform/matrix Matrix adapter (E2EE) labels Jun 7, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for preserving the existing Matrix default while making the progressive path explicit. The premise is confirmed on current main: Matrix still forces buffer_only=True in both stream-consumer setup paths (gateway/run.py:16756 and gateway/run.py:18089).

Problems

  • The PR changes both runtime branches (gateway/run.py:16841, gateway/run.py:17847), but its tests only cover StreamingConfig round-tripping. GatewayStreamConsumer uses buffer_only to suppress the interval/threshold edit path (gateway/stream_consumer.py:625-639), so add regression coverage for both branches.
  • scripts/beeper_login.py:40 adds a separate Beeper-specific authentication flow using BEEPER-PRIVATE-API-PLEASE-DONT-USE. This vendor onboarding concern is independent from Matrix streaming and should be evaluated separately.

Suggested changes

  • Exercise Matrix with the flag both off and on in each stream-consumer construction path, asserting the cursor remains empty and buffer_only flips as intended.
  • Split the Beeper helper/docs into a separate change; add mocked HTTP-flow coverage if it remains in-tree.

Automated hermes-sweeper review.

Comment thread gateway/run.py
# Buffer-only (edit at segment breaks only) unless the
# operator opts into progressive streaming. Cursor stays
# suppressed either way to avoid the tofu-glyph artifact.
_buffer_only = not _scfg.matrix_progressive

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a runner-level regression test for this branch and the run_sync sibling: Matrix with the flag off must remain buffer-only, while the flag on must set buffer_only=False and retain the empty cursor. The added config tests do not exercise either runtime path.

Comment thread scripts/beeper_login.py
# Beeper's own clients and every community login script) — it is NOT a secret and
# NOT account-specific; it only gates the unauthenticated login endpoints.
BEEPER_API = "https://api.beeper.com"
BEEPER_BEARER = "BEEPER-PRIVATE-API-PLEASE-DONT-USE"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split this Beeper-specific authentication helper from the streaming change so its external API contract and maintenance scope can be reviewed independently.

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/streaming Streaming responses: gateway delivery, provider wire labels Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/streaming Streaming responses: gateway delivery, provider wire comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have platform/matrix Matrix adapter (E2EE) sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants