插件 Hook
插件钩子(Plugin hooks)让原生 OpenClaw 插件可以观察或改变代理运行、工具调用、消息投递和生命周期事件。使用 api.on("hook_name", handler) 注册一个类型化处理器,并返回该钩子文档中记载的结果。
有三套不同的钩子系统:
| 你想要…… | 使用 |
|---|---|
| 修改提示词、对工具进行门控、自定义回复,或集成插件生命周期 | 本页中的类型化插件钩子:api.on("before_tool_call", ...) |
为 /new、/reset、/stop 或 bootstrap 事件运行操作员安装的脚本 |
内部钩子:HOOK.md 以及诸如 command:new 或 agent:bootstrap 的冒号事件名称 |
| 通过 HTTP 从外部服务触发代理 | Webhooks:Gateway HTTP 端点 |
插件也可以用 api.registerHook(...) 注册内部钩子。但这不是类型化 API:在那里注册诸如 before_tool_call 这样的下划线名称会产生警告,并且类型化运行器永远不会调用该注册。请对钩子目录中的每个钩子使用 api.on(...)。
快速开始¶
本示例会在不调用模型的情况下,回复一条包含 hook-demo-check 的用户消息。它假定你已经有一个可正常工作的 Gateway,并能向其发送普通聊天消息。关于包元数据、发布和安装选项,请参阅构建插件和插件清单。
创建一个本地 hook-demo 目录,包含以下文件:
```json package.json { "name": "hook-demo", "version": "1.0.0", "type": "module", "openclaw": { "extensions": ["./index.ts"] } }
```json openclaw.plugin.json
{
"id": "hook-demo",
"name": "Hook Demo",
"activation": { "onStartup": true },
"configSchema": { "type": "object", "additionalProperties": false }
}
```typescript index.ts import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({ id: "hook-demo", name: "Hook Demo", description: "Reply to a hook check without a model call.", register(api) { api.on( "before_agent_reply", (event) => { if (event.cleanedBody.includes("hook-demo-check")) { return { handled: true, reply: { text: "Hook is working." } }; } }, { eligibleTriggers: ["user"] }, ); }, });
在加载本地插件之前请先审查其代码:原生插件运行在 Gateway 进程中。链接并启用该目录(`--force` 表示确认从本地源安装):
```bash
openclaw plugins install --link ./hook-demo --force
openclaw plugins enable hook-demo
在 openclaw.json 中授予此插件访问会话钩子的权限:
{
"plugins": {
"entries": {
"hook-demo": {
"enabled": true,
"hooks": { "allowConversationAccess": true }
}
}
}
}
将该条目合并到现有配置中,然后让默认的混合重载模式应用它并检查:
以普通聊天消息发送 hook-demo-check,应得到 Hook is working.;其他消息继续走正常的代理路径。如果钩子没有运行,请参阅故障排查。
尽管名称如此,cleanedBody 实际是准备好的运行提示词(prompt),可能包含频道上下文。该示例匹配一个独特标记,而不是假定该字段只是发送者的原始文本。
权限与作用域¶
钩子注册不会绕过插件加载规则。插件必须已加载并启用;plugins.enabled、plugins.allow 和 plugins.deny 仍然生效。修改插件代码后,运行 openclaw plugins reload <id>。在默认的混合重载模式下,钩子策略更改会热重载现有插件运行时。
- 非随附插件需要为
before_model_resolve、agent_turn_prepare、before_prompt_build、before_agent_reply、llm_input、llm_output、before_agent_finalize、agent_end和before_agent_run显式设置plugins.entries.<id>.hooks.allowConversationAccess: true。随附插件默认被允许,除非此选项被显式设为false。 allowPromptInjection: false会阻止agent_turn_prepare、before_prompt_build、heartbeat_prompt_contribution以及持久的下一轮注入。该选项默认为允许,但并不授予会话访问权限。因此前两个钩子需要同时具备这两种权限。- 这些是特定的注册门控,不是沙箱,也不是对所有能查看消息数据的钩子的通用过滤器。只安装你信任的插件。
隐身会话不会分发 llm_input 或 llm_output 观察事件。它们的 agent_end 钩子仍会收到运行身份、成功状态和持续时间,以便进行清理和结算,但收到的 messages 为空,且没有 error 文本。策略、提供商、审批以及显式调用的工具钩子仍然保持活动。此边界不会对插件进行沙箱隔离,也不会禁用原生 harness 遥测。
类型化处理器接收 (event, ctx)。事件描述操作;第二个参数携带钩子特定的上下文。ctx.agentId、ctx.sessionKey 和 ctx.runId 等字段在许多钩子上是可选的,在触发路径上可能缺失。注册不会自动限定到某个代理或会话:需要时请在处理器中检查上下文。
在注册闭包内,通过 api.pluginConfig 读取插件解析后的设置。类型化钩子不会收到通用的 event.context.pluginConfig 字段;该字段属于内部 api.registerHook(...) 事件契约。在混合重载模式下,默认情况下,编辑 plugins.entries.<id>.config 会替换插件实例,并使用新设置重新运行注册。
选择钩子¶
| 任务 | 钩子 |
|---|---|
| 不经过模型调用直接回复 | before_agent_reply → { handled: true, reply };省略 reply 表示静默 |
| 为某一轮添加上下文或收窄工具 | before_prompt_build |
| 在受支持的 runner 上对模型输入进行门控 | before_agent_run → { outcome: "block", reason, message? } |
| 阻止工具或请求审批 | before_tool_call |
| 重写完整出站回复,包括媒体 | reply_payload_sending |
| 重写出站文本或取消发送 | message_sending |
| 在不获取原始对话文本的情况下收集模型计时 | model_call_started / model_call_ended |
| 在一轮结束后或关闭时刷新状态 | agent_end / gateway_stop |
目录是注册 API,并不保证每个运行时都会发出每个钩子。例如,before_agent_run 由嵌入式和 CLI runner 实现;不要将其作为 Codex 或 Copilot 的输入门控。原生工具、转录和压缩边界也各不相同。参见 Codex 钩子边界 和 Agent harness 插件。
故障排除¶
| 症状 | 检查 |
|---|---|
| 插件已加载但处理程序从未运行 | 使用 api.on 处理类型化名称,检查 openclaw plugins inspect <id> --runtime --json,并查看诊断信息中是否有被阻止的注册。运行时检查会在检查进程中加载插件;代码更改后请使用 openclaw plugins reload <id>。 |
| 对话钩子被阻止 | 设置 plugins.entries.<id>.hooks.allowConversationAccess: true;对于 prompt 钩子,还请检查 allowPromptInjection 是否为 false。这些键应位于 hooks 下,而不是插件的 config 中。 |
| 钩子仅对一个运行时或触发器有效 | 检查运行时边界和 eligibleTriggers。缺少上下文字段并不能证明发送者、agent 或授权状态不同。 |
| 持久化重写没有效果 | 同步返回 { message }。async 处理程序的结果会被忽略。 |
| 超时的钩子仍会执行工作 | 超时结束的是宿主机的 await,而不是插件工作。请自行将可用的 abort 信号传递到 I/O,并绑定插件拥有的工作。 |
| 某个插件的重写消失了 | 检查该钩子的合并规则和优先级。message_sending 使用最后返回的内容;reply_payload_sending 会将每个更新后的 payload 继续传递。 |
即将弃用¶
一些与钩子相关的接口已弃用但仍受支持。移除资格在插件兼容性注册表中按接口跟踪,以 removeAfter 日期或显式移除门控表示,而不是在主要版本边界处。请立即迁移:
- 纯文本通道信封 位于
inbound_claim和message_received处理程序中。优先使用类型化字段,而不是解析扁平信封文本:inbound_claim暴露event.bodyForAgent;message_received暴露event.content和结构化元数据,而不是BodyForAgent字段。参见 纯文本通道信封 → BodyForAgent。 before_tool_call中的onResolution现在使用类型化PluginApprovalResolution联合类型(allow-once/allow-always/deny/timeout/cancelled),而不是自由格式string。api.registerSessionExtension/api.enqueueNextTurnInjection仍作为顶层兼容性别名保留。新插件应使用api.session.state.registerSessionExtension(...)和api.session.workflow.enqueueNextTurnInjection(...)。
完整列表——内存能力注册、provider thinking profile、外部 auth provider、provider discovery 类型、task runtime 访问器,以及 command-auth → command-status 重命名——参见 Plugin SDK 迁移 → 当前弃用项。
各章节迁移位置¶
单页版本的每个章节现在都位于此页面或以下五个子页面之一。单页版本中的锚点仍会在此处解析。
钩子参考¶
钩子参考 — 注册规则、执行契约、每个处理程序的预算,以及完整的类型化钩子目录。
工具调用策略钩子¶
工具调用策略钩子 — 参数重写、拦截、审批、exec 环境贡献以及转录持久化。
Prompt 与会话钩子¶
Prompt 与会话钩子 — 模型解析、Prompt 构建、最终化,以及持久的插件拥有的会话状态。
消息与投递钩子¶
消息与投递钩子 — 入站拦截、回复接管以及出站投递策略。
Gateway 与安装生命周期钩子¶
Gateway 与安装生命周期钩子 — 安装时检查、Gateway 服务生命周期,以及安全的外部 cron 投影。
相关¶
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw