openclaw - 💡(How to fix) Fix Managed hooks should receive plugin lifecycle events (before_dispatch, agent_end, etc.) [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#62525Fetched 2026-04-08 03:03:02
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Author
Participants

Managed hooks (in ~/.openclaw/hooks/) cannot receive important events like before_dispatch, agent_end, or message_sent because they register via registerInternalHook() but these events only trigger hookRunner.hasHooks() (plugin hook system).

Root Cause

Summary

Managed hooks (in ~/.openclaw/hooks/) cannot receive important events like before_dispatch, agent_end, or message_sent because they register via registerInternalHook() but these events only trigger hookRunner.hasHooks() (plugin hook system).

Fix Action

Fix / Workaround

Summary

Managed hooks (in ~/.openclaw/hooks/) cannot receive important events like before_dispatch, agent_end, or message_sent because they register via registerInternalHook() but these events only trigger hookRunner.hasHooks() (plugin hook system).

But important lifecycle events like before_dispatch, agent_end, message_sent, message_received ONLY check hookRunner.hasHooks() — they never call triggerInternalHook().

From dispatch-CYHzwBjK.js:

if (hookRunner?.hasHooks("before_dispatch")) {
    const beforeDispatchResult = await hookRunner.runBeforeDispatch({...});
}

Code Example

if (hookRunner?.hasHooks("before_dispatch")) {
    const beforeDispatchResult = await hookRunner.runBeforeDispatch({...});
}

---

// In dispatch-CYHzwBjK.js, around line 482:
if (hookRunner?.hasHooks("before_dispatch")) {
    const beforeDispatchResult = await hookRunner.runBeforeDispatch({...});
}
// ADD THIS: Also fire internal hook for managed hooks
if (sessionKey) {
    triggerInternalHook(createInternalHookEvent("before", "dispatch", sessionKey ?? "", {
        content: hookContext.content,
        body: hookContext.bodyForAgent ?? hookContext.body,
        channel: hookContext.channelId,
        sessionKey: sessionStoreEntry.sessionKey ?? sessionKey,
        senderId: hookContext.senderId,
        isGroup: hookContext.isGroup,
        timestamp: hookContext.timestamp,
        messageId: hookContext.messageId
    }));
}

---

metadata:
  openclaw:
    events:
      - "before:dispatch"  # type:action format for internal hooks
RAW_BUFFERClick to expand / collapse

Feature Request: Managed Hooks Should Receive Plugin Events

Summary

Managed hooks (in ~/.openclaw/hooks/) cannot receive important events like before_dispatch, agent_end, or message_sent because they register via registerInternalHook() but these events only trigger hookRunner.hasHooks() (plugin hook system).

Problem

OpenClaw has two separate hook systems:

  1. Internal Hook SystemregisterInternalHook() + triggerInternalHook()
  2. Plugin Hook SystemhookRunner.hasHooks() + hookRunner.runXxx()

Managed hooks in ~/.openclaw/hooks/ register via registerInternalHook(), which only fires for internal events like gateway:startup.

But important lifecycle events like before_dispatch, agent_end, message_sent, message_received ONLY check hookRunner.hasHooks() — they never call triggerInternalHook().

Result: Managed hooks are invisible to these events.

Evidence

From dispatch-CYHzwBjK.js:

if (hookRunner?.hasHooks("before_dispatch")) {
    const beforeDispatchResult = await hookRunner.runBeforeDispatch({...});
}

There's no corresponding triggerInternalHook(createInternalHookEvent("before", "dispatch", ...)) call.

Proposed Solution

For each plugin-only event, also trigger the internal hook alongside the plugin hook. Example for before_dispatch:

// In dispatch-CYHzwBjK.js, around line 482:
if (hookRunner?.hasHooks("before_dispatch")) {
    const beforeDispatchResult = await hookRunner.runBeforeDispatch({...});
}
// ADD THIS: Also fire internal hook for managed hooks
if (sessionKey) {
    triggerInternalHook(createInternalHookEvent("before", "dispatch", sessionKey ?? "", {
        content: hookContext.content,
        body: hookContext.bodyForAgent ?? hookContext.body,
        channel: hookContext.channelId,
        sessionKey: sessionStoreEntry.sessionKey ?? sessionKey,
        senderId: hookContext.senderId,
        isGroup: hookContext.isGroup,
        timestamp: hookContext.timestamp,
        messageId: hookContext.messageId
    }));
}

This way:

  • Plugin hooks still work as before
  • Managed hooks (registered via registerInternalHook("before:dispatch", handler)) also receive the event
  • No breaking changes to existing plugin hooks

Affected Events

These events currently only fire plugin hooks and should also fire internal hooks:

EventLocationUse Case
before_dispatchdispatch-CYHzwBjK.jsReact to inbound messages before agent processes them
agent_endpi-embedded-DWASRjxE.jsReact after agent finishes processing
message_sentdeliver-DqZbx7oj.jsReact after sending a message (already partially supports internal hooks)
message_receiveddispatch-CYHzwBjK.jsReact to received messages

Workaround Applied

We've applied this patch locally to enable before:dispatch and agent:end internal hooks for a Kyle-specific reaction hook that sends 👀→✍️→👍 on Kyle's Telegram messages.

Files patched:

  • dispatch-CYHzwBjK.js — added triggerInternalHook for before:dispatch
  • pi-embedded-DWASRjxE.js — added triggerInternalHook for agent:end

Impact

Minimal. This only adds additional internal hook triggers alongside existing plugin hook calls. Existing plugin hooks behavior is unchanged.

Compatibility

Managed hooks already support registering for events via HOOK.md metadata:

metadata:
  openclaw:
    events:
      - "before:dispatch"  # type:action format for internal hooks

The internal hook naming uses "type:action" format (e.g., "before:dispatch", "agent:end") while plugin hooks use underscore format (e.g., "before_dispatch", "agent_end").

extent analysis

TL;DR

To fix the issue, trigger internal hooks alongside plugin hooks for events like before_dispatch, agent_end, message_sent, and message_received by adding triggerInternalHook calls in the respective event locations.

Guidance

  • Identify the locations of the affected events (dispatch-CYHzwBjK.js, pi-embedded-DWASRjxE.js, deliver-DqZbx7oj.js) and add triggerInternalHook calls for each event.
  • Ensure the triggerInternalHook calls are made with the correct event data, such as createInternalHookEvent("before", "dispatch", ...).
  • Verify that managed hooks registered via registerInternalHook receive the events by checking the hook execution.
  • Review the HOOK.md metadata for managed hooks to ensure they are correctly configured to receive internal hook events.

Example

// In dispatch-CYHzwBjK.js, around line 482:
if (hookRunner?.hasHooks("before_dispatch")) {
    const beforeDispatchResult = await hookRunner.runBeforeDispatch({...});
}
// ADD THIS: Also fire internal hook for managed hooks
if (sessionKey) {
    triggerInternalHook(createInternalHookEvent("before", "dispatch", sessionKey ?? "", {
        content: hookContext.content,
        body: hookContext.bodyForAgent ?? hookContext.body,
        channel: hookContext.channelId,
        sessionKey: sessionStoreEntry.sessionKey ?? sessionKey,
        senderId: hookContext.senderId,
        isGroup: hookContext.isGroup,
        timestamp: hookContext.timestamp,
        messageId: hookContext.messageId
    }));
}

Notes

The proposed solution involves adding triggerInternalHook calls to the existing event locations, which should not introduce breaking changes to existing plugin hooks. However, thorough testing is recommended to ensure the fix works as expected.

Recommendation

Apply the workaround by adding triggerInternalHook calls for the affected events, as this will enable managed hooks to receive important lifecycle events without breaking existing plugin hooks.

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