hermes - 💡(How to fix) Fix [Feature]: Allow agents to opt out of replying with `[SILENT]` marker

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

In group channels and multi-agent conversations, there are situations where an agent has nothing meaningful to add but still receives inbound messages that trigger a turn. Currently the agent must always produce some response — even if it's just a placeholder — because there's no mechanism to explicitly skip delivery.

Fix Action

Fix / Workaround

Patch:

Code Example

--- gateway/platforms/base.py.orig	2026-05-27 00:46:47.402790707 +0300
+++ gateway/platforms/base.py	2026-05-27 00:46:54.331714573 +0300
@@ -23,6 +23,12 @@
 
 logger = logging.getLogger(__name__)
 
+# Sentinel: when the agent's response is exactly this marker, delivery is
+# suppressed. The agent can use this to opt out of replying (e.g. in group
+# channels when there's nothing meaningful to add). Output is still persisted
+# in the transcript for continuity.
+SILENT_MARKER = "[SILENT]"
+
 # Audio file extensions Hermes recognizes for native audio delivery.
 # Kept in sync with tools/send_message_tool.py and cron/scheduler.py via
 # should_send_media_as_audio() below.
@@ -3081,6 +3087,15 @@
             # post-send block can schedule the deletion.
             response, _ephemeral_ttl = self._unwrap_ephemeral(response)
 
+            # [SILENT] marker: agent opted out of replying. Suppress delivery
+            # but keep the transcript entry for continuity.
+            if response and response.strip() == SILENT_MARKER:
+                logger.info(
+                    "[%s] Agent returned %s — suppressing delivery for %s",
+                    self.name, SILENT_MARKER, event.source.chat_id,
+                )
+                response = None
+
             # Send response if any.  A None/empty response is normal when
             # streaming already delivered the text (already_sent=True) or
             # when the message was queued behind an active agent.  Log at

---
RAW_BUFFERClick to expand / collapse

Problem or Use Case

In group channels and multi-agent conversations, there are situations where an agent has nothing meaningful to add but still receives inbound messages that trigger a turn. Currently the agent must always produce some response — even if it's just a placeholder — because there's no mechanism to explicitly skip delivery.

This is especially relevant when:

  • Multiple AI agents share a channel and not every agent needs to respond to every message
  • An agent is present as a listener/observer rather than an active participant
  • The agent determines there's nothing of value to contribute to a particular exchange

The cron subsystem already supports this pattern via the [SILENT] marker (cron/scheduler.py line 1754), but the agent chat pipeline has no equivalent.

Proposed Solution

Add a [SILENT] sentinel check in gateway/platforms/base.py within _process_message_background(). When the agent's final response is exactly [SILENT] (after stripping whitespace), suppress delivery to the messaging platform while preserving the transcript entry for continuity.

Implementation: Two surgical edits to gateway/platforms/base.py:

  1. Add SILENT_MARKER = "[SILENT]" constant near the top of the file
  2. Add the check after _unwrap_ephemeral() — if response matches the marker, log it and set response = None

The transcript entry is preserved so context isn't lost for future turns. The agent can still use tool calls during the turn — only the final text response is suppressed.

Patch:

--- gateway/platforms/base.py.orig	2026-05-27 00:46:47.402790707 +0300
+++ gateway/platforms/base.py	2026-05-27 00:46:54.331714573 +0300
@@ -23,6 +23,12 @@
 
 logger = logging.getLogger(__name__)
 
+# Sentinel: when the agent's response is exactly this marker, delivery is
+# suppressed. The agent can use this to opt out of replying (e.g. in group
+# channels when there's nothing meaningful to add). Output is still persisted
+# in the transcript for continuity.
+SILENT_MARKER = "[SILENT]"
+
 # Audio file extensions Hermes recognizes for native audio delivery.
 # Kept in sync with tools/send_message_tool.py and cron/scheduler.py via
 # should_send_media_as_audio() below.
@@ -3081,6 +3087,15 @@
             # post-send block can schedule the deletion.
             response, _ephemeral_ttl = self._unwrap_ephemeral(response)
 
+            # [SILENT] marker: agent opted out of replying. Suppress delivery
+            # but keep the transcript entry for continuity.
+            if response and response.strip() == SILENT_MARKER:
+                logger.info(
+                    "[%s] Agent returned %s — suppressing delivery for %s",
+                    self.name, SILENT_MARKER, event.source.chat_id,
+                )
+                response = None
+
             # Send response if any.  A None/empty response is normal when
             # streaming already delivered the text (already_sent=True) or
             # when the message was queued behind an active agent.  Log at

Limitation: Streaming mode sends chunks as they arrive, so the [SILENT] text would appear before the check runs. This works for non-streaming responses (the default for most platforms). Streaming support would require buffering the first chunk and deciding whether to flush or discard — a follow-up enhancement.

Note: Direct send_message tool calls bypass this mechanism entirely — they post to the platform immediately. The [SILENT] marker only works for the gateway's final_response delivery path.

Alternatives Considered

  • Returning empty string / None — Already handled by the existing if response gate, but ambiguous. An empty response could mean "the agent produced nothing" vs "the agent chose not to reply." [SILENT] makes intent explicit.
  • Configuration toggle — A per-agent or per-channel config to auto-suppress responses would be less flexible. The agent should decide case-by-case.
  • Special tool call — Adding a skip_response tool would work but adds unnecessary overhead for a simple opt-out.

Feature Type

Gateway / messaging improvement

Scope

None

Contribution

  • I'd like to implement this myself and submit a PR

Debug Report (optional)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING