fix(hooks): preserve plugin-registered hooks across gateway startup#28976
fix(hooks): preserve plugin-registered hooks across gateway startup#28976seilk wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes a bug where plugin hooks registered via Root cause: Solution implemented:
Key changes:
Confidence Score: 5/5
Last reviewed commit: dbad088 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dbad0888af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/hooks/loader.ts
Outdated
| // Unregister handlers from any previous loadInternalHooks() call (hot-reload safety). | ||
| // This only removes file-based hooks, preserving plugin-registered hooks. | ||
| for (const { event, handler } of fileRegisteredHandlers) { | ||
| unregisterInternalHook(event, handler); |
There was a problem hiding this comment.
Unregister prior file hooks before disabled-config early return
The new hot-reload cleanup only runs after the hooks.internal.enabled guard, so a second loadInternalHooks() call with hooks disabled leaves previously loaded file handlers registered and still executable. This shows up when configuration transitions from enabled to disabled (or any caller reuses loadInternalHooks for reload semantics): old hooks keep firing even though the new config says internal hooks are off. The unregister pass needs to happen before returning on disabled config.
Useful? React with 👍 / 👎.
dbad088 to
b868ca6
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b868ca64e1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return 0; | ||
| } |
There was a problem hiding this comment.
Unregister stale file hooks before disabled-config return
loadInternalHooks now tracks file-based handlers for hot reload, but the cleanup runs only after the hooks.internal.enabled check. If hooks were previously loaded and config is then reloaded with enabled: false, this function returns early and leaves those old handlers registered, so file hooks can continue firing even though internal hooks are disabled. Move the unregister pass ahead of this guard so disabling hooks actually removes previously loaded handlers.
Useful? React with 👍 / 👎.
clearInternalHooks() in startGatewaySidecars() was wiping all hooks including those registered by plugins via api.registerHook() during loadGatewayPlugins(). File-based hooks survived because loadInternalHooks() re-discovered them from disk, but plugin hooks were silently lost. This caused any plugin relying on internal hook events (command:new, message:received, etc.) to appear registered but never fire after a gateway restart. The bug appeared intermittent because config file changes triggered a plugin re-registration path that bypassed the clear. Fix: - Move clearInternalHooks() to server.impl.ts, before loadGatewayPlugins() - Remove clearInternalHooks() from startGatewaySidecars() - Track file-based handlers in loader.ts for selective cleanup on hot-reload Fixes openclaw#25859
b868ca6 to
a235d5f
Compare
|
Thanks for the PR! Multiple PRs address the same fix. Keeping #26242 as the earliest submission. Closing to reduce noise. This is an AI-assisted triage review. If we got this wrong, feel free to reopen — happy to revisit. |
Summary
Fixes #25859
Plugin hooks registered via
api.registerHook()duringloadGatewayPlugins()were wiped byclearInternalHooks()instartGatewaySidecars(). File-based hooks survived becauseloadInternalHooks()re-loaded them from disk. Plugin hooks were never restored.Why it looked intermittent: config file changes trigger plugin re-registration without calling
clearInternalHooks(), so hooks came back until the next cold restart. Every restart always killed plugin hooks.Changes
server.impl.ts: MoveclearInternalHooks()beforeloadGatewayPlugins()so plugins register into a clean registry that stays intactserver-startup.ts: RemoveclearInternalHooks()fromstartGatewaySidecars()loader.ts: Track file-based handlers and clean them up before reloading (hot-reload safe without touching plugin hooks)loader.test.ts: Two new tests: no duplicate handlers on reload + plugin hook preservationSame bug as #20541, #26184, #26242
reregisterPluginInternalHooks()registerPluginHook()+ second Map