--- summary: "插件钩子如何注册和执行,以及完整的类型化钩子目录" read_when: - "您正在连接类型化处理程序并需要排序、超时和失败规则" - "您想知道表面存在哪种类型的钩子" - "您正在注册技能生命周期或配对观察者" title: "挂钩参考" sidebarTitle: "挂钩参考" --- 注册规则、执行合同、每个处理程序的预算以及完整的类型化挂钩目录。 [ 插件挂钩 ](/plugins/hooks) 指南的一部分。 ## 注册执行 保持 `register(api)` 同步并在那里注册处理程序。除了两个同步持久性挂钩之外,处理程序本身可以是异步的。 处理程序默认优先级为 `0`;优先级较高的优先级优先,注册顺序打破了平局。执行取决于钩子类型: | 类型 | 执行合同 | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | 调整 | 顺序;结果根据下面的钩子合同进行合并。返回重写通常不会更改传递给后续处理程序的事件。 | | 宣称 | 顺序;第一个 `{ handled: true }` 获胜并跳过剩余的处理程序。 | | 闸门 | 顺序;一个块会停止剩余的处理程序。 | | 观察 | 处理程序同时运行;返回值被忽略。发射器可以等待完成或调度即发即弃。 | | 同步modify/gate | 同步,按优先顺序;每个处理程序都会看到最新的消息。承诺被忽略并带有警告。 | | 评价 | 技能评估器同时运行并产生单独的归因结果。 | 优先级不会对观察副作用进行序列化。即发即弃事件可以与后续事件重叠,并且回调不是持久的事件队列。显式返回修改,而不是依赖就地突变。 `api.on(name, handler, opts?)` 接受: | 选项 | 作用 | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `matcher` | 由 `before_tool_call` 或 `after_tool_call` 处理的标准 OpenClaw 工具 ID 的非空列表,例如 `exec`、`apply_patch` 或 `spawn_agent`。省略匹配所有工具。空列表、通配符、空白和特定于提供商的别名无效。 | | `priority` | 排序;数值越高越先运行。 | | `registrationId` | 插件内一次注册的稳定身份。技能评估者将其用作`evaluatorId`;否则使用插件 ID。 | | `timeoutMs` | 每个处理程序异步等待预算。过期应用下面的钩子失败策略;它不会取消处理程序或其副作用。忽略使用跑步者的默认值(如果有)。 | | `eligibleTriggers` | 仅适用于 `before_agent_reply`,将宿主分发限制为 `cron`、`heartbeat` 或 `user` 中的一个或多个触发器。 | | `eligibleDispatchKinds` | 仅适用于 `reply_dispatch`,将主机调度限制为 `agent`、`acp` 或两者。省略处理所有调度类型。 | | `requiresToolAuthority` | 仅适用于 `before_prompt_build`,在主机完成当前回合的工具表面并提供临时 `ctx.toolAuthority` 后运行处理程序。使用它进行必须遵循工具策略的上下文检索。 | 触发器资格由宿主在调用处理器之前强制执行。因此,使用 `eligibleTriggers: ["heartbeat", "cron"]` 注册的钩子对于用户轮次处于非活动状态,包括恢复的用户轮次。省略、为空、格式错误或部分包含未知值的列表仍不受限制,因此钩子会在这些轮次中运行。其他钩子类型不接受此选项。 运维人员可以在不修改插件代码的情况下设置钩子预算: ```json { "plugins": { "entries": { "my-plugin": { "hooks": { "timeoutMs": 30000, "timeouts": { "before_prompt_build": 90000, "agent_end": 60000 } } } } } } ``` `hooks.timeouts.` 会覆盖 `hooks.timeoutMs`,后者会覆盖插件编写的 `api.on(..., { timeoutMs })` 值。两个操作员配置字段接受最多 600000 毫秒的正整数。对于已知的慢钩子,更喜欢按钩子覆盖,这样一个插件就不会在任何地方都获得更长的预算。 超时处理程序 Promise 会继续运行,因为挂钩回调不会收到超时拥有的取消信号。 `before_tool_call` 可以接收所属工具调用的 `ctx.abortSignal`,但钩子超时到期不会中止它。当插件工作仍在进行时,钩子调度可以释放其网关许可。拥有长时间运行工作的插件必须提供自己的取消和关闭生命周期。 在结果变得不合格后,提示构建处理程序可以使用 `ctx.hookInvocation.assertActive()` 拒绝副作用。请参阅 [Handler 生命周期](/plugins/hooks/prompt-and-session#handler-lifetime)。 标准运行程序应用这些默认值 **per handler**: | 钩子 | 默认超时 | 抛出错误或超时时 | | -------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ---------------------------------------------------------------- | | `before_agent_run`, `before_tool_call`, `before_install` | 15秒 | 阻止运行、工具调用或安装 | | `before_agent_finalize`, `before_prompt_build`, `message_sending`, `reply_payload_sending`, `resolve_exec_env` | 15秒 | 记录并跳过失败的处理程序;保留其他成功结果 | | `agent_end`, `before_compaction`, `after_compaction`, `skill_changed` | 30 秒 | 记录并继续 | | `channel_pairing_requested` | 2秒 | 记录并继续 | | `gateway_stop` | 5秒 | 记录并继续关闭 | | 其他异步钩子,包括声明钩子 | 除非配置,否则没有跑步者超时 | 记录并继续 | | `tool_result_persist`, `before_message_write` | 无异步超时 | 记录同步错误;失败的结果将被忽略 | 发射器可以施加更严格的整体生命周期预算,例如下面的关闭 `session_end` 排水。超时仅限制异步等待;它不能中断同步JavaScript。对于策略要求,请使用一个阻止错误操作的门,而不是假设观察或交付挂钩将拒绝失败的操作。 对于声明挂钩,继续意味着尝试下一个处理程序。如果无人认领,则由呼叫者决定如何处理;绑定对话失败的 `inbound_claim` 可以生成绑定通知而不是普通代理回复。 使用 `createReplyDispatcher` 的通道插件同样可以通过 `beforeDeliverOptions: { timeoutMs }` 声明更大的正向每阶段预算,或者在通过 `dispatcher.appendBeforeDeliver(handler, { timeoutMs })` 追加工作时指定。若没有所有者声明的预算,这些回调会使用相同的 15 秒默认值,这样一个卡住的回调就不会占用序列化交付通道。 ## 钩子目录 钩子按其延伸的表面进行分组。种类参见上述执行合同;修改钩子不是观察钩子。 **Agent 回合** | 钩子 | 类型 | 用途 | | ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- | | `before_model_resolve` | 调整 | 在加载会话消息之前覆盖提供程序、模型或运行的后备链 | | `agent_turn_prepare` | 调整 | 检查耗尽的插件转注入并在提示钩子之前添加上下文 | | `before_prompt_build` | 调整 | 添加提示上下文,缩小当前回合提交的工具范围,或执行授权的策略后充实 | | `before_agent_run` | 闸门 | 在模型提交之前检查提示和会话消息;节点在工作本地上下文组装之前打开门网关输入 | | `before_agent_reply` | 宣称 | 使用合成回复或静默来提前结束模型回合 | | `before_agent_finalize` | 调整 | 检查自然生成的最终答案,并请求模型再次处理 | | `agent_end` | 观察 | 观察最终消息、成功状态和运行时长 | | `heartbeat_prompt_contribution` | 调整 | 为后台监控和生命周期插件添加仅用于心跳的上下文 | **对话观察** | 钩子 | 类型 | 用途 | | ----------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------ | | `model_call_started` / `model_call_ended` | 观察 | 净化的 provider/model 调用元数据:计时、结果、有界请求 ID 哈希值。无提示或响应内容。 | | `llm_input` | 观察 | 提供方输入:系统提示词、提示词、历史记录 | | `llm_output` | 观察 | 提供方输出、用量,以及可用时解析得到的 `contextTokenBudget` | **工具** | 钩子 | 类型 | 用途 | | ---------------------- | ------------------ | ---------------------------------------------------------- | | `before_tool_call` | 修改/门 | 重写工具参数、阻止执行或要求批准 | | `after_tool_call` | 观察 | 观察工具结果、错误和持续时间 | | `resolve_exec_env` | 调整 | 为 `exec` 提供插件拥有的环境变量 | | `tool_result_persist` | 同步修改 | 在转录持久化之前重写 toolResult 消息 | | `before_message_write` | 同步修改/门控 | 在转录持久化之前重写或阻止消息 | **消息与传递** | 钩子 | 类型 | 用途 | | --------------------------- | ------------- | -------------------------------------------------------------------------- | | `inbound_claim` | 宣称 | 为拥有该消息会话绑定的插件认领入站消息 | | `channel_pairing_requested` | 观察 | 观察新创建的 DM 配对请求 | | `message_received` | 观察 | 观察入站内容、发送者、线程和元数据 | | `message_sending` | 修改/门 | 重写出站内容或取消传递 | | `reply_payload_sending` | 修改/门 | 在传递前修改或取消规范化的回复负载 | | `message_sent` | 观察 | 观察出站传递的成功或失败 | | `before_dispatch` | 宣称 | 在正常模型调度之前处理入站消息 | | `reply_dispatch` | 宣称 | 自己的回复生成和发送,而不是默认的模型路径 | `inbound_claim` 不是全局预路由广播。 OpenClaw 仅针对拥有消息的核心管理对话绑定的插件调用它。要在模型输入之前抑制普通代理转动而不保留转录中的原始提示,请在受支持的运行器上使用 `before_agent_run`。要通过合成回复或静音来短路代理回合,请使用 `before_agent_reply`。 对于心跳轮换,当主机知道底层系统事件队列时,`before_agent_reply` 会收到 `ctx.heartbeatEventQueueSessionKey`。这可能与隔离心跳运行的 `ctx.sessionKey` 不同。使用提供的键进行事件查找;不要通过删除 `:heartbeat` 后缀来派生基本会话,因为配置的会话可以命名为 `heartbeat`。对于较旧的主机,该字段是可选的。没有它,插件只能检查 `ctx.sessionKey`;他们无法推断隔离运行的基本队列。该字段标识现有队列并且不授予额外的访问权限。 **会话与压缩** | 钩子 | 类型 | 用途 | | ---------------------------------------- | ------- | ------------------------------------------------------------ | | `session_start` / `session_end` | 观察 | 跟踪会话生命周期边界 | | `before_compaction` / `after_compaction` | 观察 | 观察压实边界;没有重写或否决结果 | | `before_reset` | 观察 | 观察会话重置事件(`/reset`、程序化重置) | 即使没有历史记录更改,成功的引擎拥有的内置本机压缩尝试也会发出 `after_compaction`,并带有 `compactedCount: 0`。本机跳过包括仅心跳记录和自动保留策略无操作。完成意味着尝试完成;它不保证历史重写。失败或中止的尝试不会发出该完成挂钩。 `session_end.reason` 是 `new`、`reset`、`idle`、`daily`、`compaction`、`deleted`、`shutdown`、`restart` 之一,或`unknown`。 `session_start` 无原因字段;它可以包括 `resumedFrom`。 Shutdown/restart 事件来自活动会话的网关终结器,因此插件可以在进程退出之前关闭会话状态。 在当前主机上,`session_end` 上下文包括 `endedTranscript`。当 OpenClaw 具有不可变的结束会话源时,具有会话访问权限的插件会接收 `{ available: true, readTail }`。看涨期权必须提供正值 `maxMessages` 和 `maxBytes`; OpenClaw 强制执行主机上限并返回 `{ messages, totalMessages, truncated }`。当允许的处理程序处于活动状态时,读取器仍然有效。处理程序解决、其配置的挂钩超时、所属插件实例强制退出或 shutdown/restart 耗尽截止时间后,保留或仍待处理的读取会被拒绝。 当不存在安全源时,值为`{ available: false, reason }`。当前的原因包括缺少对话权限、没有稳定截止的生命周期、已删除的隐身状态、不可用的存档以及不受支持的源。这是一个明确的非结果,而不是一个空的成绩单。根据此合同编译的插件仍应将缺失的字段视为较旧的主机。 `session_end` 本身仍然是元数据挂钩,无需对话访问即可注册。对于非捆绑插件,`plugins.entries..hooks.allowConversationAccess=true` 仅授予有界的结束转录阅读器。捆绑插件遵循相同的有效策略,可以通过 `allowConversationAccess: false` 拒绝。权限更改适用于成功发布的插件运行时替换所承认的处理程序。已经运行的处理程序保留其生成的授权,同时其有界调用仍然被允许;处理程序结算、挂钩超时、强制实例退役或关闭耗尽到期都会结束租约。前一个版本和后续版本可能会重叠。 关闭和重新启动在所有活动会话和插件处理程序中共享一个 **2 秒总计 `session_end` 消耗预算**;预算不是每个处理程序的。快速返回或保持最终确定有界和持久性崩溃一致。如果预算到期,OpenClaw 会记录 `shutdown session-end drain timed out` 并继续关闭,因此可以中断未完成的插件工作。 对于 `sessions.create` 与 `parentSessionKey` 和 `emitCommandHooks: true` 的调用,不同的子级始终接收 `session_start`。调用者声明父级是否也接收终端 `session_end` 和 `succeedsParent`:`true` 表示后继,`false` 表示并行子级。省略保留了遗留的父级翻转行为。在这两种情况下,`command:new` 和 `before_reset` 挂钩仍然描述请求的 `/new` 操作。 **子代理** - `subagent_spawned` / `subagent_ended` - 观察子代理的启动和完成。 - `subagent_progress` - 观察便携式 `started` / `ended` 后台子运行的进度;包括 `runId`、`childSessionKey`、可选请求者路由以及 `ended` 上的结果。 - `subagent_delivery_target` - 当没有核心会话绑定可以投影路线时,修改兼容性挂钩以完成交付。第一个返回的 `origin` 获胜。 - `subagent_spawned` 在 OpenClaw 已在启动前解析出子会话原生模型时,会包含 `resolvedModel` 和 `resolvedProvider`。 - `subagent_ended` 包含 `targetSessionKey`(标识 - 与 `subagent_spawned.childSessionKey` 匹配)、`targetKind`(`"subagent"` 或 `"acp"`)、`reason`、可选的 `outcome`(`"ok"`、`"error"`、`"timeout"`、`"killed"`、`"reset"` 或 `"deleted"`)、可选的 `error`、`runId`、`endedAt`、`accountId` 和 `sendFarewell`。它**不**包含 `agentId` 或 `childSessionKey`;请使用 `targetSessionKey` 与匹配的 `subagent_spawned` 事件进行关联。 **生命周期** | 钩子 | 类型 | 用途 | | -------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------- | | `gateway_start` / `gateway_stop` | 观察 | 与 Gateway 一同启动或停止插件拥有的服务 | | `cron_reconciled` | 观察 | 在启动或重新加载后与完整的 Gateway cron 状态进行协调 | | `cron_changed` | 观察 | 观察 Gateway 拥有的 cron 生命周期变化(添加、更新、移除、启动、完成、计划) | | `before_install` | 修改/门 | 从已加载的插件运行时检查暂存的技能或插件安装材料 | | `skill_changed` | 观察 | 观察已提交的实时技能创建、更新和移除事件 | ### 技能生命周期 `skill_changed` 在实时技能创建、更新或删除提交后触发,并包括带有内容和树哈希的可选 before/after 工件,以及可用的声明版本和源版本。 ### 频道配对请求 当未配对的 DM 发送者创建待处理的配对请求后,插件需要通知操作员或写入审核记录时,请使用 `channel_pairing_requested`。该钩子在创建请求时调度;配对回复的通道传递不会因缓慢或失败的挂钩处理程序而延迟。 ```typescript api.on("channel_pairing_requested", async (event) => { await notifyOperator({ text: `New ${event.channel} pairing request from ${event.senderId}: ${event.code}`, }); }); ``` 该钩子仅用于观察。它不会批准、拒绝、抑制或重写配对回复。有效负载包含通道、可选的 `accountId`、按通道范围的 `senderId`、配对 `code` 以及通道元数据。请将配对代码视为实时一次性批准凭证,并仅将其交付给受信任的操作员接收端。请将 `metadata` 视为不受信任的、由发送者提供的身份文本。该钩子不包含传入消息正文或媒体。