docs: add plugin hooks reference

This commit is contained in:
Peter Steinberger
2026-04-24 18:22:28 +01:00
parent 342583348d
commit 7330a0c7e0
10 changed files with 218 additions and 11 deletions

View File

@@ -343,6 +343,10 @@
"source": "Plugin Entry Points",
"target": "插件入口点"
},
{
"source": "Plugin hooks",
"target": "插件钩子"
},
{
"source": "Entry Points",
"target": "入口点"

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1158,6 +1158,7 @@
"group": "Building plugins",
"pages": [
"plugins/building-plugins",
"plugins/hooks",
"plugins/sdk-channel-plugins",
"plugins/sdk-provider-plugins",
"plugins/sdk-migration"

View File

@@ -33,7 +33,7 @@ falls back to npm automatically.
<Card title="Provider plugin" icon="cpu" href="/plugins/sdk-provider-plugins">
Add a model provider (LLM, proxy, or custom endpoint)
</Card>
<Card title="Tool / hook plugin" icon="wrench">
<Card title="Tool / hook plugin" icon="wrench" href="/plugins/hooks">
Register agent tools, event hooks, or services — continue below
</Card>
</CardGroup>
@@ -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

188
docs/plugins/hooks.md Normal file
View File

@@ -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<string, unknown>;
block?: boolean;
blockReason?: string;
requireApproval?: {
title: string;
description: string;
severity?: "info" | "warning" | "critical";
timeoutMs?: number;
timeoutBehavior?: "allow" | "deny";
onResolution?: (decision: string) => Promise<void> | 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.<id>.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)

View File

@@ -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).
</Tip>
## 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.

View File

@@ -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)