Skip to content

实现或调试 OpenClaw Gateway WebSocket 客户端时,本文提供完整的协议细节,包括握手挑战、帧格式(请求/响应/事件)、角色(operator/node)与作用域、设备身份签名与配对、版本协商(当前协议 v4)、客户端常量(如超时和重试间隔)以及节点后台保活事件。还列出了常见 RPC 方法家族和广播事件的范围控制规则。对于设备认证迁移,提供了 error.details.code 对照表和修复建议。

OpenClaw Gateway WebSocket 协议:握手、帧格式与认证

Gateway WS 协议是 OpenClaw 的唯一控制面 + 节点传输通道。所有客户端(CLI、Web UI、macOS 应用、iOS/Android 节点、无头节点)均通过 WebSocket 连接,并在握手时声明自己的角色(role)作用域(scope)

传输层

  • WebSocket,文本帧,JSON 载荷。
  • 第一帧必须connect 请求。
  • 预连接帧最大 64 KiB。握手成功后,客户端应遵循 hello-ok.policy.maxPayloadhello-ok.policy.maxBufferedBytes 限制。启用诊断时,超大的入站帧和慢的出站缓冲区会在 Gateway 关闭或丢弃之前发出 payload.large 事件。这些事件保留大小、限制、表面和安全的理由码,不保留消息体、附件内容、原始帧体、令牌、cookie 或密钥值。

握手(connect)

Gateway → 客户端(预连接挑战):

json
{
  "type": "event",
  "event": "connect.challenge",
  "payload": { "nonce": "…", "ts": 1737264000000 }
}

客户端 → Gateway:

json
{
  "type": "req",
  "id": "…",
  "method": "connect",
  "params": {
    "minProtocol": 3,
    "maxProtocol": 4,
    "client": {
      "id": "cli",
      "version": "1.2.3",
      "platform": "macos",
      "mode": "operator"
    },
    "role": "operator",
    "scopes": ["operator.read", "operator.write"],
    "caps": [],
    "commands": [],
    "permissions": {},
    "auth": { "token": "…" },
    "locale": "en-US",
    "userAgent": "openclaw-cli/1.2.3",
    "device": {
      "id": "device_fingerprint",
      "publicKey": "…",
      "signature": "…",
      "signedAt": 1737264000000,
      "nonce": "…"
    }
  }
}

Gateway → 客户端:

json
{
  "type": "res",
  "id": "…",
  "ok": true,
  "payload": {
    "type": "hello-ok",
    "protocol": 4,
    "server": { "version": "…", "connId": "…" },
    "features": { "methods": ["…"], "events": ["…"] },
    "snapshot": { "…": "…" },
    "auth": {
      "role": "operator",
      "scopes": ["operator.read", "operator.write"]
    },
    "policy": {
      "maxPayload": 26214400,
      "maxBufferedBytes": 52428800,
      "tickIntervalMs": 15000
    }
  }
}

如果 Gateway 仍在完成启动 sidecars,connect 请求可能会返回可重试的 UNAVAILABLE 错误,details.reason"startup-sidecars",并附带 retryAfterMs。客户端应在整体连接预算内重试,而不是将其视为最终握手失败。

serverfeaturessnapshotpolicy 都是 schema 要求的(src/gateway/protocol/schema/frames.ts)。auth 也是必需的,报告协商后的角色/作用域。pluginSurfaceUrls 是可选的,将插件表面名(如 canvas)映射到限域的托管 URL。

限域的插件表面 URL 可能会过期。节点可以调用 node.pluginSurface.refresh 并传入 { "surface": "canvas" },以便在 pluginSurfaceUrls 中获得新的条目。实验性的 Canvas 插件重构不支持已弃用的 canvasHostUrlcanvasCapabilitynode.canvas.capability.refresh 兼容路径;当前的原生客户端和 Gateway 必须使用插件表面。

未颁发设备令牌时,hello-ok.auth 报告协商的权限,不包含令牌字段:

json
{
  "auth": {
    "role": "operator",
    "scopes": ["operator.read", "operator.write"]
  }
}

受信的同一进程后端客户端(client.id: "gateway-client"client.mode: "backend")可以在直接环回连接上省略 device,前提是它们使用共享的网关令牌/密码进行身份验证。此路径保留用于内部控制面 RPC,并防止过时的 CLI/设备配对基线阻塞本地后端工作(如子智能体会话更新)。远程客户端、浏览器来源客户端、节点客户端以及显式的设备令牌/设备身份客户端仍然使用正常的配对和作用域升级检查。

当颁发设备令牌时,hello-ok 还会包含:

json
{
  "auth": {
    "deviceToken": "…",
    "role": "operator",
    "scopes": ["operator.read", "operator.write"]
  }
}

内置的 QR/设置码引导是一种新的移动交接路径。成功的基线设置码连接会返回一个主节点令牌加一个受限的 operator 令牌:

json
{
  "auth": {
    "deviceToken": "…",
    "role": "node",
    "scopes": [],
    "deviceTokens": [
      {
        "deviceToken": "…",
        "role": "operator",
        "scopes": ["operator.approvals", "operator.read", "operator.write"]
      }
    ]
  }
}

