# NousResearch/hermes-agent · streaming config precedence bugs and gateway fixes

## Open now

- Nothing open on GitHub.

Multiple bug reports and fixes around gateway streaming configuration, where display.streaming incorrectly overrides or ignores the global streaming.enabled setting. Affects multiple platforms including Telegram and DingTalk.

## TL;DR

- 10 member(s): 0 open issue(s), 4 closed issue(s), 0 open PR(s), 2 merged PR(s), 4 PR(s) closed without merging.
- A fix was merged to main in PR #9799. Verify it addresses your case.
- **Recommended:** read PR #9799 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-9799,pr-121693`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.16`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #7748: Bug: streaming.enabled=false causes message delivery failure
- #8338: \[Bug\]: (regression) \`display.streaming\` Enables Gateway Message Streaming Despite \`streaming.enabled: false\`
- #10071: \[Bug\]\[DingTalk\] global display.streaming overrides low-tier default and splits one reply into multiple messages
- #53697: \[Bug\]: Telegram per-platform streaming default bypasses global streaming.enabled=false

## Prior & competing PRs

- **#9799: fix(gateway): regression causing display.streaming to override root gateway streaming config**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `gateway/display_config.py`, `tests/gateway/test_display_config.py`, `tests/gateway/test_run_progress_topics.py`
  - fix hunks:
  - `gateway/display_config.py`

    ```diff
    @@ -9,6 +9,10 @@
         3. ``_PLATFORM_DEFAULTS[<platform>][<key>]``  — built-in sensible default
         4. ``_GLOBAL_DEFAULTS[<key>]``              — built-in global default
     
    +Exception: ``display.streaming`` is CLI-only.  Gateway streaming follows the
    +top-level ``streaming`` config unless ``display.platforms.<platform>.streaming``
    +sets an explicit per-platform override.
    +
     Backward compatibility: ``display.tool_progress_overrides`` is still read as a
     fallback for ``tool_progress`` when no ``display.platforms`` entry exists.  A
     config migration (version bump) automatically moves the old format into the new
    ```

    ```diff
    @@ -143,10 +147,13 @@ def resolve_display_setting(
                 if val is not None:
                     return _normalise(setting, val)
     
    -    # 2. Global user setting (display.<key>)
    -    val = display_cfg.get(setting)
    -    if val is not None:
    -        return _normalise(setting, val)
    +    # 2. Global user setting (display.<key>).  Skip display.streaming because
    +    # that key controls only CLI terminal streaming; gateway token streaming is
    +    # governed by the top-level streaming config plus per-platform overrides.
    +    if setting != "streaming":
    +        val = display_cfg.get(setting)
    +        if val is not None:
    +            return _normalise(setting, val)
     
         # 3. Built-in platform default
         plat_defaults = _PLATFORM_DEFAULTS.get(platform_key)
    ```
  - `tests/gateway/test_display_config.py`

    ```diff
    @@ -297,6 +297,15 @@ def test_none_means_follow_global(self):
             result = resolve_display_setting(config, "telegram", "streaming")
             assert result is None  # caller should check global StreamingConfig
     
    +    def test_global_display_streaming_is_cli_only(self):
    +        """display.streaming must not act as a gateway streaming override."""
    +        from gateway.display_config import resolve_display_setting
    +
    +        for value in (True, False):
    +            config = {"display": {"streaming": value}}
    +            assert resolve_display_setting(config, "telegram", "streaming") is None
    +            assert resolve_display_setting(config, "discord", "streaming") is None
    +
         def test_explicit_false_disables(self):
             """Explicit False disables streaming for that platform."""
             from gateway.display_config import resolve_display_setting
    ```
  - `tests/gateway/test_run_progress_topics.py`

    ```diff
    @@ -572,6 +572,27 @@ async def test_run_agent_streaming_does_not_enable_completed_interim_commentary(
         assert not any(call["content"] == "I'll inspect the repo first." for call in adapter.sent)
     
     
    [redacted:email]
    +async def test_display_streaming_does_not_enable_gateway_streaming(monkeypatch, tmp_path):
    +    adapter, result = await _run_with_agent(
    +        monkeypatch,
    +        tmp_path,
    +        CommentaryAgent,
    +        session_id="sess-display-streaming-cli-only",
    +        config_data={
    +            "display": {
    +                "streaming": True,
    +                "interim_assistant_messages": True,
    +            },
    +            "streaming": {"enabled": False},
    +        },
    +    )
    +
    +    assert result.get("already_sent") is not True
    +    assert adapter.edits == []
    +    assert [call["content"] for call in adapter.sent] == ["I'll inspect the repo first."]
    +
    +
     @pytest.mark.asyncio
     async def test_run_agent_interim_commentary_works_with_tool_progress_off(monkeypatch, tmp_path):
         adapter, result = await _run_with_agent(
    ```
- **#121693: fix(gateway): global streaming switch gates per-platform streaming; Matrix edits; keep transformed finals on failed edits (#53697, #58728)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `contributors/emails/[redacted:email]`, `gateway/config.py`, `gateway/run_turn.py`, `gateway/run_turn_runner.py`, `tests/gateway/test_run_progress_topics.py`, `website/docs/user-guide/messaging/wecom.md`
- **#8347: fix(gateway): regression causing display.streaming to override root gateway streaming config **. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#9799`
- **#8635: fix(gateway): respect streaming.enabled: false even when display.streaming is true (fixes #8338)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#9799`, `#8347`
- **#38921: fix(gateway): make streaming transport off absolute**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `c89265299a`, `#121693`, `#53697`, `#121693`
- **#53709: fix: gate per-platform streaming behind global streaming.enabled (#53697)**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `c89265299a`, `#121693`, `#53697`

## Fix status

- Merged to main in PR #9799 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-09-24`.
- Subsystem: `gateway`
