← Back to PRs

#9603: fix: initialize global hook runner on plugin registry cache hit

by kevins88288 open 2026-02-05 12:41 View on GitHub →
stale
## Problem When the plugin loader returns a cached registry (same workspace dir + same plugins config), it calls `setActivePluginRegistry(cached, cacheKey)` but skips `initializeGlobalHookRunner()`. This causes **all plugin typed hooks to stop firing** after an in-process SIGUSR1 restart. The global hook runner singleton is not re-bound to the active registry on cache hit, so `getGlobalHookRunner()` returns a stale or null runner. Every hook call site short-circuits at `hasHooks()`. ## Impact - All workspace and global plugins using `api.on()` typed hooks silently break after any config change that triggers SIGUSR1 - `before_tool_call`, `after_tool_call`, `before_agent_start`, `agent_end`, and all other typed hooks stop firing - No error messages — hooks just silently don't execute ## Fix One-line addition: call `initializeGlobalHookRunner(cached)` on cache hit, matching the cold-load path behavior. ## Reproducer 1. Create a workspace plugin that registers typed hooks via `api.on('before_tool_call', handler)` 2. Start gateway — hooks fire correctly 3. Apply a config change (e.g., update agents config) that triggers SIGUSR1 in-process restart 4. Plugin hooks stop firing silently 5. Only a full process restart (kill + respawn) recovers <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR fixes a plugin reload edge case by ensuring the global typed-hook runner singleton is re-initialized when the plugin loader returns a cached `PluginRegistry`. Previously, cache hits called `setActivePluginRegistry(cached, cacheKey)` but skipped `initializeGlobalHookRunner()`, leaving `getGlobalHookRunner()` bound to a stale/null registry after an in-process SIGUSR1 restart and causing typed hooks (e.g. `before_tool_call`, `after_tool_call`, agent lifecycle hooks) to silently stop firing. Changes: - `src/plugins/loader.ts`: on registry cache hit, call `initializeGlobalHookRunner(cached)` before returning. - `package.json` / `pnpm-lock.yaml`: add `redis` dependency and update lockfile accordingly (note this is unrelated to the hook-runner fix and expands the dependency surface). <h3>Confidence Score: 4/5</h3> - This PR is likely safe once the unrelated dependency changes are removed or split out. - The functional change in `src/plugins/loader.ts` is a straightforward, consistent call to `initializeGlobalHookRunner()` on cache hits and matches the cold-load path. The main concern is the unrelated addition of `redis` and associated lockfile churn, which increases review surface and risk for a fix PR. - package.json, pnpm-lock.yaml <!-- greptile_other_comments_section --> <sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub> <!-- /greptile_comment -->

Most Similar PRs