operator 交接是有意限制的,以便 QR 初次安装可以启动移动 operator 循环,而不授予 operator.adminoperator.pairingoperator.talk.secrets。这些作用域需要单独的已批准 operator 配对或令牌流程。客户端应仅在 connect 使用了受信传输(如 wss:// 或环回/本地配对)上的引导身份验证时,才持久化 hello-ok.auth.deviceTokens

节点示例

json
{
  "type": "req",
  "id": "…",
  "method": "connect",
  "params": {
    "minProtocol": 3,
    "maxProtocol": 4,
    "client": {
      "id": "ios-node",
      "version": "1.2.3",
      "platform": "ios",
      "mode": "node"
    },
    "role": "node",
    "scopes": [],
    "caps": ["camera", "canvas", "screen", "location", "voice"],
    "commands": ["camera.snap", "canvas.navigate", "screen.record", "location.get"],
    "permissions": { "camera.capture": true, "screen.record": false },
    "auth": { "token": "…" },
    "locale": "en-US",
    "userAgent": "openclaw-ios/1.2.3",
    "device": {
      "id": "device_fingerprint",
      "publicKey": "…",
      "signature": "…",
      "signedAt": 1737264000000,
      "nonce": "…"
    }
  }
}

帧格式

  • 请求(Request){type:"req", id, method, params}
  • 响应(Response){type:"res", id, ok, payload|error}
  • 事件(Event){type:"event", event, payload, seq?, stateVersion?}

有副作用的方法需要携带幂等性 key(参见 schema)。

角色与作用域

完整的 operator 作用域模型、审批时检查和共享密钥语义请参见 Operator 作用域

角色

  • operator = 控制面客户端(CLI/UI/自动化)
  • node = 能力宿主(摄像头/屏幕/画布/system.run)

作用域(operator)

常见作用域:

  • operator.read
  • operator.write
  • operator.admin
  • operator.approvals
  • operator.pairing
  • operator.talk.secrets

使用 includeSecrets: truetalk.config 需要 operator.talk.secrets(或 operator.admin)。

插件注册的 Gateway RPC 方法可以请求自己的 operator 作用域,但保留的核心 admin 前缀(config.*exec.approvals.*wizard.*update.*)始终解析为 operator.admin

方法作用域只是第一道门禁。通过 chat.send 触发的部分 slash 命令还会在此之上执行更严格的命令级别检查。例如,持久写入操作 /config set/config unset 需要 operator.admin 作用域。

node.pair.approve 在基础方法作用域之上还有额外的审批时作用域检查:

  • 无命令的请求:operator.pairing
  • 包含非 exec 节点命令的请求:operator.pairing + operator.write
  • 包含 system.runsystem.run.preparesystem.which 的请求:operator.pairing + operator.admin

能力/命令/权限(node)

节点在连接时声明能力:

  • caps:高级能力类别,如 cameracanvasscreenlocationvoicetalk
  • commands:用于调用的命令白名单
  • permissions:细粒度开关(例如 screen.recordcamera.capture

Gateway 将这些视为声明并在服务端执行白名单验证。

在线状态

  • system-presence 返回以设备身份为 key 的条目。
  • 在线条目包含 deviceIdrolesscopes,方便 UI 为同时以 operator 和 node 两种角色连接的设备显示单行记录。
  • node.list 包含可选的 lastSeenAtMslastSeenReason 字段。已连接的节点将其当前连接时间报告为 lastSeenAtMs,原因为 connect;已配对节点也可以在受信节点事件更新其配对元数据时报告持久的后台存在。

节点后台保活事件

节点可以调用 node.event,传入 event: "node.presence.alive",记录已配对节点在后台唤醒时的存活状态,而不标记其为已连接。

json
{
  "event": "node.presence.alive",
  "payloadJSON": "{\"trigger\":\"silent_push\",\"sentAtMs\":1737264000000,\"displayName\":\"Peter's iPhone\",\"version\":\"2026.4.28\",\"platform\":\"iOS 18.4.0\",\"deviceFamily\":\"iPhone\",\"modelIdentifier\":\"iPhone17,1\",\"pushTransport\":\"relay\"}"
}

trigger 是一个封闭的枚举:backgroundsilent_pushbg_app_refreshsignificant_locationmanualconnect。未知的触发器字符串在持久化前会被 Gateway 规范化为 background。该事件仅对经过身份验证的节点设备会话持久化;无设备或未配对的会话返回 handled: false

成功的 Gateway 会返回结构化结果:

json
{
  "ok": true,
  "event": "node.presence.alive",
  "handled": true,
  "reason": "persisted"
}

较旧的 Gateway 可能仍返回 { "ok": true } 作为对 node.event 的响应;客户端应将其视为已确认的 RPC,而非持久的在线状态。

广播事件的作用域控制

服务端推送的 WebSocket 广播事件受作用域门控,使配对作用域或仅节点会话不会被动接收会话内容。

  • Chat、agent 和工具结果帧(包括流式 agent 事件和工具调用结果)至少需要 operator.read。没有 operator.read 的会话会跳过这些帧。
  • 插件定义的 plugin.* 广播根据插件注册方式,门控到 operator.writeoperator.admin
  • 状态和传输事件heartbeatpresencetick、连接/断开生命周期等)保持不受限制,以便每个经过身份验证的会话都能观察传输健康。
  • 未知的广播事件家族默认受作用域门控(故障关闭),除非已注册的处理程序明确放宽。

每个客户端连接维护自己的客户端序列号,因此即使不同客户端看到作用域过滤后的不同事件子集,广播也能在该 socket 上保持单调排序。

常见 RPC 方法家族

公有 WS 表面比上述握手/认证示例更广。这不是自动生成的转储——hello-ok.features.methods 是从 src/gateway/server-methods-list.ts 加上已加载插件/渠道方法导出构建的保守发现列表。请将其视为功能发现,而非 src/gateway/server-methods/*.ts 的完整枚举。

系统与身份

  • health:返回缓存或最新探测的 Gateway 健康快照
  • diagnostics.stability:返回最近有界的诊断稳定性记录器。它保留操作元数据(事件名称、计数、字节大小、内存读数、队列/会话状态、渠道/插件名称和会话 ID)。不保留聊天文本、webhook 请求体、工具输出、原始请求或响应体、令牌、cookie 或密钥值。需要 operator.read 作用域
  • status:返回 /status 风格的 Gateway 摘要;敏感字段仅向 admin 作用域的 operator 客户端开放
  • gateway.identity.get:返回 relay 和配对流程使用的 Gateway 设备身份
  • system-presence:返回已连接 operator/节点设备的当前在线快照
  • system-event:追加系统事件,可更新/广播在线上下文
  • last-heartbeat:返回最新持久化的心跳事件
  • set-heartbeats:切换 Gateway 上的心跳处理

模型与用量

  • models.list:返回运行时允许的模型目录。传入 { "view": "configured" } 获取选择器大小的已配置模型(优先使用 agents.defaults.models,然后是 models.providers.*.models),传入 { "view": "all" } 获取完整目录
  • usage.status:返回提供商用量窗口/剩余配额摘要
  • usage.cost:返回日期范围内的聚合成本摘要
  • doctor.memory.status:返回默认 agent 工作区的向量记忆/embedding 就绪状态。仅在调用方明确需要实时 embedding 提供商 ping 时才传入 { "probe": true }{ "deep": true }
  • doctor.memory.remHarness:返回远程控制面客户端的有界只读 REM harness 预览。可能包含工作区路径、记忆片段、渲染的 grounded markdown 和深度提升候选,因此调用方需要 operator.read
  • sessions.usage:返回每会话用量摘要
  • sessions.usage.timeseries:返回某会话的时序用量
  • sessions.usage.logs:返回某会话的用量日志条目

渠道与登录助手

  • channels.status:返回内置 + 捆绑渠道/插件的状态摘要
  • channels.logout:注销支持注销的特定渠道/账号
  • web.login.start:启动当前支持 QR/网页登录的渠道提供商的登录流程
  • web.login.wait:等待 QR/网页登录流程完成,成功后启动渠道
  • push.test:向已注册的 iOS 节点发送测试 APNs 推送
  • voicewake.get:返回存储的唤醒词触发器
  • voicewake.set:更新唤醒词触发器并广播变更

消息与日志

  • send:在聊天执行器外,直接向渠道/账号/线程发送消息的出站投递 RPC
  • logs.tail:返回带游标/限制和最大字节控制的 Gateway 文件日志尾

Talk 与 TTS

  • talk.catalog:返回只读的 Talk 提供商目录,包含提供商标识、标签、已配置状态、暴露的模型/语音 ID、规范模式、传输方式、大脑策略以及实时音频/能力标志。不返回提供商密钥或变更全局配置
  • talk.config:返回有效的 Talk 配置载荷;includeSecrets 需要 operator.talk.secrets(或 operator.admin
  • talk.session.create:创建 Gateway 拥有的 Talk 会话,用于 realtime/gateway-relaytranscription/gateway-relaystt-tts/managed-room。对于 stt-tts/managed-room,传入 sessionKeyoperator.write 调用方还必须传入 spawnedBy 以实现作用域会话 key 可见性;无作用域的 sessionKey 创建和 brain: "direct-tools" 需要 operator.admin
  • talk.session.join:验证托管房间会话令牌,根据需要发出 session.readysession.replaced 事件,并返回房间/会话元数据及最近的 Talk 事件(不包含明文令牌或存储的令牌哈希)
  • talk.session.appendAudio:将 base64 PCM 输入音频追加到 Gateway 拥有的实时中继和转录会话
  • talk.session.startTurntalk.session.endTurntalk.session.cancelTurn:驱动托管房间的回合生命周期,在清除状态前拒绝过期的回合
  • talk.session.cancelOutput:停止助理音频输出,主要用于 VAD 门控的抢话(barge-in)
  • talk.session.submitToolResult:完成由 Gateway 拥有的实时中继会话发出的提供商工具调用。传入 options: { willContinue: true } 表示中间工具输出(稍后有最终结果),传入 options: { suppressResponse: true } 表示工具结果应满足提供商调用而不启动另一轮助理响应
  • talk.session.close:关闭 Gateway 拥有的中继、转录或托管房间会话,并发出终端 Talk 事件
  • talk.mode:设置/广播当前 Talk 模式状态(用于 WebChat/控制 UI 客户端)
  • talk.client.create:创建客户端拥有的实时提供商会话,使用 webrtcprovider-websocket,同时 Gateway 拥有配置、凭据、指令和工具策略
  • talk.client.toolCall:让客户端拥有的实时传输将提供商工具调用转发给 Gateway 策略。第一个支持的工具是 openclaw_agent_consult;客户端收到运行 ID,然后等待正常的聊天生命周期事件,再提交提供商特定的工具结果
  • talk.event:Talk 事件通道,用于实时、转录、STT/TTS、托管房间、电话和会议适配器
  • talk.speak:通过当前激活的 Talk 语音提供商合成语音
  • tts.status:返回 TTS 启用状态、当前提供商、回退提供商和提供商配置状态
  • tts.providers:返回可见的 TTS 提供商清单
  • tts.enable / tts.disable:切换 TTS 偏好状态
  • tts.setProvider:更新首选 TTS 提供商
  • tts.convert:运行一次性文本转语音转换

Secrets、配置、更新与向导

  • secrets.reload:重新解析活跃的 SecretRef,仅在完全成功时替换运行时密钥状态
  • secrets.resolve:解析特定命令/目标集的命令目标密钥分配
  • config.get:返回当前配置快照和哈希
  • config.set:写入已验证的配置载荷
  • config.patch:合并部分配置更新
  • config.apply:验证并替换完整配置载荷
  • config.schema:返回控制 UI 和 CLI 工具使用的实时配置 schema 载荷:schema、uiHints、版本和生成元数据,包括运行时能加载的插件 + 渠道 schema 元数据。schema 包含从 UI 使用的相同标签和帮助文本派生的字段 title/description 元数据,包括嵌套对象、通配符、数组项以及 anyOf/oneOf/allOf 组合分支(当匹配的字段文档存在时)
  • config.schema.lookup:返回某配置路径的路径作用域查找载荷:归一化路径、浅层 schema 节点、匹配的 hint + hintPath、可选的 reloadKind,以及用于 UI/CLI 下钻的直接子摘要。reloadKindrestarthotnone 之一,反映 Gateway 对该路径的配置重载计划。查找 schema 节点保留用户面向的文档和常见验证字段(titledescriptiontypeenumconstformatpattern、数字/字符串/数组/对象边界,以及 additionalPropertiesdeprecatedreadOnlywriteOnly 等标志)。子摘要暴露 key、归一化 pathtyperequiredhasChildren、可选的 reloadKind,以及匹配的 hint/hintPath
  • update.run:运行 Gateway 更新流程,仅在更新本身成功时才安排重启;带有会话的调用方可包含 continuationMessage,以便启动时通过重启延续队列恢复一次代理回合。包管理器的控制面更新使用分离的托管服务交接,而不是替换实时 Gateway 内的包树。已启动的交接返回 ok: true 并带 result.reason: "managed-service-handoff-started"handoff.status: "started";不可用或失败的交接返回 ok: false 并带 managed-service-handoff-unavailablemanaged-service-handoff-failed,同时 handoff.command 在需要手动 shell 更新时出现。在已启动的交接期间,重启哨兵可能短暂报告 stats.reason: "restart-health-pending";延续会延迟,直到 CLI 验证重启的 Gateway 并写入最终 ok 哨兵
  • update.status:返回最新的缓存更新重启哨兵,包括可用时的重启后运行版本
  • wizard.startwizard.nextwizard.statuswizard.cancel:通过 WS RPC 暴露引导向导

Agent 与工作区助手

  • agents.list:返回已配置的 agent 条目,包括有效模型和运行时元数据
  • agents.createagents.updateagents.delete:管理 agent 记录和工作区接线
  • agents.files.listagents.files.getagents.files.set:管理某 agent 暴露的 bootstrap 工作区文件
  • tasks.listtasks.gettasks.cancel:向 SDK 和 operator 客户端暴露 Gateway 任务账本
  • artifacts.listartifacts.getartifacts.download:暴露基于 transcript 的产物摘要和下载,限定于显式 sessionKeyrunIdtaskId 作用域。运行和任务查询在服务端解析所属会话,仅返回具有匹配来源的 transcript 媒体;不安全或本地 URL 来源返回不支持的下载,而不是服务端抓取
  • environments.listenvironments.status:为 SDK 客户端暴露只读的 Gateway 本地和节点环境发现
  • agent.identity.get:返回某个 agent 或会话的有效助理身份
  • agent.wait:等待运行完成并在可用时返回终端快照

会话控制

  • sessions.list:返回当前会话索引,包括配置了 agent 运行时后端时的每行 agentRuntime 元数据
  • sessions.subscribe / sessions.unsubscribe:切换当前 WS 客户端的会话变更事件订阅
  • sessions.messages.subscribe / sessions.messages.unsubscribe:切换某会话的转录/消息事件订阅
  • sessions.preview:返回特定会话 key 的有界转录预览
  • sessions.describe:返回精确会话 key 的一个 Gateway 会话行
  • sessions.resolve:解析或规范化会话目标
  • sessions.create:创建新会话条目
  • sessions.send:向已有会话发送消息
  • sessions.steer:活跃会话的中断并转向变体
  • sessions.abort:中止会话的活跃工作。调用方可传入 key 加可选的 runId,或单独传入 runId(用于 Gateway 能解析到会话的活跃运行)
  • sessions.patch:更新会话元数据/覆盖,并报告解析后的规范模型及有效 agentRuntime
  • sessions.resetsessions.deletesessions.compact:执行会话维护
  • sessions.get:返回完整的存储会话行
  • 聊天执行仍然使用 chat.historychat.sendchat.abortchat.injectchat.history 是面向 UI 客户端显示规范化的:内联指令标签从可见文本中移除,纯文本工具调用 XML 载荷(包括 <tool_call>...</tool_call><function_call>...</function_call><tool_calls>...</tool_calls><function_calls>...</function_calls> 以及截断的工具调用块)和泄漏的 ASCII/全角模型控制令牌会被移除,纯静默令牌 assistant 行(如精确的 NO_REPLY/no_reply)被省略,过大的行可以用占位符替换

设备配对与设备 Token

  • device.pair.list:返回待审批和已审批的配对设备
  • device.pair.approvedevice.pair.rejectdevice.pair.remove:管理设备配对记录
  • device.token.rotate:在已审批的角色和作用域边界内轮换配对设备令牌
  • device.token.revoke:撤销配对设备令牌

节点配对、调用与待处理工作

  • node.pair.requestnode.pair.listnode.pair.approvenode.pair.rejectnode.pair.removenode.pair.verify:节点配对与 bootstrap 验证
  • node.list / node.describe:返回已知/已连接的节点状态
  • node.rename:更新配对节点标签
  • node.invoke:将命令转发给已连接的节点
  • node.invoke.result:返回调用请求的结果
  • node.event:将节点发起的事件回传到 Gateway
  • node.pending.pull / node.pending.ack:已连接节点的队列 API
  • node.pending.enqueue / node.pending.drain:管理离线/已断开节点的持久待处理工作

审批系列

  • exec.approval.requestexec.approval.getexec.approval.listexec.approval.resolve:一次性 exec 审批请求及待审批查找/重放
  • exec.approval.waitDecision:等待一个待审批的 exec 审批并返回最终决定(超时返回 null
  • exec.approvals.get / exec.approvals.set:管理 Gateway exec 审批策略快照
  • exec.approvals.node.get / exec.approvals.node.set:通过节点中继命令管理节点本地 exec 审批策略
  • plugin.approval.requestplugin.approval.listplugin.approval.waitDecisionplugin.approval.resolve:插件定义的审批流程

自动化、技能与工具

  • 自动化:wake(调度立即或下次心跳的唤醒文本注入);cron.getcron.listcron.statuscron.addcron.updatecron.removecron.runcron.runs 管理定时任务
  • cron.run 仍是一个入队风格的 RPC。需要完成语义的客户端应读取返回的 runId 并轮询 cron.runs
  • cron.runs 接受可选的非空 runId 过滤器,以便客户端可以跟踪一个排队的单次运行,而不会与同一作业的其他历史条目产生竞态
  • 技能与工具:commands.listskills.*tools.catalogtools.effectivetools.invoke

常见事件系列

  • chat:UI 聊天更新,如 chat.inject 和其他仅 transcript 的聊天事件。在协议 v4 中,delta 载荷携带 deltaTextmessage 保持累积的 assistant 快照。非前缀替换设置 replace=true 并使用 deltaText 作为替换文本
  • session.messagesession.operationsession.tool:已订阅会话的 transcript、进行中会话操作和事件流更新
  • sessions.changed:会话索引或元数据变更
  • presence:系统在线快照更新
  • tick:周期性 keepalive / 存活事件
  • health:Gateway 健康快照更新
  • heartbeat:心跳事件流更新
  • cron:cron 运行/任务变更事件
  • shutdown:Gateway 关闭通知
  • node.pair.requested / node.pair.resolved:节点配对生命周期
  • node.invoke.request:节点调用请求广播
  • device.pair.requested / device.pair.resolved:配对设备生命周期
  • voicewake.changed:唤醒词触发配置变更
  • exec.approval.requested / exec.approval.resolved:exec 审批生命周期
  • plugin.approval.requested / plugin.approval.resolved:插件审批生命周期

节点辅助方法

  • 节点可调用 skills.bins 获取当前技能可执行文件列表,用于自动允许检查

任务账本 RPC

Operator 客户端可以通过任务账本 RPC 检查和取消 Gateway 后台任务记录。这些方法返回清理后的任务摘要,而非原始运行时状态。

  • tasks.list 需要 operator.read
    • 参数:可选的 status"queued""running""completed""failed""cancelled""timed_out")或这些状态的数组,可选的 agentId、可选的 sessionKey、可选的 limit(1-500)、可选的字符串 cursor
    • 结果:{ "tasks": TaskSummary[], "nextCursor"?: string }
  • tasks.get 需要 operator.read
    • 参数:{ "taskId": string }
    • 结果:{ "task": TaskSummary }
    • 缺失的任务 ID 返回 Gateway 的未找到错误形状。
  • tasks.cancel 需要 operator.write
    • 参数:{ "taskId": string, "reason"?: string }
    • 结果:{ "found": boolean, "cancelled": boolean, "reason"?: string, "task"?: TaskSummary }
    • found 报告账本是否包含匹配的任务。cancelled 报告运行时是否接受或记录了取消。

TaskSummary 包含 idstatus 和可选的元数据,如 kindruntimetitleagentIdsessionKeychildSessionKeyownerKeyrunIdtaskIdflowIdparentTaskIdsourceId、时间戳、进度、终端摘要和清理后的错误文本。

Operator 辅助方法

  • Operator 可以调用 commands.list(需要 operator.read)来获取某个 agent 的运行时命令清单。
    • agentId 是可选的;省略则读取默认 agent 工作区。
    • scope 控制主要 name 的目标表面:
      • text 返回不带前导 / 的主要文本命令令牌
      • native 和默认的 both 路径在可用时返回提供商感知的原生名称
    • textAliases 携带精确的斜杠别名,如 /model/m
    • nativeName 在存在时携带提供商感知的原生命令名称。
    • provider 是可选的,仅影响原生命名和原生插件命令可用性。
    • includeArgs=false 从响应中省略序列化的参数元数据。
  • Operator 可以调用 tools.catalog(需要 operator.read)来获取某个 agent 的运行时工具目录。响应包含分组工具和来源元数据:
    • sourcecoreplugin
    • pluginId:当 source="plugin" 时的插件所有者
    • optional:插件工具是否可选
  • Operator 可以调用 tools.effective(需要 operator.read)来获取某个会话的运行时有效工具清单。
    • sessionKey 是必需的。
    • Gateway 从服务端会话中派生受信运行时上下文,而不是接受调用方提供的 auth 或投递上下文。
    • 响应是会话作用域的,反映当前对话可以使用的所有工具,包括 core、plugin 和 channel 工具。
  • Operator 可以调用 tools.invoke(需要 operator.write)通过与 /tools/invoke 相同的 Gateway 策略路径调用一个可用工具。
    • name 是必需的。argssessionKeyagentIdconfirmidempotencyKey 是可选的。
    • 如果同时提供 sessionKeyagentId,则解析的会话 agent 必须与 agentId 匹配。
    • 响应是一个 SDK 面向的包装,包含 oktoolName、可选的 output 和类型化的 error 字段。审批或策略拒绝会在 payload 中返回 ok:false,而不是绕过 Gateway 工具策略管道。
  • Operator 可以调用 skills.status(需要 operator.read)来获取某个 agent 的可见技能清单。
    • agentId 是可选的;省略则读取默认 agent 工作区。
    • 响应包括资格、缺失要求、配置检查和清理后的安装选项,不暴露原始密钥值。
  • Operator 可以调用 skills.searchskills.detail(需要 operator.read)进行 ClawHub 发现元数据。
  • Operator 可以调用 skills.upload.beginskills.upload.chunkskills.upload.commit(需要 operator.admin)来暂存私有技能归档,然后再安装。这是一个用于受信客户端的独立 admin 上传路径,不是正常的 ClawHub 技能安装流程,默认禁用,除非 skills.install.allowUploadedArchives 已启用。
    • skills.upload.begin({ kind: "skill-archive", slug, sizeBytes, sha256?, force?, idempotencyKey? }) 创建一个绑定到该 slug 和 force 值的上传。
    • skills.upload.chunk({ uploadId, offset, dataBase64 }) 在精确的解码偏移处追加字节。
    • skills.upload.commit({ uploadId, sha256? }) 验证最终大小和 SHA-256。commit 仅完成上传,不安装技能。
    • 上传的技能归档是包含根 SKILL.md 的 zip 归档。归档的内部目录名从不选择安装目标。
  • Operator 可以调用 skills.install(需要 operator.admin),有三种模式:
    • ClawHub 模式:{ source: "clawhub", slug, version?, force? } 将技能文件夹安装到默认 agent 工作区的 skills/ 目录。
    • 上传模式:{ source: "upload", uploadId, slug, force?, sha256?, timeoutMs? } 将已提交的上传安装到默认 agent 工作区的 skills/<slug> 目录。slug 和 force 值必须与原始 skills.upload.begin 请求匹配。此模式被拒绝,除非 skills.install.allowUploadedArchives 已启用。该设置不影响 ClawHub 安装。
    • Gateway 安装程序模式:{ name, installId, dangerouslyForceUnsafeInstall?, timeoutMs? } 在 Gateway 主机上执行声明的 metadata.openclaw.install 操作。
  • Operator 可以调用 skills.update(需要 operator.admin),有两种模式:
    • ClawHub 模式更新默认 agent 工作区中的一个 tracked slug 或所有 tracked ClawHub 安装。
    • 配置模式修补 skills.entries.<skillKey> 值,如 enabledapiKeyenv

models.list 视图

models.list 接受可选的 view 参数:

  • 省略或 "default":当前运行时行为。如果配置了 agents.defaults.models,则响应是允许的目录,包括动态发现的 provider/* 条目。否则响应是完整的 Gateway 目录。
  • "configured":选择器大小的行为。如果配置了 agents.defaults.models,它仍然优先,包括 provider/* 条目的提供商作用域发现。如果没有 allowlist,则响应使用显式的 models.providers.*.models 条目,仅当没有已配置的模型行时回退到完整目录。
  • "all":完整的 Gateway 目录,绕过 agents.defaults.models。用于诊断和发现 UI,而不是正常的模型选择器。

Exec 审批

  • 当 exec 请求需要审批时,Gateway 会广播 exec.approval.requested
  • Operator 客户端通过调用 exec.approval.resolve(需要 operator.approvals 作用域)来解决审批。
  • 对于 host=nodeexec.approval.request 必须包含 systemRunPlan(规范 argv/cwd/rawCommand/会话元数据)。缺少 systemRunPlan 的请求会被拒绝。
  • 审批后,转发的 node.invoke system.run 调用会重用该规范 systemRunPlan 作为权威命令/cwd/会话上下文。
  • 如果调用方在准备和最终批准的 system.run 转发之间改变了 commandrawCommandcwdagentIdsessionKey,Gateway 会拒绝运行,而不是信任改变后的载荷。

Agent 投递回退

  • agent 请求可以包含 deliver=true 以请求出站投递。
  • bestEffortDeliver=false 保持严格行为:无法解析或仅限内部的投递目标返回 INVALID_REQUEST
  • bestEffortDeliver=true 允许回退到仅会话执行,当无法解析外部可投递路由时(例如内部/WebChat 会话或模糊的多渠道配置)。
  • 最终的 agent 结果可能包含 result.deliveryStatus,当请求了投递时,使用与 openclaw agent --json --deliver 相同的 sentsuppressedpartial_failedfailed 状态。

版本管理

  • PROTOCOL_VERSION 定义在 src/gateway/protocol/version.ts
  • 客户端发送 minProtocol + maxProtocol;服务端拒绝不包含其当前协议的版本范围。当前客户端和服务端需要协议 v4。
  • Schema 和模型由 TypeBox 定义生成:
    • pnpm protocol:gen
    • pnpm protocol:gen:swift
    • pnpm protocol:check

客户端常量

参考客户端在 src/gateway/client.ts 中使用这些默认值。它们在协议 v4 中稳定,是第三方客户端的预期基线。

常量默认值来源
PROTOCOL_VERSION4src/gateway/protocol/version.ts
MIN_CLIENT_PROTOCOL_VERSION4src/gateway/protocol/version.ts
请求超时(每次 RPC)30_000 mssrc/gateway/client.ts (requestTimeoutMs)
预认证 / connect-challenge 超时15_000 mssrc/gateway/handshake-timeouts.ts (配置/env 可以提高配对的服务器/客户端预算)
初始重连回退1_000 mssrc/gateway/client.ts (backoffMs)
最大重连回退30_000 mssrc/gateway/client.ts (scheduleReconnect)
设备令牌关闭后的快速重试钳制250 mssrc/gateway/client.ts
强制停止优雅期(terminate() 前)250 msFORCE_STOP_TERMINATE_GRACE_MS
stopAndWait() 默认超时1_000 msSTOP_AND_WAIT_TIMEOUT_MS
默认 tick 间隔(hello-ok 前)30_000 mssrc/gateway/client.ts
Tick 超时关闭码静默超过 tickIntervalMs * 2 时关闭码 4000src/gateway/client.ts
MAX_PAYLOAD_BYTES25 * 1024 * 1024 (25 MB)src/gateway/server-constants.ts

服务端在 hello-ok 中广播有效的 policy.tickIntervalMspolicy.maxPayloadpolicy.maxBufferedBytes;客户端应遵循这些值,而不是握手前的默认值。

认证

  • 共享密钥 Gateway 认证使用 connect.params.auth.tokenconnect.params.auth.password,取决于配置的认证模式。
  • 身份承载模式(如 Tailscale Serve (gateway.auth.allowTailscale: true) 或非环回 gateway.auth.mode: "trusted-proxy")从请求头满足 connect 认证检查,而不是通过 connect.params.auth.*
  • 私有入口 gateway.auth.mode: "none" 完全跳过共享密钥 connect 认证;不要将该模式暴露在公共/非受信入口上。
  • 配对后,Gateway 会颁发一个设备令牌,作用域绑定到连接角色和作用域。它在 hello-ok.auth.deviceToken 中返回,客户端应持久化以便将来连接。
  • 任何成功连接后,客户端都应持久化主 hello-ok.auth.deviceToken
  • 使用该存储的设备令牌重新连接时,还应重用该令牌的已批准作用域集合。这保留了已授予的读/探测/状态访问权限,并避免静默地将重新连接折叠到更窄的隐式仅 admin 作用域。
  • 客户端侧 connect 认证组装(selectConnectAuthsrc/gateway/client.ts 中):
    • auth.password 是正交的,设置后始终转发。
    • auth.token 按优先级顺序填充:首先是显式共享令牌,然后是显式 deviceToken,然后是存储的按设备令牌(以 deviceId + role 为 key)。
    • auth.bootstrapToken 仅当以上都没有解析出 auth.token 时才发送。共享令牌或任何已解析的设备令牌会抑制它。
    • 在一次性 AUTH_TOKEN_MISMATCH 重试中自动提升存储的设备令牌仅限于受信端点——环回或带有固定 tlsFingerprintwss://。没有固定的公共 wss:// 不符合条件。
  • 内置设置码引导返回主节点 hello-ok.auth.deviceToken 加上 hello-ok.auth.deviceTokens 中的受限 operator 令牌,用于受信移动交接。operator 令牌排除 operator.adminoperator.pairingoperator.talk.secrets
  • 当非基线设置码引导正在等待批准时,PAIRING_REQUIRED 的详细信息包括 recommendedNextStep: "wait_then_retry"retryable: truepauseReconnect: false。客户端应继续使用相同的引导令牌重新连接,直到请求被批准或令牌变得无效。
  • 仅当 connect 使用了受信传输(如 wss:// 或环回/本地配对)上的引导认证时,才持久化 hello-ok.auth.deviceTokens
  • 如果客户端提供显式 deviceToken 或显式 scopes,则调用方请求的作用域集保持权威;仅当客户端重用存储的按设备令牌时,才会重用缓存的作用域。
  • 设备令牌可以通过 device.token.rotatedevice.token.revoke 轮换/吊销(需要 operator.pairing 作用域)。
  • device.token.rotate 返回轮换元数据。仅对已使用该设备令牌认证的同一设备调用回显替换承载令牌,以便仅令牌客户端可以在重新连接前持久化其替换。共享/admin 轮换不回显承载令牌。
  • 令牌签发、轮换和吊销保持在该设备配对记录中批准的委托角色集范围内;令牌突变不能扩展或针对配对批准从未授予的设备角色。
  • 对于配对设备令牌会话,设备管理是自作用域的,除非调用方也具有 operator.admin:非 admin 调用方只能移除/吊销/轮换它们自己的设备条目。
  • device.token.rotatedevice.token.revoke 还会检查目标 operator 令牌作用域集与调用方当前会话作用域。非 admin 调用方不能轮换或吊销比它们已有的更宽的 operator 令牌。
  • 认证失败包括 error.details.code 加上恢复提示:
    • error.details.canRetryWithDeviceToken (boolean)
    • error.details.recommendedNextStep (retry_with_device_tokenupdate_auth_configurationupdate_auth_credentialswait_then_retryreview_auth_configuration)
  • 客户端处理 AUTH_TOKEN_MISMATCH 的行为:
    • 受信客户端可以尝试使用缓存的按设备令牌进行一次有限重试。
    • 如果该重试失败,客户端应停止自动重连循环并显示 operator 操作指导。
  • AUTH_SCOPE_MISMATCH 表示设备令牌被识别但不覆盖请求的角色/作用域。客户端不应将其视为坏令牌;提示 operator 重新配对或批准更窄/更宽的作用域合约。

设备身份与配对

  • 节点应携带从密钥对指纹派生的稳定设备身份(device.id)。
  • Gateway 按设备 + 角色颁发令牌。
  • 新设备 ID 需要配对审批,除非启用本地自动审批。
  • 配对自动审批以直接本地环回连接为中心。
  • OpenClaw 还有一个狭窄的后端/容器本地自连接路径,用于受信的共享密钥辅助流程。
  • 同主机 tailnet 或 LAN 连接在配对方面仍视为远程,需要审批。
  • WS 客户端通常应在 connect 时包含 device 身份(operator + 节点)。唯一的无设备 operator 异常是显式受信路径:
    • gateway.controlUi.allowInsecureAuth=true 用于仅 localhost 的不安全 HTTP 兼容模式
    • 成功的 gateway.auth.mode: "trusted-proxy" operator Control UI 认证
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true(紧急方案,严重安全降级)
    • 直接环回 gateway-client 后端 RPC,使用共享网关令牌/密码认证
  • 所有连接必须对服务端提供的 connect.challenge nonce 进行签名。

设备认证迁移诊断

对于仍使用挑战签名之前旧版行为的遗留客户端,connect 现在会在 error.details.code 中返回 DEVICE_AUTH_* 详细码,并在 error.details.reason 中提供稳定原因。

常见迁移失败:

消息details.codedetails.reason含义
device nonce requiredDEVICE_AUTH_NONCE_REQUIREDdevice-nonce-missing客户端未提供 device.nonce(或为空)
device nonce mismatchDEVICE_AUTH_NONCE_MISMATCHdevice-nonce-mismatch客户端使用了过期/错误的 nonce 进行签名
device signature invalidDEVICE_AUTH_SIGNATURE_INVALIDdevice-signature签名载荷与 v2 载荷格式不匹配
device signature expiredDEVICE_AUTH_SIGNATURE_EXPIREDdevice-signature-stale签名时间戳超出允许的时钟偏差范围
device identity mismatchDEVICE_AUTH_DEVICE_ID_MISMATCHdevice-id-mismatchdevice.id 与公钥指纹不匹配
device public key invalidDEVICE_AUTH_PUBLIC_KEY_INVALIDdevice-public-key公钥格式或规范化失败

迁移目标:

  • 始终等待 connect.challenge
  • 使用包含服务端 nonce 的 v2 载荷进行签名。
  • connect.params.device.nonce 中发送相同的 nonce。
  • 推荐使用 v3 签名载荷,它在 device/client/role/scopes/token/nonce 字段之外还绑定了 platformdeviceFamily
  • 向后兼容:v2 签名仍被接受,但配对后设备元数据固定仍会在重连时控制命令策略。

TLS 与证书固定

  • WS 连接支持 TLS。
  • 客户端可选择性地固定 Gateway 证书指纹(参见 gateway.tls 配置以及 gateway.remote.tlsFingerprint 或 CLI --tls-fingerprint)。

接口范围

本协议暴露了完整的 Gateway API(状态、渠道、模型、聊天、agent、会话、节点、审批等)。确切接口范围由 src/gateway/protocol/schema.ts 中的 TypeBox schema 定义。

相关文档

常见问题

connect 请求返回 UNAVAILABLE 错误怎么办?

表示 Gateway 仍在启动 sidecars。检查 error.details.reason 是否为 "startup-sidecars",如果是,根据 retryAfterMs 重试。应在整体连接预算内重试,不要视为最终失败。

设备配对时权限不足,提示 AUTH_SCOPE_MISMATCH 怎么处理?

设备令牌被识别但请求的角色/作用域不在已批准的范围内。需要重新配对或由 operator 审批更宽/更窄的作用域合约。不要将设备视为坏令牌,应提示操作者。

协议版本协商失败如何排查?

客户端发送 minProtocolmaxProtocol,服务端仅接受包含其 PROTOCOL_VERSION 的版本范围。当前双方都要求协议 v4。如果客户端发送范围不包括 v4,服务端会拒绝连接。运行 pnpm protocol:check 可以验证本地 schema 一致性。