#12082: feat: implement plugin lifecycle interception hook architecture
docs
gateway
extensions: lobster
commands
agents
stale
size: XL
Cluster:
Plugin Hook Enhancements
## Origin
This implementation is based on:
- Proposal gist: https://gist.github.com/openmetaloom/657c4668c09d235f8da1306e2438904b
- OpenClaw discussion: https://github.com/openclaw/openclaw/discussions/9872
## Summary
This PR implements a typed plugin lifecycle interception architecture and wires it to concrete runtime execution points.
It includes:
- lifecycle API: `api.lifecycle.on(phase, handler, options)`
- raw hook API: `api.on(hookName, handler, options)`
- canonical + alias phase mapping to runtime hooks
- hardened runtime behavior (fail-open/fail-closed propagation, timeout handling, retries, scoping, concurrency controls)
- integration wiring in gateway, dispatch/messaging, recall/memory, and tool execution flows
- expanded unit/integration tests for mapping, wiring, and failure semantics
## Lifecycle APIs
- `api.on("<hook_name>", handler, opts)` for raw runtime hooks
- `api.lifecycle.on("<phase>", handler, opts)` for canonical phases and aliases
### Execution options
Supported on both APIs:
- `priority`
- `timeoutMs`
- `mode` (`fail-open` | `fail-closed`)
- `onTimeout` (`fail-open` | `fail-closed`)
- `retry` (`count`, optional `backoffMs`)
- `maxConcurrency`
- `scope` (`channels`, `agentIds`, `toolNames`)
- `condition`
## Canonical phase -> runtime hook mapping
- `boot.pre` -> `gateway_pre_start`
- `boot.post` -> `gateway_start`
- `shutdown.pre` -> `gateway_pre_stop`
- `shutdown.post` -> `gateway_stop`
- `agent.pre` -> `before_agent_start`
- `agent.post` -> `agent_end`
- `request.pre` -> `message_received`
- `request.post` -> `request_post`
- `recall.pre` -> `before_recall`
- `recall.post` -> `after_recall`
- `message.pre` -> `message_sending`
- `message.post` -> `message_sent`
- `tool.pre` -> `before_tool_call`
- `tool.post` -> `after_tool_call`
- `tool.error` -> `tool_error`
- `response.error` -> `response_error`
- `error` -> `agent_error`
- `memory.compaction.pre` -> `before_compaction`
- `memory.compaction.post` -> `after_compaction`
## Alias -> canonical -> runtime mapping
- `preBoot` -> `boot.pre` -> `gateway_pre_start`
- `postBoot` -> `boot.post` -> `gateway_start`
- `preShutdown` -> `shutdown.pre` -> `gateway_pre_stop`
- `postShutdown` -> `shutdown.post` -> `gateway_stop`
- `preAgent` -> `agent.pre` -> `before_agent_start`
- `postRequest` -> `agent.post` -> `agent_end`
- `preRequest` -> `request.pre` -> `message_received`
- `postRequestIngress` -> `request.post` -> `request_post`
- `preRecall` -> `recall.pre` -> `before_recall`
- `postRecall` -> `recall.post` -> `after_recall`
- `preResponse` -> `message.pre` -> `message_sending`
- `postResponse` -> `message.post` -> `message_sent`
- `preToolExecution` -> `tool.pre` -> `before_tool_call`
- `postToolExecution` -> `tool.post` -> `after_tool_call`
- `onToolError` -> `tool.error` -> `tool_error`
- `onResponseError` -> `response.error` -> `response_error`
- `preCompaction` -> `memory.compaction.pre` -> `before_compaction`
- `postCompaction` -> `memory.compaction.post` -> `after_compaction`
- `onError` -> `error` -> `agent_error`
Alias coverage is validated in code to enforce one alias per runtime hook and no overlaps.
## Runtime wiring added/verified
- Gateway lifecycle: pre-start/start/pre-stop/stop
- Agent lifecycle: pre-start, post-run, error
- Recall lifecycle: before recall and after recall
- Message lifecycle: inbound (`message_received`), post-ingress (`request_post`), pre-send (`message_sending`), post-send (`message_sent`), send-failure (`response_error`)
- Tool lifecycle: before call, after call, tool error
- Compaction lifecycle: pre/post compaction
## Reliability and hardening
- Structured hook execution errors via `PluginHookExecutionError`
- Fail-closed propagation at critical interception points
- Timeout behavior configurable independently via `onTimeout`
- Retry with optional backoff for transient hook failures
- Per-handler concurrency limits
- Scope-based gating by channel/agent/tool
## Tests and validation
Locally validated with:
- `pnpm lint`
- `pnpm tsgo`
- `pnpm canvas:a2ui:bundle && pnpm test`
- `pnpm canvas:a2ui:bundle && bunx vitest run --config vitest.unit.config.ts`
Both CI-equivalent test paths passed locally.
Most Similar PRs
#21224: Introduce optional runtime lifecycle hooks for tool and model bound...
by mikeholownych · 2026-02-19
74.3%
#18889: feat(hooks): add agent and tool lifecycle boundaries
by vincentkoc · 2026-02-17
70.0%
#20580: feat(hooks): bridge after_tool_call to internal hook handler system
by CryptoKrad · 2026-02-19
69.6%
#18860: feat(agents): expose tools and their schemas via new after_tools_re...
by lan17 · 2026-02-17
69.2%
#15571: feat: infrastructure foundation — hooks, model failover, sessions, ...
by tangcruz · 2026-02-13
69.1%
#17667: feat: tool-hooks extension — run shell commands on tool calls
by FaradayHunt · 2026-02-16
68.9%
#10679: fix(hooks): invoke gateway_start and gateway_stop in lifecycle
by yassinebkr · 2026-02-06
68.5%
#10678: feat(hooks): wire after_tool_call hook into tool execution pipeline
by yassinebkr · 2026-02-06
68.2%
#6405: feat(security): Add HTTP API security hooks for plugin scanning
by masterfung · 2026-02-01
67.9%
#20067: feat(plugins): add before_agent_reply hook for message interception
by JoshuaLelon · 2026-02-18
67.6%