跳转至

插件 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 }
      }
    }
  }
}

将该条目合并到现有配置中,然后让默认的混合重载模式应用它并检查:

openclaw plugins inspect hook-demo --runtime --json

以普通聊天消息发送 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