# NousResearch/hermes-agent · Suppress noisy gateway status messages, config flags

## Open now

- 2 issues, 1 PR open on GitHub.
- #19572: Add config flag to suppress lifecycle status messages on messaging gateways
- #60604: \[Bug\]: compact context fail
- PR #20040: fix(gateway): gate lifecycle status messages

These issues and PRs concern suppressing internal operational noise (status updates, retry messages, lifecycle events) from leaking to users across chat gateways (WhatsApp, Discord, Slack, Signal, Telegram), plus related config options and some compression/retry fixes.

## TL;DR

- 11 member(s): 2 open issue(s), 4 closed issue(s), 1 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #53316. Verify it addresses your case.
- **Recommended:** read PR #53316 (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: `unresolved`. 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.14`: 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.

## Open related issues

- #19572: Add config flag to suppress lifecycle status messages on messaging gateways
- #60604: \[Bug\]: compact context fail

## Closed / duplicate issues

- #27131: \[Feature\] Add config option to suppress fallback/rate-limit status messages from gateway channel
- #39293: \[Bug\]: Gateway status/error sanitization is Telegram-only — WhatsApp/Discord/Slack/Signal leak internal noise to users
- #63554: Add config flags to suppress '⏳ Working' and '💾 Self-improvement review' system messages on chat platforms
- #63555: Add config flags to suppress 'Working' and 'Self-improvement review' system messages on chat platforms

## Prior & competing PRs

