#10679: fix(hooks): invoke gateway_start and gateway_stop in lifecycle
gateway
Cluster:
Hook and Gateway Improvements
# 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
#20541: fix(hooks): clear internal hooks before plugins register
by ramarnat · 2026-02-19
85.2%
#12582: feat(hooks): emit gateway shutdown lifecycle events
by vincentkoc · 2026-02-09
81.2%
#9603: fix: initialize global hook runner on plugin registry cache hit
by kevins88288 · 2026-02-05
80.5%
#15571: feat: infrastructure foundation — hooks, model failover, sessions, ...
by tangcruz · 2026-02-13
79.5%
#8084: fix(plugins): wire up message_sending hook in outbound delivery
by lailoo · 2026-02-03
79.5%
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
79.2%
#13415: fix(hooks): bridge agent_end events to internal/workspace hooks
by mcaxtr · 2026-02-10
78.6%
#9909: Gateway: add HITL approvals integration
by FletcherFrimpong · 2026-02-05
78.6%
#15611: fix(gateway): invalidate hook transform cache on config reload
by AI-Reviewer-QS · 2026-02-13
77.0%
#23410: Gateway: require prefixes for hook request session-key overrides
by bmendonca3 · 2026-02-22
76.6%