hermes - 💡(How to fix) Fix [Feature]: Ambient / Blend-In Agent — Selective Response Gating for Gateway

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…

Fix Action

Fix / Workaround

Current workarounds and why they fail:

  • Strong system prompt + free_response_channels: \"*\": Works behaviorally, but still pays for a full LLM call on every message. In a busy channel, this is unsustainable.
  • Forking the Discord adapter as a user plugin: Survives hermes update, but requires maintaining a 6,000-line fork.
  • Gateway hooks (agent:start, agent:end, etc.): The existing hook system fires after the adapter has committed to responding. By agent:start, it's too late to gate.

Code Example

decisions = await self.hooks.emit_collect(\"message:received\", {
    \"platform\": source.platform.value if source.platform else \"\",
    \"chat_id\": channel_id,
    \"user_id\": author_id,
    \"message\": message_text,
    \"is_mention\": bool,
    \"is_bot\": bool,
    \"channel_name\": str,
})

for decision in decisions:
    if isinstance(decision, dict) and decision.get(\"action\") == \"ignore\":
        return  # silently drop; no agent invocation, no cost

---

discord:
  smart_response:
    enabled: true
    classifier_model: \"google/gemini-2.0-flash-lite\"
    classifier_prompt: |
      You are a gatekeeper. You see a Discord message. Decide: should the bot respond?
      The bot should ONLY respond if it has genuinely useful, funny, or insightful input.
      Reply with exactly one word: RESPOND or SILENT.
RAW_BUFFERClick to expand / collapse

Problem or Use Case

The Hermes Discord (and other platform) adapters have a binary response gate: either require_mention: true (bot only responds when @mentioned) or free_response_channels: \"*\" (bot responds to every message). There is no middle ground for an ambient, "blend-in" agent — one that passively observes all channel activity but only replies when it genuinely has something useful, funny, or insightful to add.

Users who want their Hermes bot to feel like a natural member of a human channel are forced into an awkward choice:

  1. require_mention: true — The bot is silent and only speaks when summoned. This feels robotic, not like a participant.
  2. free_response_channels: \"*\" — The bot responds to every message. In an active channel, this is spammy and expensive (every message triggers a full agent turn).

Use case:

"I want to place an Agent in a room with a bunch of humans and have it blend in. It should read all messages, build context, know the inside jokes, understand the ongoing topics — but only chime in when it actually has something valuable to add."

Current workarounds and why they fail:

  • Strong system prompt + free_response_channels: \"*\": Works behaviorally, but still pays for a full LLM call on every message. In a busy channel, this is unsustainable.
  • Forking the Discord adapter as a user plugin: Survives hermes update, but requires maintaining a 6,000-line fork.
  • Gateway hooks (agent:start, agent:end, etc.): The existing hook system fires after the adapter has committed to responding. By agent:start, it's too late to gate.

Proposed Solution

Add a new hook event and/or adapter configuration that enables selective response gating at the platform adapter level, before the agent is ever invoked.

Option A: New hook event message:received

Emit a hook before the adapter decides whether to build a MessageEvent and invoke the agent:


decisions = await self.hooks.emit_collect(\"message:received\", {
    \"platform\": source.platform.value if source.platform else \"\",
    \"chat_id\": channel_id,
    \"user_id\": author_id,
    \"message\": message_text,
    \"is_mention\": bool,
    \"is_bot\": bool,
    \"channel_name\": str,
})

for decision in decisions:
    if isinstance(decision, dict) and decision.get(\"action\") == \"ignore\":
        return  # silently drop; no agent invocation, no cost

A user-installed hook at ~/.hermes/hooks/ambient-gate/ could:

  • Run a cheap classification model (~$0.0003/msg) to decide "should I respond?"
  • Use channel memory / ongoing topics to gate responses
  • Be completely optional and update-safe

Option B: Adapter-level smart_response config

Add a new config section (e.g., discord.smart_response) that pairs a lightweight model with a system prompt for gating:

discord:
  smart_response:
    enabled: true
    classifier_model: \"google/gemini-2.0-flash-lite\"
    classifier_prompt: |
      You are a gatekeeper. You see a Discord message. Decide: should the bot respond?
      The bot should ONLY respond if it has genuinely useful, funny, or insightful input.
      Reply with exactly one word: RESPOND or SILENT.

Option C: Both (recommended)

  • message:received hook for power users who want custom logic
  • smart_response config for users who want a simple toggle

Why this needs a core change: The Discord adapter's _handle_message() gates messages before they ever reach the gateway runner. A hook that fires after agent:start is too late — the decision to respond has already been made. To enable true ambient behavior, the gate must move earlier in the message flow.


Alternatives Considered

  1. Config-only approach (require_mention: false + strong system prompt): Works but is expensive — every message triggers a full agent turn. In an active Discord channel, this is unsustainable.
  2. User plugin fork of Discord adapter: Survives hermes update but requires manually syncing 6,000 lines of upstream adapter code for every Discord API change or bugfix.
  3. Gateway agent:start hook: Fires too late — by the time this hook runs, the adapter has already passed all mention/channel gates and committed to responding.

The proposed message:received hook is the cleanest solution: ~10-15 lines of core change, zero impact on default behavior, and power users can implement arbitrary gating logic without forking adapters.


Feature Type

Gateway / messaging improvement


Scope

Medium (few files, ~300 lines)


Related Issues

  • #22344 — "Message interceptor hooks" (different use case: bypass agent entirely for simple tasks)
  • #5143 — "Multi-Role Auto-Routing via Gateway Hooks" (uses hooks for session switching, not response gating)
  • #14853 — "Multi-Agent Discord channel collaboration" (multi-agent history injection, no selective response)

Open Questions

  1. Hook location: Should message:received fire in gateway/run.py (universal across platforms) or in each platform adapter (platform-specific context)?
  2. Hook API shape: Simple \"ignore\" / \"respond\" or richer structure with confidence scores?
  3. Cost concern: Is a per-message classification call acceptable, or should there be a cheaper heuristic pre-filter (keyword/regex)?
  4. Memory integration: Should the hook context include access to persistent memory for better gating decisions?

I am willing to contribute a PR if the direction is approved. I have a working Hermes environment with Discord gateway active and can test against the Discord adapter specifically.

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

hermes - 💡(How to fix) Fix [Feature]: Ambient / Blend-In Agent — Selective Response Gating for Gateway