- **#53316: fix(gateway): suppress operational status/error noise on all chat gateways (#39293)**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `gateway/run.py`, `tests/gateway/test_telegram_noise_filter.py`
  - fix hunks:
  - `gateway/run.py`

    ```diff
    @@ -87,6 +87,22 @@
         re.IGNORECASE | re.DOTALL,
     )
     
    +# Surfaces that consume gateway text programmatically (CLI/TUI "local"
    +# diagnostics, API JSON, webhook payloads) and therefore must keep RAW
    +# status/error text. EVERY other platform is a human-facing chat surface
    +# where operational lifecycle/provider-error noise (and any secrets in it)
    +# must be suppressed or sanitized. Widens #28533's Telegram-only filter to
    +# all chat gateways (#39293). Fail-closed: unknown/empty platform -> chat.
    +_GATEWAY_RAW_TEXT_PLATFORMS = frozenset(
    +    {"local", "api_server", "webhook", "msgraph_webhook"}
    +)
    +
    +
    +def _gateway_surface_passes_raw_text(platform: Any) -> bool:
    +    """True only for programmatic/local surfaces that must keep raw text."""
    +    return _gateway_platform_value(platform) in _GATEWAY_RAW_TEXT_PLATFORMS
    +
    +
     _GATEWAY_PROVIDER_ERROR_RE = re.compile(
         r"("  # infrastructure/provider error preambles, not ordinary assistant prose
         r"api\s+(?:call\s+)?failed"
    ```

    ```diff
    @@ -370,15 +386,18 @@ def _looks_like_gateway_provider_error(text: str) -> bool:
     
     
     def _sanitize_gateway_final_response(platform: Any, text: str) -> str:
    -    """Sanitize final gateway replies before sending them to high-noise chats.
    -
    -    Telegram is Bob's mobile inbox, so it should receive concise, safe provider
    -    failure categories instead of raw HTTP bodies, request IDs, or policy text.
    -    Other platforms keep the existing behaviour for now.
    +    """Sanitize final gateway replies before sending them to chat surfaces.
    +
    +    Every human-facing chat surface (Telegram, WhatsApp, Discord, Slack,
    +    Signal, Matrix, plugin platforms, etc.) should receive concise, safe
    +    provider failure categories with secrets redacted instead of raw HTTP
    +    bodies, request IDs, leaked credentials, or policy text. Only programmatic
    +    surfaces in ``_GATEWAY_RAW_TEXT_PLATFORMS`` (CLI/TUI ``local`` diagnostics,
    +    API JSON, webhook payloads) keep the raw text unchanged.
         """
         if not text:
             return text
    -    if _gateway_platform_value(platform) != "telegram":
    +    if _gateway_surface_passes_raw_text(platform):
             return text
     
         redacted = _redact_gateway_user_facing_secrets(str(text))
    ```

    ```diff
    @@ -392,7 +411,7 @@ def _prepare_gateway_status_message(platform: Any, event_type: str, message: str
         text = str(message or "").strip()
         if not text:
             return None
    -    if _gateway_platform_value(platform) != "telegram":
    +    if _gateway_surface_passes_raw_text(platform):
             return text
     
         text = _redact_gateway_user_facing_secrets(text)
    ```
  - `tests/gateway/test_telegram_noise_filter.py`

    ```diff
    @@ -1,4 +1,6 @@
    -"""Telegram-specific gateway filtering for noisy status/error output."""
    +"""Gateway noise/secret filtering across chat surfaces (Telegram + siblings)."""
    +
    +import pytest
     
     from gateway.config import Platform
     from gateway.run import (
    ```

    ```diff
    @@ -6,6 +8,34 @@
         _sanitize_gateway_final_response,
     )
     
    +# Every human-facing chat surface that must receive noise-filtered,
    +# secret-redacted, provider-error-sanitized output (not just Telegram).
    +CHAT_PLATFORMS = [
    +    "telegram",
    +    "whatsapp",
    +    "discord",
    +    "slack",
    +    "signal",
    +    "matrix",
    +    "mattermost",
    +    "dingtalk",
    +    "feishu",
    +    "wecom",
    +    "weixin",
    +    "bluebubbles",
    +    "qqbot",
    +    "homeassistant",
    +    "sms",
    +]
    +
    +NOISY_STATUS_MESSAGES = [
    +    "🗜️ Preflight compression check before sending...",
    +    "🗜️ Compacting context — summarizing earlier conversation so I can continue...",
    +    "⚠ Compression summary failed: upstream error. Inserted a fallback context marker.",
    +    "⏱️ Rate limited. Waiting 30.0s (attempt 2/3)...",
    +    "⏳ Retrying in 4.2s (attempt 1/3)...",
    +]
    +
     
     def test_telegram_status_suppresses_auxiliary_and_retry_noise():
         """Auxiliary failures and retry backoff chatter should not hit Telegram."""
    ```

    ```diff
    @@ -23,12 +53,91 @@ def test_telegram_status_suppresses_auxiliary_and_retry_noise():
             assert _prepare_gateway_status_message(Platform.TELEGRAM, "warn", message) is None
     
     
    -def test_non_telegram_status_is_unchanged():
    -    """The Telegram quieting policy must not hide CLI/Discord diagnostics."""
    +def test_programmatic_surfaces_keep_raw_status():
    +    """Programmatic surfaces (local/api/webhook) must keep raw diagnostics.
    +
    +    Negative case for the invariant: the chat-noise filter must not touch
    +    CLI/TUI diagnostics, API JSON, or webhook payloads.
    +    """
         message = "⏳ Retrying in 4.2s (attempt 1/3)..."
     
    -    assert _prepare_gateway_status_message(Platform.DISCORD, "lifecycle", message) == message
    -    assert _prepare_gateway_status_message("local", "lifecycle", message) == message
    +    for platform in ("local", "api_server", "webhook", "msgraph_webhook"):
    +        assert (
    +            _prepare_gateway_status_message(platform, "lifecycle", message) == message
    +        )
    +
    +
    [redacted:email]("platform", CHAT_PLATFORMS)
    [redacted:email]("message", NOISY_STATUS_MESSAGES)
    +def test_all_chat_gateways_suppress_noise(platform, message):
    +    """Operational lifecycle/retry noise must be suppressed on every chat surface."""
    +    assert _prepare_gateway_status_message(platform, "warn", message) is None
    +
    +
    [redacted:email]("platform", ["whatsapp", "slack", "signal", "matrix"])
    +def test_chat_gateways_redact_secret_in_provider_error(platform):
    +    """Provider-error bodies carrying secrets must never reach chat users.
    +
    +    THE security invariant being widened from Telegram (#28533) to all chat
    +    surfaces (#39293): a leaked bearer token in a provider error body must be
    +    redacted/replaced before delivery on any chat platform.
    +    """
    +    raw = (
    +        "API call failed after 3 retries: HTTP 401 Unauthorized — "
    +        "Authorization: Bearer [redacted:openai-key]"
    +    )
    +
    +    sanitized = _sanitize_gateway_final_response(platform, raw)
    +
    +    assert "[redacted:openai-key]" not in sanitized
    +    assert "sk-ABCDEF" not in sanitized
    +    assert "HTTP 401" not in sanitized
    +    # The user gets the safe provider-error category instead of the raw body.
    +    assert "provider" in sanitized.lower()
    +
    +
    [redacted:email]("platform", ["whatsapp", "slack", "signal", "matrix"])
    +def test_chat_gateways_redact_secret_in_non_error_body(platform):
    +    """Secrets must be redacted even when no provider-error rewrite fires.
    +
    +    The provider-error case above is rewritten wholesale to a generic
    +    category string, so it cannot, on its own, prove the secret-redaction
    +    layer works — the rewrite would strip the body regardless. This case
    +    feeds ordinary assistant prose that merely *echoes* a bearer token (not
    +    a provider-error envelope), so `_redact_gateway_user_facing_secrets` is
    +    the only thing standing between the token and the user. Removing the
    +    redaction patterns makes this fail (genuine regression guard); the
    +    surrounding prose must survive intact.
    +    """
    +    raw = (
    +        "Sure — here is the example request you asked for: "
    +        "curl -H 'Authorization: Bearer [redacted:openai-key]' "
    +        "https://api.example.com/v1/models"
    +    )
    +
    +    sanitized = _sanitize_gateway_final_response(platform, raw)
    +
    +    assert "[redacted:openai-key]" not in sanitized
    +    assert "sk-ABCDEF" not in sanitized
    +    assert "[REDACTED]" in sanitized
    +    # Non-secret prose is preserved — redaction is surgical, not a wholesale
    +    # rewrite, on bodies that are not provider-error envelopes.
    +    assert "here is the example request you asked for" in sanitized
    +
    +
    +def test_plugin_platform_string_suppresses_noise():
    +    """Unknown/plugin chat platforms fail closed to the chat-filter path."""
    +    message = "⏳ Retrying in 4.2s (attempt 1/3)..."
    +
    +    assert _prepare_gateway_status_message("irc", "warn", message) is None
    +
    +
    [redacted:email]("platform", CHAT_PLATFORMS)
    +def test_chat_gateways_keep_normal_answers(platform):
    +    """Normal assistant content must pass through unchanged on chat surfaces."""
    +    answer = "Here is the clean summary you asked for."
    +
    +    assert _sanitize_gateway_final_response(platform, answer) == answer
     
     
     def test_telegram_status_sanitizes_raw_provider_security_errors():
    ```
- **#20040: fix(gateway): gate lifecycle status messages**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `gateway/display_config.py`, `gateway/run.py`, `hermes_cli/config.py`, `tests/gateway/test_display_config.py`, `tests/gateway/test_run_progress_topics.py`
- **#27134: feat(logging): add config option to suppress fallback/rate-limit status messages**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#4242`, `#4242`, `#4242`, `#4242`, `#4242`
- **#39323: fix(gateway): suppress operational status/error noise on all chat gateways, not just Telegram (#39293)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `57864d07e`, `c377e954f`, `#53316`
- **#60744: fix(compression): raise timeout floor from 300s to 900s for slow local models (#60604)**. closed without merge
  - maintainer direction: category `stale_abandoned`; implication `active`

## Fix status

- Merged to main in PR #53316 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-26`.
- Subsystem: `agent`
