配置 — 钩子
入站 Hook 键位于 hooks.* 下。
有关完整键索引和其他顶级配置域,请参阅 配置参考。
Hooks¶
hooks.* 配置通用 Gateway HTTP 入站。有关设置和验证后的第一个请求,请参阅 Webhooks。这与 内部 hooks(hooks.internal、HOOK.md)是分开的。
{
hooks: {
enabled: true,
token: "<long-random-hook-token>",
path: "/hooks",
allowedAgentIds: ["main"],
allowRequestSessionKey: false,
},
}
将 main 替换为预期配置的 agent。Hook 令牌授予的是入站访问权限,而非经过认证的发送者身份;应将负载内容视为不受信任的数据,并分别限制目标 agent 的工具和工作区。
| 字段 | 默认值 | 契约 |
|---|---|---|
enabled |
false |
启用 HTTP 端点。需要非空的 token。 |
token |
未设置 | 共享的 Hook 密钥字符串。请使用专门的较长随机值;此处不支持 SecretRef 对象。 |
path |
/hooks |
专用基础路径;自动添加前导斜杠并移除尾部斜杠。拒绝 /。 |
allowedAgentIds |
不受限制 | 有效的 agent 允许列表,包括默认 agent 路径。省略或包含 "*" 时允许全部;[] 拒绝全部。 |
defaultSessionKey |
未设置 | 当未提供请求/映射键时使用的逻辑 agent 运行键;否则会生成新的 hook:<uuid>。它本身不会启用持久会话。 |
allowRequestSessionKey |
false |
允许来自 /agent、/wake 以及从负载派生的映射/转换值的键。 |
allowedSessionKeyPrefixes |
不受限制 | 用于显式请求/映射键以及默认/生成键的不区分大小写前缀。空列表或全为空白项的列表不施加任何限制;否则空白项会被忽略。请参阅下方的会话策略。 |
presets |
[] |
内置映射追加在自定义映射之后。可用预设:"gmail";未知名称不会添加任何映射。 |
mappings |
[] |
有序映射列表;首个匹配生效。请参阅 映射详情。 |
transformsDir |
<config-dir>/hooks/transforms |
转换目录,限定在该根目录内,包括对符号链接的限定。通常为 ~/.openclaw/hooks/transforms。 |
gmail |
未设置 | Gmail 传输与处理默认值;请参阅 Gmail 集成。 |
internal |
独立子系统 | 内部事件 Hook 配置;请参阅 Hooks。它不会启用 HTTP 入站。 |
hooks.token 应区别于当前生效的 Gateway 共享密钥认证(gateway.auth.token / OPENCLAW_GATEWAY_TOKEN 或 gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD)。启动时若复用,会记录一条非致命警告;openclaw security audit 会报告一项严重发现,包括在审计时提供的密码认证(--auth password --password <password>)。使用 openclaw doctor --fix 轮换已持久化的复用 Hook 令牌,然后更新所有外部发送方。
Hook HTTP 契约¶
以下路径假定 hooks.path: "/hooks";如果配置不同,请替换该前缀。发送 POST 请求,并附带 JSON 请求体和 Content-Type: application/json。
认证接受 Authorization: Bearer <token> 或 x-openclaw-token。非空的 Bearer 令牌优先。即使同时存在有效的头部,token 查询参数也会被拒绝并返回 400。缺失或错误的凭据返回 401。在 60 秒窗口内失败 20 次后,来自该客户端的进一步无效认证尝试将以 429 和 Retry-After 进行限流;有效认证会重置计数器。回环地址也不例外。在暴露代理路由之前,请正确配置受信任的代理归属。
正常请求体限制为 256 KiB,请求体读取超时为 30 秒。Gmail 路径映射会获得下文所述的更大派生额度。通用 Hook 会解析 JSON,但不要求 JSON content-type 头部。
| 端点 | 负载与结果 |
|---|---|
POST /hooks/wake |
必需的非空 text;可选 mode(默认 "now" 或 "next-heartbeat")、agentId、sessionKey。返回 200 { ok: true, mode, eventOutcome };当队列接受唤醒时 eventOutcome 为 "queued",当同一唤醒已经是队列中最近待处理事件时为 "coalesced"。无论哪种情况,now 都会请求一次心跳;该结果不能证明心跳已执行。 |
POST /hooks/agent |
Agent 负载。默认在会话/全局放置准入后返回 200 { ok: true, runId }。当 waitForCompletion: true 时,等待已准入的运行,并在 completion 中添加有界的终结执行/交付事实。 |
POST /hooks/<name> |
首个匹配的映射会产生唤醒/Agent 操作。没有匹配的映射返回 404;没有操作返回 204。Agent 扇出具有批量响应契约。 |
直接的 /wake 和 /agent 端点优先于具有这些名称的映射。/hooks 本身没有操作。
| 状态 | 含义 |
|---|---|
400 |
无效的 JSON、负载、路由/会话策略或交付/账户选择。重试前请阅读 error。 |
401 |
Hook 身份验证失败。 |
404 |
该路径没有 Hook 操作或映射。已禁用的 Hook 会落入 Gateway 路由的其余部分。 |
405 |
方法错误;返回 Allow: POST。 |
408 |
请求体超时。 |
413 |
请求体超过该路径的字节限制。 |
429 |
身份验证失败限流;请遵循 Retry-After。 |
409 |
Agent 准入被拒绝,因为目标会话已更改或无法接受工作。 |
500 |
映射/转换异常(hook mapping failed);检查 Gateway 日志。 |
502 |
Agent 在准入前准备失败。 |
503 |
单次运行未在 15 秒内完成准入;该排队工作将被取消。扇出待处理工作不同:它会在后台继续。Gateway 暂停/重启也可能返回 503 gateway_unavailable。 |
Agent 准入失败使用 { ok: false, error, runId? }。早期的方法/身份验证/路径失败可能是纯文本;不要假设每个错误响应都是 JSON。15 秒的准入截止时间与请求体读取超时以及 Agent 回合的 timeoutSeconds 是分开的。HTTP 成功不能证明模型结果或通道交付。参见Hook 验证。
Hook agent 负载¶
| 字段 | 默认值 | 契约 |
|---|---|---|
| 字段 | 默认值 | 约定 |
|---|---|---|
message |
必填 | 非空的代理输入文本;外部内容会进行安全包装。 |
name |
"Hook" |
日志/完成事件中使用的 Hook 标签。 |
agentId |
已解析的所有者 | 直接提供时必须指定一个已配置的代理。当无法解析隐式/保留的所有者时必填。 |
sessionKey |
默认/生成的密钥 | 受调用方密钥选择加入和前缀策略约束。 |
sessionMode |
"isolated" |
"isolated" 创建新的运行会话;"persistent" 复用已解析的会话。 |
idempotencyKey |
未设置 | 可选的重放密钥;请求头优先。参见下文的重试。 |
waitForCompletion |
false |
仅限直接 /agent。当为 true 时,在准入后保持 HTTP 响应打开,并返回带有 status 和可用投递事实的 completion。映射和扇出仍仅限准入。 |
wakeMode |
"now" |
"now" 或 "next-heartbeat";控制完成事件的唤醒,而不是代理是否立即被调度。 |
deliver |
true |
仅 false 表示退出。没有直接目的地时,成功输出可成为主会话完成事件。false 会记录成功完成而不发出通知,并忽略目的地字段。非正常执行结果会产生状态事件。 |
channel |
直接投递时无 | 已注册的具体通道 id;必须与 to 配对。直接 /agent 不能使用 "last"。 |
to |
未设置 | 直接通知投递的非空接收者,与 channel 配对。 |
accountId |
通道默认值 | 选择一个已配置且已启用的账户;需要 channel 和 to。未知、已禁用或无效的选择会在调度前返回 400。 |
model |
代理/模型默认值 | 模型 id 或别名覆盖,受模型可用性和允许列表策略约束。 |
thinking |
代理/模型默认值 | 本次运行的思考覆盖。 |
timeoutSeconds |
代理超时 | 正数轮次超时覆盖;直接负载值向下取整为整秒。无效或非正值会被忽略。 |
省略所有目的地字段时,运行将没有直接通知目的地。
仅提供部分目的地时,在投递启用情况下会以 400 失败。
deliver: false 禁用通知,而不是禁用代理使用消息工具的能力;必要时在代理策略中限制这些工具。
Hook 会话与代理策略¶
直接请求的代理 id 必须存在。映射代理 id 会解析到已配置的代理,对于未知映射 id 使用旧版默认代理回退。如果无法解析所有者,准入会失败,而不是凭空创建一个代理。有效代理必须通过 allowedAgentIds;全局会话存储的所有权也会被强制检查。带代理前缀的密钥会重新限定到所选代理,并再次进行前缀检查。
对于固定的全局会话存储,省略的目标会优先使用其持久化的 agents.defaults.sessionStore.agentId 所有者,而不是运行时默认值。与该所有者冲突的显式目标会被拒绝。持久化所有者仍必须通过 allowedAgentIds。
键从请求/映射中解析,然后是 hooks.defaultSessionKey,最后是一个生成的 hook:<uuid>。已配置的默认值必须匹配前缀允许列表。如果没有默认值,允许列表必须接受生成的 hook: 键。
- 直接
/agent持久模式需要显式请求sessionKey、allowRequestSessionKey: true以及非空前缀允许列表。 - 持久映射需要稳定的映射
sessionKey或defaultSessionKey。静态映射键不需要调用方键选择加入,但仍须遵守已配置的前缀。 - 模板化映射键在配置解析时需要非空前缀允许列表,并在分发时需要
allowRequestSessionKey: true。这包括内置 Gmail 预设,除非更早的映射覆盖它。 /wake仅在mode: "now"且采用相同的调用方键/前缀策略时接受显式键。如果没有,则使用所选代理的主会话;defaultSessionKey用于代理运行,而不是唤醒。
逻辑钩子键并不总是存储的会话键。隔离运行即使钩子键稳定也会使用新的自动化运行会话。持久性控制会话复用,而不是工具权限或沙箱。共享同一规范逻辑键的请求会串行化直至完成,即使在隔离模式下。因此,固定的 defaultSessionKey 会为这些请求排序,但可能导致后续单个请求在先前运行仍活跃时命中准入超时。
映射详情¶
自定义 mappings 按数组顺序在 presets 之前运行。第一个匹配项拥有该请求,包括返回 null 的转换;后续映射不会被尝试。当提供两个匹配谓词时,二者都必须通过。省略它们则匹配任意自定义钩子路径。
| 映射字段 | 默认值 | 约定 |
|---|---|---|
id |
mapping-<index> |
为已准入的代理操作提供有界的入口来源归属,而不是经过身份验证的主体或调用者。 |
match.path |
任意自定义路径 | hooks.path 之后的子路径,去除前导/尾随斜杠(gmail 匹配 /hooks/gmail)。 |
match.source |
任意来源 | 与负载中的字符串 source 字段精确匹配。 |
action |
"agent" |
"agent" 或 "wake"。 |
wakeMode |
"now" |
"now" 或 "next-heartbeat";对于唤醒操作会成为 mode。 |
name |
分发时为 "Hook" |
模板化的代理运行标签。 |
agentId |
解析出的所有者 | 静态目标代理 id;受有效代理允许列表约束。 |
sessionKey |
默认/生成键 | 静态或模板化逻辑键;参见会话策略。 |
sessionMode |
"isolated" |
对于代理操作为 "isolated" 或 "persistent"。 |
messageTemplate |
空 | 代理输入模板;最终操作必须具有非空消息。 |
textTemplate |
空 | 唤醒文本模板;最终操作必须具有非空文本。请使用受信任的通知文本,而不是原始不可信内容。 |
forEach |
未设置 | 顶层负载数组键;每个条目一个操作,上限 200 个条目。嵌套/原型路径会被拒绝。 |
deliver |
true |
代理公告策略。与直接 /agent 不同,映射交付可以使用 "last",或将部分目标延迟到自动化交付解析器。 |
channel |
"last" |
已注册渠道 id 或 "last"。映射不公开 accountId。 |
to |
未设置 | 模板化交付目标。优先使用显式 channel 和 to。 |
model |
代理/模型默认值 | 模板化模型覆盖。 |
thinking |
代理/模型默认值 | 模板化思考覆盖。 |
timeoutSeconds |
代理超时 | 正整数轮次超时。 |
allowUnsafeExternalContent |
false |
危险:为此映射禁用代理外部内容包装。Gmail 的全局不安全标志也可以禁用包装。 |
| 映射字段 | 默认值 | 约定 |
|---|---|---|
transform.module |
未设置 | 位于 transformsDir 下的安全相对 JS/TS 模块;绝对路径、路径穿越、URL/驱动器形式以及符号链接逃逸均会被拒绝。 |
transform.export |
default,然后 transform |
命名函数导出;显式指定的命名导出必须存在。 |
模板支持 {{payload.field}} 或 {{field}},以及数组索引,例如
{{messages[0].subject}}、{{headers.x-event-type}}、{{query.kind}}、{{path}}
和 {{now}}(ISO 时间戳)。缺失/null 值会变成空字符串;对象会序列化为 JSON。
渲染后为空的会话密钥模板会被拒绝。
转换函数接收 { payload, headers, url, path },可以返回部分操作覆盖,必要时可异步返回。
操作输出使用 kind: "agent" 或 "wake",分别对应 message 或 text。
返回 null 会跳过该操作;当没有剩余操作时,响应为 204,此时尚未创建任何运行、任务、执行标识或审计回执。
转换异常返回 500。
转换函数提供的 sessionKey 默认被视为外部派生。只有生成固定密钥的可信代码才应标记
sessionKeySource: "static";切勿使用该标记来绕过针对 payload 派生密钥的策略。
转换函数作为可信 Gateway 代码执行,而不是在读取代理的沙箱中执行。
它们会被缓存,直到钩子配置重新加载。请将模块保留在钩子转换根目录下,而不是工作区技能目录中;
如果 doctor 报告了无效模块,请将其移动到该目录,或移除无效的 transformsDir。
钩子重试与扇出¶
代理重放密钥按以下顺序解析:Idempotency-Key、X-OpenClaw-Idempotency-Key,
然后是 payload 中的 idempotencyKey。仅使用去除首尾空白后的非空字符串,且长度最多为 256 个字符。
同一密钥仅对相同的 token、路径和已解析的分发字段进行重放;更改消息或路由可能会创建新的运行。
待处理的准入以及完成状态未确定的已准入运行会被保留,不受 TTL 或大小驱逐限制。
终态完成后,其重放条目会在 5 分钟后过期,并计入 1,000 个终态条目的内存上限。
重启会清除所有重放状态。失败的准入仍然可重试。直接重试可以更改 waitForCompletion,
而不改变分发标识:仅准入的调用方会重放 runId,而等待的调用方共享同一个完成 Promise,
并重放其终态结果。
在请求时,completion.status 为 ok、error 或 skipped,replyDisposition 为
visible、silent 或 empty。该处置仅暴露是否存在终态模型回复,绝不暴露其文本。
可选的投递字段为 delivered、deliveryAttempted、deliveryError 和
deliverySuppressionReason(empty、silent、heartbeat 或 channel_transform)。
缺失的投递字段保持未知。准入后的失败仍返回 HTTP 200;只有准入失败才使用上述非 2xx 状态码。
deliveryError 存在时,是固定的分类值 "delivery-failed"。提供商、运行时、模型、目标、会话、诊断、
输出和摘要详情均不会返回。
对于 forEach,模板/转换函数看到的是原始 payload,其中所选数组被替换为 [currentItem]。
缺失、空或非数组值不会产生任何操作(204)。仅处理前 200 项;多余项会被丢弃并产生警告,
而不是 HTTP 失败。请在发送端拆分更大的批次。
扇出代理分发在映射/转换工作之后最多等待 8 秒。待处理的准入会在后台继续, 不受单次运行 15 秒取消期限的限制。完全准入的多代理批次返回:
{
"ok": true,
"runId": "<first-hook-request-run-id>",
"runIds": ["<hook-request-run-id-1>", "<hook-request-run-id-2>"],
"dispatched": 2
}
已结算的单项批次保留 { ok: true, runId }。部分失败或待处理项会返回非 2xx,
并带有 ok: false、不完整批次的 error、已准入的 runIds,以及 errors 中最多五条失败消息。
仅待处理的批次使用 503。因此,错误可能与已准入或仍待处理的工作并存。
代理扇出即使没有显式幂等密钥,也会从每个渲染后的操作派生重放标识。相同重试会在缓存生命周期内
协调待处理/已准入项;请保持转换函数在重试时具有确定性。唤醒操作会立即分发,并且没有重放标识,
包括混合的唤醒/代理批次。如果任何唤醒被其队列接受,其响应包含 eventOutcome: "queued";
如果所有唤醒都被其队列合并,则包含 "coalesced"。这不是持久化的恰好一次处理。
Gmail 集成¶
Gmail 预设通过 forEach: "messages" 和 sessionKey: "hook:gmail:{{messages[0].id}}"
路由 /hooks/gmail,默认使用隔离模式。自定义匹配映射会在预设之前运行。
如果映射中没有 agentId,预设会使用已解析的默认代理;会话隔离不会限制该代理的工具或工作区。
在连接不受信任的邮件之前,请应用受限 Gmail 读取器配置。
设置命令配置的是传输,而不是读取器或会话密钥策略。对于模板化密钥,请设置
allowRequestSessionKey: true 和 allowedSessionKeyPrefixes: ["hook:gmail:"],
并配合匹配的 defaultSessionKey,或者允许更广泛的 "hook:" 命名空间。
若要禁用调用方密钥覆盖,请使用静态 sessionKey 的更早映射替换预设。
除非有意复用上下文,否则请保持隔离模式。
{
hooks: {
gmail: {
account: "reader@example.com",
topic: "projects/<project-id>/topics/gog-gmail-watch",
subscription: "gog-gmail-watch-push",
pushToken: "<separate-random-push-token>",
hookUrl: "http://127.0.0.1:18789/hooks/gmail",
includeBody: true,
maxBytes: 20000,
renewEveryMinutes: 720,
serve: { bind: "127.0.0.1", port: 8788, path: "/" },
tailscale: { mode: "funnel", path: "/gmail-pubsub" },
model: "openai/gpt-6-astra",
thinking: "high",
},
},
}
这是传输块,不是完整的 reader 配置。模型仅为示例,且必须对 reader 可用。Gmail 字段:
hooks.gmail 字段 |
运行时默认值 | 约定 |
|---|---|---|
account |
必填 | 已在 gog 中授权的 Gmail 账户。 |
label |
"INBOX" |
要监控的 Gmail 标签。OpenClaw 在启动 watcher 时会排除 SPAM、TRASH、DRAFT 和 SENT。 |
topic |
必填 | 完整的 Pub/Sub 主题路径。Setup 可以创建 gog-gmail-watch 主题。 |
subscription |
"gog-gmail-watch-push" |
Setup 使用的 Pub/Sub 订阅。 |
pushToken |
必填 | 用于验证发往 watcher 的入站推送。与 hooks.token 不同,后者用于验证转发到 OpenClaw。如果缺失,Setup 会生成一个。 |
hookUrl |
本地 Gateway /hooks/gmail |
除非已配置,否则由 hooks.path 和 Gateway 端口构建的转发 URL。 |
includeBody |
true |
包含邮件正文片段。在配置中设置为 false 可省略它们。 |
maxBytes |
20000 |
传递给 watcher 的每条消息正文上限,必须为正整数。也用于推导 Gmail HTTP 正文允许大小。 |
renewEveryMinutes |
720 |
监控续期间隔,必须为正整数。 |
serve.bind |
"127.0.0.1" |
watcher 绑定主机。 |
serve.port |
8788 |
watcher 端口,必须为正整数。 |
serve.path |
"/gmail-pubsub" |
watcher 路径。启用 Tailscale 且未显式指定 target 时,由于暴露的前缀会被剥离,它会变为 /。 |
tailscale.mode |
"off" |
"off"、"serve" 或 "funnel"。Setup 默认使用 "funnel";运行时如果没有已保存配置,则默认使用 "off"。 |
tailscale.path |
解析后的 serve 路径 | 暴露的 Tailscale 路径,在 Setup 中通常为 /gmail-pubsub。 |
tailscale.target |
本地 watcher | 可选的端口、host:port 或 URL target。显式 target 会保留已配置的 serve 路径。 |
model |
agent/model 默认值 | Gmail 模型默认值;显式映射模型会覆盖它。不允许的 Gmail 默认值会被忽略,而无效的显式运行覆盖会导致准备失败。 |
thinking |
agent/model 默认值 | "off"、"minimal"、"low"、"medium" 或 "high";显式映射 thinking 优先。 |
allowUnsafeExternalContent |
false |
危险:为 Gmail agent 轮次禁用邮件安全包装。对于不受信任的收件箱,请保持关闭。 |
Gmail 路径映射使用请求正文允许大小
max(256 KiB, min(32 MiB, 100 × (3 × maxBytes + 8192)))。该乘数用于为转义内容和消息元数据预留空间;它并不保证所有上游积压都能容纳。上游历史页大小按历史记录计数,而一条历史记录可能包含多条消息。扇出仍然只处理前 200 项,并记录被丢弃的多余项。参见 批量限制和重试。
当 hooks.enabled: true 且设置了 hooks.gmail.account 时,如果其可执行文件和所需的传输配置可用,Gateway 会启动 gog gmail watch serve 并续期 watch。设置 OPENCLAW_SKIP_GMAIL_WATCHER=1 可选择退出。请勿在同一监听器上启动第二个前台 watcher。Setup 输出可能包含 token;参见 CLI 参考。
一次成功的 push 或 hook 响应只是传输/准入证据,并非已完成
邮件处理或投递的证明。请通过日志及其运行输出验证受限 reader。对于
reader 到 agent 的交接,仅暴露所需工具,并使用 allow 约束默认开启的 tools.agentToAgent
策略;如果不需要交接,则设置 enabled: false;另请参阅 Prompt 注入 和
按 agent 的沙箱与工具。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw