diff --git a/docs/.i18n/glossary.zh-CN.json b/docs/.i18n/glossary.zh-CN.json
index 99a8398890a..f744566cf92 100644
--- a/docs/.i18n/glossary.zh-CN.json
+++ b/docs/.i18n/glossary.zh-CN.json
@@ -343,6 +343,10 @@
"source": "Plugin Entry Points",
"target": "插件入口点"
},
+ {
+ "source": "Plugin hooks",
+ "target": "插件钩子"
+ },
{
"source": "Entry Points",
"target": "入口点"
diff --git a/docs/automation/hooks.md b/docs/automation/hooks.md
index eefec6b7a40..0c3eb7465d5 100644
--- a/docs/automation/hooks.md
+++ b/docs/automation/hooks.md
@@ -205,9 +205,12 @@ Runs `BOOT.md` from the active workspace when the gateway starts.
## Plugin hooks
-Plugins can register hooks through the Plugin SDK for deeper integration: intercepting tool calls, modifying prompts, controlling message flow, and more. The Plugin SDK exposes 28 hooks covering model resolution, agent lifecycle, message flow, tool execution, subagent coordination, and gateway lifecycle.
+Plugins can register typed hooks through the Plugin SDK for deeper integration:
+intercepting tool calls, modifying prompts, controlling message flow, and more.
+Use plugin hooks when you need `before_tool_call`, `before_agent_reply`,
+`before_install`, or other in-process lifecycle hooks.
-For the complete plugin hook reference including `before_tool_call`, `before_agent_reply`, `before_install`, and all other plugin hooks, see [Plugin Architecture](/plugins/architecture-internals#provider-runtime-hooks).
+For the complete plugin hook reference, see [Plugin hooks](/plugins/hooks).
## Configuration
@@ -315,5 +318,5 @@ Check for missing binaries (PATH), environment variables, config values, or OS c
- [CLI Reference: hooks](/cli/hooks)
- [Webhooks](/automation/cron-jobs#webhooks)
-- [Plugin Architecture](/plugins/architecture-internals#provider-runtime-hooks) — full plugin hook reference
+- [Plugin hooks](/plugins/hooks) — in-process plugin lifecycle hooks
- [Configuration](/gateway/configuration-reference#hooks)
diff --git a/docs/automation/index.md b/docs/automation/index.md
index bc3ac89cc11..bd0fed50685 100644
--- a/docs/automation/index.md
+++ b/docs/automation/index.md
@@ -40,7 +40,7 @@ flowchart TD
| Audit what ran and when | Background Tasks | `openclaw tasks list` and `openclaw tasks audit` |
| Multi-step research then summarize | Task Flow | Durable orchestration with revision tracking |
| Run a script on session reset | Hooks | Event-driven, fires on lifecycle events |
-| Execute code on every tool call | Hooks | Hooks can filter by event type |
+| Execute code on every tool call | Plugin hooks | In-process hooks can intercept tool calls |
| Always check compliance before replying | Standing Orders | Injected into every session automatically |
### Scheduled Tasks (Cron) vs Heartbeat
@@ -83,7 +83,11 @@ See [Standing Orders](/automation/standing-orders).
### Hooks
-Hooks are event-driven scripts triggered by agent lifecycle events (`/new`, `/reset`, `/stop`), session compaction, gateway startup, message flow, and tool calls. Hooks are automatically discovered from directories and can be managed with `openclaw hooks`.
+Internal hooks are event-driven scripts triggered by agent lifecycle events
+(`/new`, `/reset`, `/stop`), session compaction, gateway startup, and message
+flow. They are automatically discovered from directories and can be managed
+with `openclaw hooks`. For in-process tool-call interception, use
+[Plugin hooks](/plugins/hooks).
See [Hooks](/automation/hooks).
@@ -97,7 +101,7 @@ See [Heartbeat](/gateway/heartbeat).
- **Cron** handles precise schedules (daily reports, weekly reviews) and one-shot reminders. All cron executions create task records.
- **Heartbeat** handles routine monitoring (inbox, calendar, notifications) in one batched turn every 30 minutes.
-- **Hooks** react to specific events (tool calls, session resets, compaction) with custom scripts.
+- **Hooks** react to specific events (session resets, compaction, message flow) with custom scripts. Plugin hooks cover tool calls.
- **Standing orders** give the agent persistent context and authority boundaries.
- **Task Flow** coordinates multi-step flows above individual tasks.
- **Tasks** automatically track all detached work so you can inspect and audit it.
@@ -108,6 +112,7 @@ See [Heartbeat](/gateway/heartbeat).
- [Background Tasks](/automation/tasks) — task ledger for all detached work
- [Task Flow](/automation/taskflow) — durable multi-step flow orchestration
- [Hooks](/automation/hooks) — event-driven lifecycle scripts
+- [Plugin hooks](/plugins/hooks) — in-process tool, prompt, message, and lifecycle hooks
- [Standing Orders](/automation/standing-orders) — persistent agent instructions
- [Heartbeat](/gateway/heartbeat) — periodic main-session turns
- [Configuration Reference](/gateway/configuration-reference) — all config keys
diff --git a/docs/cli/hooks.md b/docs/cli/hooks.md
index 8ddb5002bba..78c75edc723 100644
--- a/docs/cli/hooks.md
+++ b/docs/cli/hooks.md
@@ -15,7 +15,7 @@ Running `openclaw hooks` with no subcommand is equivalent to `openclaw hooks lis
Related:
- Hooks: [Hooks](/automation/hooks)
-- Plugin hooks: [Plugin hooks](/plugins/architecture-internals#provider-runtime-hooks)
+- Plugin hooks: [Plugin hooks](/plugins/hooks)
## List All Hooks
diff --git a/docs/concepts/agent-loop.md b/docs/concepts/agent-loop.md
index ba4458e4290..979f6fc37b4 100644
--- a/docs/concepts/agent-loop.md
+++ b/docs/concepts/agent-loop.md
@@ -112,7 +112,7 @@ Hook decision rules for outbound/tool guards:
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
- `message_sending`: `{ cancel: false }` is a no-op and does not clear a prior cancel.
-See [Plugin hooks](/plugins/architecture-internals#provider-runtime-hooks) for the hook API and registration details.
+See [Plugin hooks](/plugins/hooks) for the hook API and registration details.
Harnesses may adapt these hooks differently. The Codex app-server harness keeps
OpenClaw plugin hooks as the compatibility contract for documented mirrored
diff --git a/docs/docs.json b/docs/docs.json
index bd31fb890fc..f5da4c35a00 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -1158,6 +1158,7 @@
"group": "Building plugins",
"pages": [
"plugins/building-plugins",
+ "plugins/hooks",
"plugins/sdk-channel-plugins",
"plugins/sdk-provider-plugins",
"plugins/sdk-migration"
diff --git a/docs/plugins/building-plugins.md b/docs/plugins/building-plugins.md
index 28e44aeccea..e4eb4d4de0a 100644
--- a/docs/plugins/building-plugins.md
+++ b/docs/plugins/building-plugins.md
@@ -33,7 +33,7 @@ falls back to npm automatically.
Add a model provider (LLM, proxy, or custom endpoint)
-
+
Register agent tools, event hooks, or services — continue below
@@ -163,7 +163,8 @@ A single plugin can register any number of capabilities via the `api` object:
| Embedded Pi extension | `api.registerEmbeddedExtensionFactory(...)` | [SDK Overview](/plugins/sdk-overview#registration-api) |
| Agent tools | `api.registerTool(...)` | Below |
| Custom commands | `api.registerCommand(...)` | [Entry Points](/plugins/sdk-entrypoints) |
-| Event hooks | `api.registerHook(...)` | [Entry Points](/plugins/sdk-entrypoints) |
+| Plugin hooks | `api.on(...)` | [Plugin hooks](/plugins/hooks) |
+| Internal event hooks | `api.registerHook(...)` | [Entry Points](/plugins/sdk-entrypoints) |
| HTTP routes | `api.registerHttpRoute(...)` | [Internals](/plugins/architecture-internals#gateway-http-routes) |
| CLI subcommands | `api.registerCli(...)` | [Entry Points](/plugins/sdk-entrypoints) |
@@ -197,7 +198,7 @@ If custom approval plumbing needs to detect that same bounded fallback case,
prefer `isApprovalNotFoundError` from `openclaw/plugin-sdk/error-runtime`
instead of matching approval-expiry strings manually.
-See [SDK Overview hook decision semantics](/plugins/sdk-overview#hook-decision-semantics) for details.
+See [Plugin hooks](/plugins/hooks) for examples and the hook reference.
## Registering agent tools
diff --git a/docs/plugins/hooks.md b/docs/plugins/hooks.md
new file mode 100644
index 00000000000..3a4cf69c129
--- /dev/null
+++ b/docs/plugins/hooks.md
@@ -0,0 +1,188 @@
+---
+summary: "Plugin hooks: intercept agent, tool, message, session, and Gateway lifecycle events"
+title: "Plugin hooks"
+read_when:
+ - You are building a plugin that needs before_tool_call, before_agent_reply, message hooks, or lifecycle hooks
+ - You need to block, rewrite, or require approval for tool calls from a plugin
+ - You are deciding between internal hooks and plugin hooks
+---
+
+Plugin hooks are in-process extension points for OpenClaw plugins. Use them
+when a plugin needs to inspect or change agent runs, tool calls, message flow,
+session lifecycle, subagent routing, installs, or Gateway startup.
+
+Use [internal hooks](/automation/hooks) instead when you want a small
+operator-installed `HOOK.md` script for command and Gateway events such as
+`/new`, `/reset`, `/stop`, `agent:bootstrap`, or `gateway:startup`.
+
+## Quick start
+
+Register typed plugin hooks with `api.on(...)` from your plugin entry:
+
+```typescript
+import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
+
+export default definePluginEntry({
+ id: "tool-preflight",
+ name: "Tool Preflight",
+ register(api) {
+ api.on(
+ "before_tool_call",
+ async (event) => {
+ if (event.toolName !== "web_search") {
+ return;
+ }
+
+ return {
+ requireApproval: {
+ title: "Run web search",
+ description: `Allow search query: ${String(event.params.query ?? "")}`,
+ severity: "info",
+ timeoutMs: 60_000,
+ timeoutBehavior: "deny",
+ },
+ };
+ },
+ { priority: 50 },
+ );
+ },
+});
+```
+
+Hook handlers run sequentially in descending `priority`. Same-priority hooks
+keep registration order.
+
+## Common hooks
+
+| Hook | Use it for |
+| ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
+| `before_tool_call` | Rewrite tool params, block execution, or request user approval before a tool runs. |
+| `after_tool_call` | Observe tool results, errors, and duration after execution. |
+| `before_prompt_build` | Add dynamic context or system prompt text before the model call. |
+| `before_model_resolve` | Override provider or model before session messages are loaded. |
+| `before_agent_reply` | Short-circuit the model turn with a synthetic reply or silence. |
+| `llm_input` / `llm_output` | Observe provider input/output for conversation-aware plugins. |
+| `agent_end` | Observe final messages, success state, and run duration. |
+| `message_received` | Observe inbound channel messages after channel parsing. |
+| `message_sending` | Rewrite or cancel outbound channel messages. |
+| `message_sent` | Observe outbound delivery success or failure. |
+| `session_start` / `session_end` | Track session lifecycle boundaries. |
+| `before_compaction` / `after_compaction` | Observe or annotate compaction cycles. |
+| `subagent_spawning` / `subagent_delivery_target` / `subagent_spawned` / `subagent_ended` | Coordinate subagent routing and completion delivery. |
+| `gateway_start` / `gateway_stop` | Start or stop plugin services with the Gateway. |
+| `before_install` | Inspect skill or plugin install scans and optionally block. |
+
+## Tool call policy
+
+`before_tool_call` receives:
+
+- `event.toolName`
+- `event.params`
+- optional `event.runId`
+- optional `event.toolCallId`
+- context fields such as `ctx.agentId`, `ctx.sessionKey`, `ctx.sessionId`, and
+ diagnostic `ctx.trace`
+
+It can return:
+
+```typescript
+type BeforeToolCallResult = {
+ params?: Record;
+ block?: boolean;
+ blockReason?: string;
+ requireApproval?: {
+ title: string;
+ description: string;
+ severity?: "info" | "warning" | "critical";
+ timeoutMs?: number;
+ timeoutBehavior?: "allow" | "deny";
+ onResolution?: (decision: string) => Promise | void;
+ };
+};
+```
+
+Rules:
+
+- `block: true` is terminal and skips lower-priority handlers.
+- `block: false` is treated as no decision.
+- `params` rewrites the tool parameters for execution.
+- `requireApproval` pauses the agent run and asks the user through plugin
+ approvals. The `/approve` command can approve both exec and plugin approvals.
+- A lower-priority `block: true` can still block after a higher-priority hook
+ requested approval.
+
+## Prompt and model hooks
+
+Use the phase-specific hooks for new plugins:
+
+- `before_model_resolve`: receives only the current prompt and attachment
+ metadata. Return `providerOverride` or `modelOverride`.
+- `before_prompt_build`: receives the current prompt and session messages.
+ Return `prependContext`, `systemPrompt`, `prependSystemContext`, or
+ `appendSystemContext`.
+
+`before_agent_start` remains for compatibility. Prefer the explicit hooks above
+so your plugin does not depend on a legacy combined phase.
+
+Non-bundled plugins that need `llm_input`, `llm_output`, or `agent_end` must set:
+
+```json
+{
+ "plugins": {
+ "entries": {
+ "my-plugin": {
+ "hooks": {
+ "allowConversationAccess": true
+ }
+ }
+ }
+ }
+}
+```
+
+Prompt-mutating hooks can be disabled per plugin with
+`plugins.entries..hooks.allowPromptInjection=false`.
+
+## Message hooks
+
+Use message hooks for channel-level routing and delivery policy:
+
+- `message_received`: observe inbound content, sender, `threadId`, and metadata.
+- `message_sending`: rewrite `content` or return `{ cancel: true }`.
+- `message_sent`: observe final success or failure.
+
+Prefer typed `threadId` and `replyToId` fields before using channel-specific
+metadata.
+
+Decision rules:
+
+- `message_sending` with `cancel: true` is terminal.
+- `message_sending` with `cancel: false` is treated as no decision.
+- Rewritten `content` continues to lower-priority hooks unless a later hook
+ cancels delivery.
+
+## Install hooks
+
+`before_install` runs after the built-in scan for skill and plugin installs.
+Return additional findings or `{ block: true, blockReason }` to stop the
+install.
+
+`block: true` is terminal. `block: false` is treated as no decision.
+
+## Gateway lifecycle
+
+Use `gateway_start` for plugin services that need Gateway-owned state. The
+context exposes `ctx.config`, `ctx.workspaceDir`, and `ctx.getCron?.()` for
+cron inspection and updates. Use `gateway_stop` to clean up long-running
+resources.
+
+Do not rely on the internal `gateway:startup` hook for plugin-owned runtime
+services.
+
+## Related
+
+- [Building plugins](/plugins/building-plugins)
+- [Plugin SDK overview](/plugins/sdk-overview)
+- [Plugin entry points](/plugins/sdk-entrypoints)
+- [Internal hooks](/automation/hooks)
+- [Plugin architecture internals](/plugins/architecture-internals)
diff --git a/docs/plugins/sdk-overview.md b/docs/plugins/sdk-overview.md
index 7c99488a92f..be6ae861eda 100644
--- a/docs/plugins/sdk-overview.md
+++ b/docs/plugins/sdk-overview.md
@@ -17,6 +17,7 @@ reference for **what to import** and **what you can register**.
- First plugin? Start with [Building plugins](/plugins/building-plugins).
- Channel plugin? See [Channel plugins](/plugins/sdk-channel-plugins).
- Provider plugin? See [Provider plugins](/plugins/sdk-provider-plugins).
+- Tool or lifecycle hook plugin? See [Plugin hooks](/plugins/hooks).
## Import convention
@@ -229,6 +230,9 @@ AI CLI backend such as `codex-cli`.
| `api.on(hookName, handler, opts?)` | Typed lifecycle hook |
| `api.onConversationBindingResolved(handler)` | Conversation binding callback |
+See [Plugin hooks](/plugins/hooks) for examples, common hook names, and guard
+semantics.
+
### Hook decision semantics
- `before_tool_call`: returning `{ block: true }` is terminal. Once any handler sets it, lower-priority handlers are skipped.
diff --git a/docs/start/hubs.md b/docs/start/hubs.md
index ced82a9796d..c84af3f75a6 100644
--- a/docs/start/hubs.md
+++ b/docs/start/hubs.md
@@ -164,6 +164,7 @@ Use these hubs to discover every page, including deep dives and reference docs t
- [Plugins overview](/tools/plugin)
- [Building plugins](/plugins/building-plugins)
+- [Plugin hooks](/plugins/hooks)
- [Plugin manifest](/plugins/manifest)
- [Agent tools](/plugins/building-plugins#registering-agent-tools)
- [Plugin bundles](/plugins/bundles)