← Back to PRs

#10679: fix(hooks): invoke gateway_start and gateway_stop in lifecycle

by yassinebkr open 2026-02-06 20:59 View on GitHub →
gateway
# PR: Fire `gateway_start` and `gateway_stop` lifecycle hooks Closes #6711 ## Problem The `gateway_start` and `gateway_stop` hooks are fully defined in the hook runner (`src/plugins/hooks.ts`) and exposed via `HookRunner.runGatewayStart()` / `HookRunner.runGatewayStop()`, but **never actually invoked** anywhere in the gateway lifecycle. This means plugins that register handlers via `api.on("gateway_start", handler)` or `api.on("gateway_stop", handler)` will never have their handlers called — the hooks are dead code from the plugin developer's perspective. ## Fix ### `gateway_start` — `src/gateway/server.impl.ts` Added invocation **immediately after `startGatewaySidecars()` completes**. At this point: - Config is loaded and validated - Plugin registry is initialized and the global hook runner is available - All channels are started - Browser control server is running (if enabled) - Plugin services are active This is the correct point because all subsystems a plugin might depend on are fully initialized. ### `gateway_stop` — `src/gateway/server-close.ts` Added invocation **at the beginning of the close handler**, before any teardown begins. This gives plugins a chance to: - Flush buffers or pending writes - Notify external services - Clean up resources gracefully The `reason` string from the close handler options is passed through to the hook event. Both invocations are wrapped in try/catch to ensure a misbehaving hook handler cannot prevent gateway startup or shutdown. ## What this enables for plugin developers Plugins can now reliably hook into the gateway lifecycle: ```ts api.on("gateway_start", async (event, ctx) => { console.log(`Gateway started on port ${event.port}`); // Initialize external connections, start background jobs, etc. }); api.on("gateway_stop", async (event, ctx) => { console.log(`Gateway stopping: ${event.reason}`); // Flush data, close connections, notify services, etc. }); ``` ## Changes | File | Change | |------|--------| | `src/gateway/server.impl.ts` | Import `getGlobalHookRunner`; invoke `runGatewayStart` after sidecars start | | `src/gateway/server-close.ts` | Import `getGlobalHookRunner` + logger; invoke `runGatewayStop` before teardown | | `src/plugins/hooks.gateway-lifecycle.test.ts` | New test file — 6 tests covering both hooks | ## Tests ``` ✓ src/plugins/hooks.gateway-lifecycle.test.ts (6 tests) ✓ fires all registered gateway_start handlers ✓ fires all registered gateway_stop handlers ✓ is a no-op when no gateway_start handlers are registered ✓ is a no-op when no gateway_stop handlers are registered ✓ catches handler errors gracefully for gateway_start ✓ catches handler errors gracefully for gateway_stop ``` <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR wires the previously-defined `gateway_start`/`gateway_stop` plugin lifecycle hooks into the gateway startup (`src/gateway/server.impl.ts`) and shutdown (`src/gateway/server-close.ts`) paths by calling the global hook runner (with errors caught so startup/shutdown can proceed). It also adds a Vitest suite to validate that registered handlers fire, no-op when empty, and that handler exceptions are contained. The main issue found is in the new test helper registry construction: it builds a `PluginRegistry` with an invalid `hooks` field value (typed hook registrations assigned to the internal hook list), which should break type-checking for the test file and doesn’t reflect a real registry instance. <h3>Confidence Score: 4/5</h3> - Mostly safe to merge once the new test file type mismatch is fixed. - The runtime changes are straightforward (hook invocations guarded + try/catch) and localized, but the newly added Vitest file appears to construct an invalid `PluginRegistry` (assigning typed hook registrations to the internal `hooks` array), which should break TypeScript checking and indicates the tests may not compile as written. - src/plugins/hooks.gateway-lifecycle.test.ts <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs