openclaw - ✅(Solved) Fix [Meta] `message_sending` plugin hook does not fire on Telegram agent-reply path in 2026.4.15 [4 pull requests, 1 participants]

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…
GitHub stats
openclaw/openclaw#69807Fetched 2026-04-22 07:48:04
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0

After 3 days of work implementing a claim-verification plugin using the message_sending hook, confirmed via direct live instrumentation that agent replies to Telegram in OpenClaw 2026.4.15 bypass the plugin hook infrastructure entirely. The plugin registered correctly, appeared in openclaw hooks list as "ready", and the global hook runner reported hasHooks("message_sending") === true. Yet no hook handler was ever invoked during normal agent-reply traffic.

Root Cause

The actual agent-reply code path in extensions/telegram/bot-Ch7__EHu.js calls bot.api.sendMessage(chatId, text, ...) directly (9 call sites observed at lines 321, 330, 867, 1047, 1147, 1296, 1348, 1499, 1708). This bypasses:

  • deliverReplies in extensions/telegram/delivery-AYrG1NE_.js (which has the correct hookRunner.runMessageSending wiring)
  • sendMessageTelegram in extensions/telegram/send-DlzbQJQs.js (the exported canonical send)

Per issue #26422's existing table, this is the historical bypass pattern. PR #32649 fixed the tool-send path (via dispatchChannelMessageAction → plugin.actions.handleAction → telegramActionRuntime.sendMessageTelegram) but the agent-reply path in the bot file still uses bot.api.sendMessage directly.

Fix Action

Fix / Workaround

  • #26422 — message_sending plugin hook never fires — dead code in all outbound paths
  • #56349 — Unbypassable outbound policy enforcement (pre-send guarantee)
  • #61551 — ensureRuntimePluginsLoaded() can overwrite global hook runner during message processing
  • #48408 — fix: wire message_sending plugin hook into all channel reply dispatchers
  • #38340 — fix(dispatcher): add beforeDeliver hook, wire message_sending for Discord
  • #32649 — fix(telegram): fire message hooks in reply delivery path (merged, but only on some paths)

Installed a plugin via definePluginEntry registering message_sending. Patched three installed dist files with process.stderr.write diagnostics at:

  • deliverReplies function entry
  • hasHooks("message_sending") (with stack-trace capture)
  • sendMessageTelegram function entry

Per issue #26422's existing table, this is the historical bypass pattern. PR #32649 fixed the tool-send path (via dispatchChannelMessageAction → plugin.actions.handleAction → telegramActionRuntime.sendMessageTelegram) but the agent-reply path in the bot file still uses bot.api.sendMessage directly.

PR fix notes

PR #38340: fix(dispatcher): add beforeDeliver hook, wire message_sending for Discord

Description (problem / solution / changelog)

Problem

Discord same-surface replies bypass deliverOutboundPayloads entirely, which means message_sending plugin hooks never fire. Plugins that rely on this hook to strip or transform outbound text (e.g. removing metadata blocks injected by the LLM) have no way to intercept Discord replies.

The message_sending hook works correctly for cross-surface routing (e.g. web chat → Discord), but not for Discord → Discord (same-surface) because the Discord message handler uses its own deliver callback that calls deliverDiscordReply directly.

Solution

  1. Adds an optional beforeDeliver callback to ReplyDispatcherOptions — runs just before deliver() and may return a modified payload or null to cancel delivery. This is a general-purpose mechanism that any channel can opt into.

  2. Wires beforeDeliver in the Discord message handler to call runMessageSending from the plugin hook runner, matching the behavior that deliverOutboundPayloads already provides for cross-surface routing.

Changes

  • src/auto-reply/reply/reply-dispatcher.ts — New beforeDeliver option + call site in enqueue()
  • src/discord/monitor/message-handler.process.tsbeforeDeliver implementation calling message_sending hooks

Follow-up

Other channel handlers (Signal, Slack, iMessage, web chat) have similar deliver callbacks that bypass the hook pipeline. They could adopt the same beforeDeliver pattern in follow-up PRs.

