Compare commits
2 Commits
main
...
vincentkoc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84075f02bd | ||
|
|
3fba484d96 |
@ -8,16 +8,28 @@ title: "Hooks"
|
|||||||
|
|
||||||
# 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
|
## Getting Oriented
|
||||||
|
|
||||||
Hooks are small scripts that run when something happens. There are two kinds:
|
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.
|
- **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:
|
Common uses:
|
||||||
|
|
||||||
@ -37,6 +49,14 @@ The hooks system allows you to:
|
|||||||
- Trigger custom automations on agent lifecycle events
|
- Trigger custom automations on agent lifecycle events
|
||||||
- Extend OpenClaw's behavior without modifying core code
|
- 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
|
## Getting Started
|
||||||
|
|
||||||
### Bundled Hooks
|
### Bundled Hooks
|
||||||
@ -199,7 +219,7 @@ const myHandler = async (event) => {
|
|||||||
|
|
||||||
// Your custom logic here
|
// Your custom logic here
|
||||||
|
|
||||||
// Optionally send message to user
|
// Optionally send a reply on replyable surfaces
|
||||||
event.messages.push("✨ My hook executed!");
|
event.messages.push("✨ My hook executed!");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -216,7 +236,7 @@ Each event includes:
|
|||||||
action: string, // e.g., 'new', 'reset', 'stop', 'received', 'sent'
|
action: string, // e.g., 'new', 'reset', 'stop', 'received', 'sent'
|
||||||
sessionKey: string, // Session identifier
|
sessionKey: string, // Session identifier
|
||||||
timestamp: Date, // When the event occurred
|
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: {
|
context: {
|
||||||
// Command events:
|
// Command events:
|
||||||
sessionEntry?: SessionEntry,
|
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
|
## Event Types
|
||||||
|
|
||||||
### Command Events
|
### Command Events
|
||||||
|
|||||||
@ -521,34 +521,20 @@ Then enable it in config:
|
|||||||
|
|
||||||
## Plugin hooks
|
## Plugin hooks
|
||||||
|
|
||||||
Plugins can register hooks at runtime. This lets a plugin bundle event-driven
|
Plugins can register hooks at runtime, but there are two different APIs with
|
||||||
automation without a separate hook pack install.
|
different jobs.
|
||||||
|
|
||||||
### Example
|
### Choose The Right Hook API
|
||||||
|
|
||||||
```ts
|
- Use `api.on(...)` for typed runtime lifecycle hooks. This is the preferred surface for middleware, policy, message rewriting, prompt shaping, and tool control.
|
||||||
export default function register(api) {
|
- 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.
|
||||||
api.registerHook(
|
|
||||||
"command:new",
|
|
||||||
async () => {
|
|
||||||
// Hook logic here.
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "my-plugin.command-new",
|
|
||||||
description: "Runs when /new is invoked",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
Quick rule:
|
||||||
|
|
||||||
- Register hooks explicitly via `api.registerHook(...)`.
|
- If the handler needs priority, merge semantics, or block/cancel behavior, use `api.on(...)`.
|
||||||
- Hook eligibility rules still apply (OS/bins/env/config requirements).
|
- If the handler just wants to react to `command:new`, `command:reset`, `message:sent`, or similar coarse events, `api.registerHook(...)` is fine.
|
||||||
- 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.
|
|
||||||
|
|
||||||
### Agent lifecycle hooks (`api.on`)
|
### Typed runtime hooks (`api.on`)
|
||||||
|
|
||||||
For typed runtime lifecycle hooks, use `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) {
|
export default function register(api) {
|
||||||
api.on(
|
api.on(
|
||||||
"before_prompt_build",
|
"before_prompt_build",
|
||||||
(event, ctx) => {
|
(_event, _ctx) => {
|
||||||
return {
|
return {
|
||||||
prependSystemContext: "Follow company style guide.",
|
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_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.
|
- `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.
|
- `before_agent_start`: legacy compatibility hook. Prefer the two explicit hooks above.
|
||||||
|
|
||||||
Core-enforced hook policy:
|
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.
|
- 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.
|
- 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)
|
## Provider plugins (model auth)
|
||||||
|
|
||||||
Plugins can register **model provider auth** flows so users can run OAuth or
|
Plugins can register **model provider auth** flows so users can run OAuth or
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user