跳转至

配置 — 钩子

入站 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