{"repo":"NousResearch/hermes-agent","public_id":"7748","display_label":"streaming config precedence bugs and gateway fixes","summary":"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.","subsystem":"gateway","counts":{"members":10,"open_issues":0,"open_issues_distinct":0,"open_prs":0,"closed_issues":4,"merged_prs":2,"attempt_prs":4},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":9799},"resolution":{"state":"partially_resolved","by":"pr-9799,pr-121693"},"freshness":{"state":"verified","last_verified_at":"2026-09-24T17:01:38+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.15733333333333333},"fix_edges":[{"issue_number":6558,"pr_number":121693,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":8338,"pr_number":9799,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":53697,"pr_number":121693,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":58728,"pr_number":121693,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":119323,"pr_number":121693,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false}],"members":[{"kind":"issue","number":7748,"title":"Bug: streaming.enabled=false causes message delivery failure","url":null,"state":"closed"},{"kind":"issue","number":8338,"title":"[Bug]: (regression) `display.streaming` Enables Gateway Message Streaming Despite `streaming.enabled: false`","url":null,"state":"closed"},{"kind":"issue","number":10071,"title":"[Bug][DingTalk] global display.streaming overrides low-tier default and splits one reply into multiple messages","url":null,"state":"closed"},{"kind":"issue","number":53697,"title":"[Bug]: Telegram per-platform streaming default bypasses global streaming.enabled=false","url":null,"state":"closed"},{"kind":"pr","number":8347,"title":"fix(gateway): regression causing display.streaming to override root gateway streaming config ","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#9799","number":9799}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":8635,"title":"fix(gateway): respect streaming.enabled: false even when display.streaming is true (fixes #8338)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#9799","number":9799},{"kind":"pr","value":"#8347","number":8347}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":9799,"title":"fix(gateway): regression causing display.streaming to override root gateway streaming config","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["gateway/display_config.py","tests/gateway/test_display_config.py","tests/gateway/test_run_progress_topics.py"],"fix_hunks":[{"file":"gateway/display_config.py","hunks":["@@ -9,6 +9,10 @@\n     3. ``_PLATFORM_DEFAULTS[<platform>][<key>]``  — built-in sensible default\n     4. ``_GLOBAL_DEFAULTS[<key>]``              — built-in global default\n \n+Exception: ``display.streaming`` is CLI-only.  Gateway streaming follows the\n+top-level ``streaming`` config unless ``display.platforms.<platform>.streaming``\n+sets an explicit per-platform override.\n+\n Backward compatibility: ``display.tool_progress_overrides`` is still read as a\n fallback for ``tool_progress`` when no ``display.platforms`` entry exists.  A\n config migration (version bump) automatically moves the old format into the new","@@ -143,10 +147,13 @@ def resolve_display_setting(\n             if val is not None:\n                 return _normalise(setting, val)\n \n-    # 2. Global user setting (display.<key>)\n-    val = display_cfg.get(setting)\n-    if val is not None:\n-        return _normalise(setting, val)\n+    # 2. Global user setting (display.<key>).  Skip display.streaming because\n+    # that key controls only CLI terminal streaming; gateway token streaming is\n+    # governed by the top-level streaming config plus per-platform overrides.\n+    if setting != \"streaming\":\n+        val = display_cfg.get(setting)\n+        if val is not None:\n+            return _normalise(setting, val)\n \n     # 3. Built-in platform default\n     plat_defaults = _PLATFORM_DEFAULTS.get(platform_key)"]},{"file":"tests/gateway/test_display_config.py","hunks":["@@ -297,6 +297,15 @@ def test_none_means_follow_global(self):\n         result = resolve_display_setting(config, \"telegram\", \"streaming\")\n         assert result is None  # caller should check global StreamingConfig\n \n+    def test_global_display_streaming_is_cli_only(self):\n+        \"\"\"display.streaming must not act as a gateway streaming override.\"\"\"\n+        from gateway.display_config import resolve_display_setting\n+\n+        for value in (True, False):\n+            config = {\"display\": {\"streaming\": value}}\n+            assert resolve_display_setting(config, \"telegram\", \"streaming\") is None\n+            assert resolve_display_setting(config, \"discord\", \"streaming\") is None\n+\n     def test_explicit_false_disables(self):\n         \"\"\"Explicit False disables streaming for that platform.\"\"\"\n         from gateway.display_config import resolve_display_setting"]},{"file":"tests/gateway/test_run_progress_topics.py","hunks":["@@ -572,6 +572,27 @@ async def test_run_agent_streaming_does_not_enable_completed_interim_commentary(\n     assert not any(call[\"content\"] == \"I'll inspect the repo first.\" for call in adapter.sent)\n \n \n[redacted:email]\n+async def test_display_streaming_does_not_enable_gateway_streaming(monkeypatch, tmp_path):\n+    adapter, result = await _run_with_agent(\n+        monkeypatch,\n+        tmp_path,\n+        CommentaryAgent,\n+        session_id=\"sess-display-streaming-cli-only\",\n+        config_data={\n+            \"display\": {\n+                \"streaming\": True,\n+                \"interim_assistant_messages\": True,\n+            },\n+            \"streaming\": {\"enabled\": False},\n+        },\n+    )\n+\n+    assert result.get(\"already_sent\") is not True\n+    assert adapter.edits == []\n+    assert [call[\"content\"] for call in adapter.sent] == [\"I'll inspect the repo first.\"]\n+\n+\n @pytest.mark.asyncio\n async def test_run_agent_interim_commentary_works_with_tool_progress_off(monkeypatch, tmp_path):\n     adapter, result = await _run_with_agent(\n"]}]},{"kind":"pr","number":38921,"title":"fix(gateway): make streaming transport off absolute","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"c89265299a","number":0},{"kind":"pr","value":"#121693","number":121693},{"kind":"pr","value":"#53697","number":53697},{"kind":"pr","value":"#121693","number":121693}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":53709,"title":"fix: gate per-platform streaming behind global streaming.enabled (#53697)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"c89265299a","number":0},{"kind":"pr","value":"#121693","number":121693},{"kind":"pr","value":"#53697","number":53697}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":121693,"title":"fix(gateway): global streaming switch gates per-platform streaming; Matrix edits; keep transformed finals on failed edits (#53697, #58728)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["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"]}],"trust":{"default_tier":"verified","includes_suggested":false}}