Changed files

  • extensions/discord/src/monitor/message-handler.process.ts (modified, +43/-0)
  • src/auto-reply/reply/reply-dispatcher.ts (modified, +20/-1)
  • src/plugin-sdk/reply-runtime.ts (modified, +1/-0)

PR #70118: fix(auto-reply): run message_sending before inbound delivery

Description (problem / solution / changelog)

Summary

Inbound auto-replies currently bypass message_sending, so policies that rely on that hook only apply to the outbound pipeline and not to replies emitted by reply-dispatcher.

This adds a small beforeDeliver seam to ReplyDispatcher and wires it from inbound dispatch so message_sending runs right before delivery for both buffered and non-buffered inbound replies. Hooks can now cancel or rewrite those payloads the same way they already can for outbound sends.

Why this shape

This keeps the fix in shared auto-reply wiring instead of patching channels one by one or wrapping deliver() ad hoc. One dispatcher seam covers all inbound reply paths.

Tests

  • pnpm test src/auto-reply/dispatch.test.ts src/auto-reply/reply/before-deliver.test.ts

pnpm build was also attempted, but this checkout currently fails outside this patch during runtime-postbuild / tsgo:core because of unrelated issues in src/agents/pi-embedded-runner/* and bundled runtime dep staging for @aws/bedrock-token-generator.

Related

AI-assisted. I reviewed and tested the patch locally.

Changed files

  • extensions/telegram/src/bot-message-dispatch.ts (modified, +2/-0)
  • src/auto-reply/dispatch.test.ts (modified, +116/-1)
  • src/auto-reply/dispatch.ts (modified, +79/-2)
  • src/auto-reply/reply/before-deliver.test.ts (added, +68/-0)
  • src/auto-reply/reply/reply-dispatcher.ts (modified, +23/-3)
  • src/auto-reply/reply/reply-dispatcher.types.ts (modified, +1/-0)
RAW_BUFFERClick to expand / collapse

[Meta] message_sending plugin hook does not fire on Telegram agent-reply path in 2026.4.15

Summary

After 3 days of work implementing a claim-verification plugin using the message_sending hook, confirmed via direct live instrumentation that agent replies to Telegram in OpenClaw 2026.4.15 bypass the plugin hook infrastructure entirely. The plugin registered correctly, appeared in openclaw hooks list as "ready", and the global hook runner reported hasHooks("message_sending") === true. Yet no hook handler was ever invoked during normal agent-reply traffic.

Relates to

  • #26422 — message_sending plugin hook never fires — dead code in all outbound paths
  • #56349 — Unbypassable outbound policy enforcement (pre-send guarantee)
  • #61551 — ensureRuntimePluginsLoaded() can overwrite global hook runner during message processing
  • #48408 — fix: wire message_sending plugin hook into all channel reply dispatchers
  • #38340 — fix(dispatcher): add beforeDeliver hook, wire message_sending for Discord
  • #32649 — fix(telegram): fire message hooks in reply delivery path (merged, but only on some paths)

Reproduction

Installed a plugin via definePluginEntry registering message_sending. Patched three installed dist files with process.stderr.write diagnostics at:

  • deliverReplies function entry
  • hasHooks("message_sending") (with stack-trace capture)
  • sendMessageTelegram function entry

Ran gateway in live traffic with active Telegram DM conversation (~20 inbound/outbound message pairs over 8 minutes).

Observed

Over 8 minutes of live traffic with messages successfully delivered to Telegram:

  • hasHooks("message_sending") called 82+ times — ALL from my own plugin's watchdog (scripts/reliability-layer/src/watchdog.ts), zero from Telegram delivery
  • deliverReplies called 0 times
  • sendMessageTelegram (the exported canonical send) called 0 times

The only entries in the log matching Sent via Telegram. Message ID: NNNN were from bash[<shortlived-pid>] — the CLI openclaw message send subprocess invoked for automated post-restart notifications. Those are NOT the agent-reply path.

Root cause

The actual agent-reply code path in extensions/telegram/bot-Ch7__EHu.js calls bot.api.sendMessage(chatId, text, ...) directly (9 call sites observed at lines 321, 330, 867, 1047, 1147, 1296, 1348, 1499, 1708). This bypasses:

  • deliverReplies in extensions/telegram/delivery-AYrG1NE_.js (which has the correct hookRunner.runMessageSending wiring)
  • sendMessageTelegram in extensions/telegram/send-DlzbQJQs.js (the exported canonical send)

Per issue #26422's existing table, this is the historical bypass pattern. PR #32649 fixed the tool-send path (via dispatchChannelMessageAction → plugin.actions.handleAction → telegramActionRuntime.sendMessageTelegram) but the agent-reply path in the bot file still uses bot.api.sendMessage directly.

Impact

Plugins that register message_sending to enforce outbound policies on agent replies (claim verification, content filters, PII scrubbers, compliance gates) will NOT receive those replies. They can only intercept:

  • Tool-based sends via the message tool (goes through dispatchChannelMessageAction, but even this path calls sendMessageTelegram which has no hook either — verified in our diagnostic run)
  • CLI openclaw message send invocations (separate process, own global state, hook never triggered unless re-registered in that subprocess)

The result is a capability gap: the SDK documents a plugin hook that cannot actually enforce outbound policies on the vast majority of outbound traffic.

Proposed fix

Route all outbound Telegram sends through deliverReplies (or a wrapper that calls runMessageSending first). Specifically:

  • bot-Ch7__EHu.js agent-reply pipeline — wrap bot.api.sendMessage calls with a runMessageSending pre-check
  • extensions/telegram/send-DlzbQJQs.js:sendMessageTelegram — add runMessageSending call before the HTTP POST

Adjacent PRs to merge:

  • #48408 (universal fix) OR
  • #38340-style per-channel beforeDeliver pattern for Telegram specifically

Evidence

Full diagnostic dump attached: memory/2026-04-21-component-a-killed.md (post-mortem with reproduction steps, log excerpts, and root-cause analysis). Available on request.

Outcome on our side

Killed the plugin (uninstalled, worker service disabled, crons removed). Plugin source preserved for future re-evaluation when the hook wiring gap is closed.

Environment

  • OpenClaw 2026.4.15 (041266a) on Linux 6.12.73+deb13-amd64, Node v22.22.0
  • Telegram channel via bot-Ch7__EHu.js agent-reply pipeline
  • Plugin registered via canonical definePluginEntry from openclaw/plugin-sdk/plugin-entry

extent analysis

TL;DR

The message_sending plugin hook does not fire on Telegram agent-reply paths in OpenClaw 2026.4.15 due to direct bot.api.sendMessage calls bypassing the hook infrastructure, and can be fixed by routing all outbound Telegram sends through deliverReplies or a wrapper that calls runMessageSending first.

Guidance

  • Identify and modify the bot-Ch7__EHu.js agent-reply pipeline to wrap bot.api.sendMessage calls with a runMessageSending pre-check.
  • Update extensions/telegram/send-DlzbQJQs.js:sendMessageTelegram to add a runMessageSending call before the HTTP POST.
  • Consider merging adjacent PRs #48408 or #38340 to implement a universal fix or per-channel beforeDeliver pattern for Telegram.
  • Verify the fix by registering a plugin with the message_sending hook and testing it with Telegram agent replies.

Example

// Modified bot-Ch7__EHu.js agent-reply pipeline
const runMessageSending = require('./hookRunner').runMessageSending;
// ...
bot.api.sendMessage = function(chatId, text, options) {
  runMessageSending(chatId, text, options);
  // original sendMessage implementation
};

Notes

The proposed fix requires modifying the OpenClaw codebase to route all outbound Telegram sends through deliverReplies or a wrapper that calls runMessageSending first. This may have implications for performance and compatibility with other plugins or features.

Recommendation

Apply the proposed fix by modifying the bot-Ch7__EHu.js agent-reply pipeline and extensions/telegram/send-DlzbQJQs.js:sendMessageTelegram to route all outbound Telegram sends through deliverReplies or a wrapper that calls runMessageSending first, as this will ensure that the message_sending plugin hook is fired for all Telegram agent replies.

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

openclaw - ✅(Solved) Fix [Meta] `message_sending` plugin hook does not fire on Telegram agent-reply path in 2026.4.15 [4 pull requests, 1 participants]