hermes - 💡(How to fix) Fix [Feature] Add pre_gateway_text_send plugin hook for outbound message interception

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

Currently, the plugin system provides pre_gateway_dispatch as an inbound hook — it fires before the gateway processes an incoming user message, allowing plugins to skip, rewrite, or allow the message. However, there is no corresponding hook for outbound messages. Once the agent generates its final response text, it flows directly to the platform adapter and is delivered to the user without any opportunity for plugins to inspect or transform it.

This is the outbound counterpart to the existing pre_gateway_dispatch hook:

  • pre_gateway_dispatch — fires on inbound user messages (before agent processing). Already implemented in gateway/run.py.
  • pre_gateway_text_send — fires on outbound agent responses (after agent processing, before delivery). Proposed here.

Code Example

invoke_hook(
    "pre_gateway_text_send",
    platform=platform,       # e.g. "telegram", "discord"
    session_id=session_id,   # session identifier (may be None)
    chat_id=chat_id,         # platform-specific chat identifier
    text=text,               # the response text about to be sent
    mode=mode,               # "streaming" or "non_streaming"
    is_edit=is_edit,         # True if editing an existing message
    finalize=finalize,       # True for the final send in a sequence
)
RAW_BUFFERClick to expand / collapse

Problem

Currently, the plugin system provides pre_gateway_dispatch as an inbound hook — it fires before the gateway processes an incoming user message, allowing plugins to skip, rewrite, or allow the message. However, there is no corresponding hook for outbound messages. Once the agent generates its final response text, it flows directly to the platform adapter and is delivered to the user without any opportunity for plugins to inspect or transform it.

This gap means there is no plugin-level extension point for:

  • Content policy enforcement — blocking or rewriting responses that violate usage guidelines
  • PII scrubbing — stripping phone numbers, emails, or other sensitive data before delivery
  • Fact-checking / citation injection — annotating responses with sources or confidence flags
  • Response formatting — applying consistent formatting, watermarking, or branding
  • Audit logging — recording outbound messages for compliance without modifying core gateway code

Proposed Solution

Add a new plugin hook: pre_gateway_text_send

This hook fires immediately before a text response is delivered to the platform adapter — the last chance to inspect, rewrite, or suppress the final text the user will see.

Hook name

pre_gateway_text_send

Where it fires

The hook should be invoked in two places:

  1. Non-streaming pathgateway/platforms/base.py, in BasePlatformAdapter._deliver_text(), just before the call to _send_with_retry(). At this point the full response text is available as a single string.

  2. Streaming pathgateway/stream_consumer.py, in GatewayStreamConsumer.run(), at the got_done block where the final accumulated text is about to be delivered. The hook runs on the fully assembled text (after think-block filtering and MEDIA: tag cleanup), not on individual streaming deltas.

Return contract

Plugins implementing pre_gateway_text_send receive:

invoke_hook(
    "pre_gateway_text_send",
    platform=platform,       # e.g. "telegram", "discord"
    session_id=session_id,   # session identifier (may be None)
    chat_id=chat_id,         # platform-specific chat identifier
    text=text,               # the response text about to be sent
    mode=mode,               # "streaming" or "non_streaming"
    is_edit=is_edit,         # True if editing an existing message
    finalize=finalize,       # True for the final send in a sequence
)

Plugins return one of:

Return valueEffect
None or {"action": "allow"}Text passes through unchanged
{"action": "rewrite", "text": "..."}Replace the response text with the provided string
{"action": "block", "text": "..."}Block the original response; send the provided text as a fallback/explanation

If multiple plugins are registered, the first rewrite or block result wins (last-write-wins across results is also acceptable; the key requirement is determinism). Errors in hook invocation should be caught and logged without blocking delivery.

Relationship to existing hooks

This is the outbound counterpart to the existing pre_gateway_dispatch hook:

  • pre_gateway_dispatch — fires on inbound user messages (before agent processing). Already implemented in gateway/run.py.
  • pre_gateway_text_send — fires on outbound agent responses (after agent processing, before delivery). Proposed here.

Together they give plugins a complete interception layer: inspect/rewrite what comes in, and inspect/rewrite what goes out.

Implementation notes

  • A shared helper function (e.g., _apply_text_hooks()) in gateway/platforms/base.py would keep both paths DRY.
  • The streaming path should only invoke the hook on the final assembled text, not on progressive edit deltas, to avoid performance overhead and partial-text mutations.
  • The gateway startup sequence (gateway/run.py) should ensure plugins are loaded before the message loop starts so hooks are available from the first message.

Use cases

  1. PII redaction plugin: Scans outgoing text for phone numbers, SSNs, API keys and replaces them with [REDACTED].
  2. Content policy plugin: Checks responses against a classifier and blocks or rewrites policy-violating content.
  3. Citation plugin: Appends source links or footnotes to responses that reference retrieved documents.
  4. Audit plugin: Logs all outbound messages to an external system for compliance without touching core code.

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