Compare commits
2 Commits
main
...
vincentkoc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84075f02bd | ||
|
|
3fba484d96 |
@ -8,16 +8,28 @@ title: "Hooks"
|
||||
|
||||
# Hooks
|
||||
|
||||
Hooks provide an extensible event-driven system for automating actions in response to agent commands and events. Hooks are automatically discovered from directories and can be managed via CLI commands, similar to how skills work in OpenClaw.
|
||||
Hooks provide an event-driven automation surface for operator-managed actions in response to agent commands and coarse lifecycle events. Hooks are automatically discovered from directories and can be managed via CLI commands, similar to how skills work in OpenClaw.
|
||||
|
||||
## Getting Oriented
|
||||
|
||||
Hooks are small scripts that run when something happens. There are two kinds:
|
||||
|
||||
- **Hooks** (this page): run inside the Gateway when agent events fire, like `/new`, `/reset`, `/stop`, or lifecycle events.
|
||||
- **Hooks** (this page): run inside the Gateway when command, message, session, agent, or gateway events fire.
|
||||
- **Webhooks**: external HTTP webhooks that let other systems trigger work in OpenClaw. See [Webhook Hooks](/automation/webhook) or use `openclaw webhooks` for Gmail helper commands.
|
||||
|
||||
Hooks can also be bundled inside plugins; see [Plugins](/tools/plugin#plugin-hooks).
|
||||
Plugins can also register this same internal hook system with `api.registerHook(...)`; see [Plugins](/tools/plugin#plugin-hooks).
|
||||
|
||||
## Choose The Right Surface
|
||||
|
||||
OpenClaw has a few extension surfaces that look similar but solve different problems:
|
||||
|
||||
| If you want to... | Use... | Why |
|
||||
| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| Save a snapshot on `/new`, log `/reset`, call an external API after `message:sent`, or add coarse operator automation | Hooks (`HOOK.md`, this page) | File-based hooks are meant for operator-managed side effects and command/lifecycle automation |
|
||||
| Rewrite prompts, block tools, cancel outbound messages, or add ordered middleware/policy | Typed plugin hooks via `api.on(...)` | Typed hooks have explicit contracts, priorities, merge rules, and block/cancel semantics |
|
||||
| Add telemetry-only export or observability | Diagnostic events | Observability is a separate event bus, not a policy hook surface |
|
||||
|
||||
Use hooks when you want automation that behaves like a small installed integration. Use typed plugin hooks when you need runtime lifecycle control.
|
||||
|
||||
Common uses:
|
||||
|
||||
@ -37,6 +49,14 @@ The hooks system allows you to:
|
||||
- Trigger custom automations on agent lifecycle events
|
||||
- Extend OpenClaw's behavior without modifying core code
|
||||
|
||||
What hooks are not for:
|
||||
|
||||
- Ordered middleware that must run by priority
|
||||
- Blocking or rewriting runtime decisions inside the agent loop
|
||||
- Fine-grained policy enforcement on prompt/tool/message mutation
|
||||
|
||||
For those cases, use typed plugin hooks via `api.on(...)`.
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Bundled Hooks
|
||||
@ -199,7 +219,7 @@ const myHandler = async (event) => {
|
||||
|
||||
// Your custom logic here
|
||||
|
||||
// Optionally send message to user
|
||||
// Optionally send a reply on replyable surfaces
|
||||
event.messages.push("✨ My hook executed!");
|
||||
};
|
||||
|
||||
@ -216,7 +236,7 @@ Each event includes:
|
||||
action: string, // e.g., 'new', 'reset', 'stop', 'received', 'sent'
|
||||
sessionKey: string, // Session identifier
|
||||
timestamp: Date, // When the event occurred
|
||||
messages: string[], // Push messages here to send to user
|
||||
messages: string[], // Push replies here on replyable surfaces only
|
||||
context: {
|
||||
// Command events:
|
||||
sessionEntry?: SessionEntry,
|
||||
@ -237,6 +257,11 @@ Each event includes:
|
||||
}
|
||||
```
|
||||
|
||||
`event.messages` is only delivered automatically on replyable surfaces such as
|
||||
`command:*` and `message:received`. Lifecycle-only events such as
|
||||
`agent:bootstrap`, `session:*`, `gateway:*`, or `message:sent` do not have a
|
||||
reply channel and will ignore pushed messages.
|
||||
|
||||
## Event Types
|
||||
|
||||
### Command Events
|
||||
|
||||
@ -521,34 +521,20 @@ Then enable it in config:
|
||||
|
||||
## Plugin hooks
|
||||
|
||||
Plugins can register hooks at runtime. This lets a plugin bundle event-driven
|
||||
automation without a separate hook pack install.
|
||||
Plugins can register hooks at runtime, but there are two different APIs with
|
||||
different jobs.
|
||||
|
||||
### Example
|
||||
### Choose The Right Hook API
|
||||
|
||||
```ts
|
||||
export default function register(api) {
|
||||
api.registerHook(
|
||||
"command:new",
|
||||
async () => {
|
||||
// Hook logic here.
|
||||
},
|
||||
{
|
||||
name: "my-plugin.command-new",
|
||||
description: "Runs when /new is invoked",
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
- Use `api.on(...)` for typed runtime lifecycle hooks. This is the preferred surface for middleware, policy, message rewriting, prompt shaping, and tool control.
|
||||
- Use `api.registerHook(...)` only when you want to participate in the legacy internal hook system described in [Hooks](/automation/hooks). This is mainly for coarse command/lifecycle side effects and compatibility with existing HOOK-style automation.
|
||||
|
||||
Notes:
|
||||
Quick rule:
|
||||
|
||||
- Register hooks explicitly via `api.registerHook(...)`.
|
||||
- Hook eligibility rules still apply (OS/bins/env/config requirements).
|
||||
- Plugin-managed hooks show up in `openclaw hooks list` with `plugin:<id>`.
|
||||
- You cannot enable/disable plugin-managed hooks via `openclaw hooks`; enable/disable the plugin instead.
|
||||
- If the handler needs priority, merge semantics, or block/cancel behavior, use `api.on(...)`.
|
||||
- If the handler just wants to react to `command:new`, `command:reset`, `message:sent`, or similar coarse events, `api.registerHook(...)` is fine.
|
||||
|
||||
### Agent lifecycle hooks (`api.on`)
|
||||
### Typed runtime hooks (`api.on`)
|
||||
|
||||
For typed runtime lifecycle hooks, use `api.on(...)`:
|
||||
|
||||
@ -556,7 +542,7 @@ For typed runtime lifecycle hooks, use `api.on(...)`:
|
||||
export default function register(api) {
|
||||
api.on(
|
||||
"before_prompt_build",
|
||||
(event, ctx) => {
|
||||
(_event, _ctx) => {
|
||||
return {
|
||||
prependSystemContext: "Follow company style guide.",
|
||||
};
|
||||
@ -566,10 +552,13 @@ export default function register(api) {
|
||||
}
|
||||
```
|
||||
|
||||
Important hooks for prompt construction:
|
||||
Important hooks for runtime mutation and policy:
|
||||
|
||||
- `before_model_resolve`: runs before session load (`messages` are not available). Use this to deterministically override `modelOverride` or `providerOverride`.
|
||||
- `before_prompt_build`: runs after session load (`messages` are available). Use this to shape prompt input.
|
||||
- `message_sending`: rewrite or cancel outbound content before delivery.
|
||||
- `before_tool_call`: modify or block tool params before the tool runs.
|
||||
- `tool_result_persist`: synchronously transform tool results before they are written to the session transcript.
|
||||
- `before_agent_start`: legacy compatibility hook. Prefer the two explicit hooks above.
|
||||
|
||||
Core-enforced hook policy:
|
||||
@ -601,6 +590,52 @@ Migration guidance:
|
||||
- Move static guidance from `prependContext` to `prependSystemContext` (or `appendSystemContext`) so providers can cache stable system-prefix content.
|
||||
- Keep `prependContext` for per-turn dynamic context that should stay tied to the user message.
|
||||
|
||||
### Internal hook bridge (`api.registerHook`)
|
||||
|
||||
`api.registerHook(...)` lets a plugin bundle the same internal hook system that
|
||||
HOOK packs use, without requiring a separate hook install.
|
||||
|
||||
```ts
|
||||
export default function register(api) {
|
||||
api.registerHook(
|
||||
"command:new",
|
||||
async () => {
|
||||
// Hook logic here.
|
||||
},
|
||||
{
|
||||
name: "my-plugin.command-new",
|
||||
description: "Runs when /new is invoked",
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `api.registerHook(...)` registers legacy internal hooks, not typed plugin lifecycle hooks.
|
||||
- Hook eligibility rules still apply (OS/bins/env/config requirements).
|
||||
- Plugin-managed hooks show up in `openclaw hooks list` with `plugin:<id>`.
|
||||
- You cannot enable/disable plugin-managed hooks via `openclaw hooks`; enable/disable the plugin instead.
|
||||
|
||||
This bridge remains supported for compatibility, but new middleware-style
|
||||
features should target `api.on(...)` instead of extending the internal hook
|
||||
contract. The internal hook system does not offer typed payloads, ordered merge
|
||||
semantics, or veto/block behavior.
|
||||
|
||||
Use `api.registerHook(...)` for:
|
||||
|
||||
- session snapshots on `command:new` or `command:reset`
|
||||
- audit logging on command or message lifecycle events
|
||||
- coarse side effects such as file writes, webhooks, or notifications
|
||||
- plugin-packaged equivalents of installed HOOK packs
|
||||
|
||||
Do not use `api.registerHook(...)` for:
|
||||
|
||||
- prompt mutation or prompt injection policy
|
||||
- tool allow/deny gates
|
||||
- outbound content rewriting or cancellation
|
||||
- other security or middleware proposals that depend on order or blocking
|
||||
|
||||
## Provider plugins (model auth)
|
||||
|
||||
Plugins can register **model provider auth** flows so users can run OAuth or
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user