引导与事件
会话列表引导¶
调用 sessions.subscribe 并传入非空的 sessions.list 参数对象,例如 { limit: 60, ownerFirst: true },即可在一次请求中完成订阅并加载初始列表。成功的 WebSocket 响应负载为 { subscribed: true, list },其中 list 是普通的 SessionsListResult。使用 {} 调用则保留仅确认的响应 { subscribed: true },且不读取快照。列表参数用于选择快照;它们不过滤该连接上对会话事件的订阅。
Gateway 在投影列表之前注册订阅。客户端必须在发起请求之前监听 sessions.changed:事件可能在快照构建期间到达。请将这些事件与响应进行对账,并在需要时发出后续的 sessions.list 刷新,包括事件仅令缓存列表失效的情况。重新连接需要新的订阅和快照。
Gateway 在内存中保存持久化的会话元数据,并在正常启动完成之前完成其初始行物化。重新连接的客户端一旦 Gateway 就绪即可读取初始列表。已提交的属主变更会增量刷新受影响的行;不存在已完成页面的缓存,也没有一秒的陈旧窗口。按键的描述、解析和聊天启动会为其请求的行做准备,而无需等待批量刷新。新加入或被替换的存储会加载一次其元数据,当存储离开当前拓扑时,行会消失。每个响应都会应用当前查看者的可见性和当前活动时间。
常驻行使用存储的标题和使用量。可选的消息预览和终端回退模型元数据通过有界只读后台转录读取来填充;它们可能在早期响应中缺失。前台请求优先。这些读取不会恢复冷归档、解析超长消息、调用模型,也不会更改存储的元数据或会话活动顺序。缺失的历史标题和旧版 ACP 键仅能通过 openclaw doctor --fix 修复。缺失的使用量会一直保持缺失,直到正常的用量写入器记录它。
这两种方法都接受 activeOnly: true,用于在分页之前选择当前正在运行或已排队的会话。活动状态来自实时运行时属主,而非存储的状态标志。当该选项被省略或为 false 时,普通列表行为保持不变。仅活动结果包含每个可见的、由代理拥有的 global 和 unknown 会话,以及其原始键和捕获的 agentId;调用者通过代理、键和 sessionId 共同识别行。字面意义上的 agent:<id>:global 和 agent:<id>:unknown 会话仍然是不同的行。仅活动结果中的原始哨兵行省略了可选的 childSessions 和 hasActiveSubagentRun 字段;请使用 hasActiveRun 来判断直接活动。普通权限、归档/包含过滤器和分页限制仍然适用。无会话/内部运行不在会话索引之内。
这两种方法都接受 ownerFirst: true,以便在普通第一页前面插入最多 60 个匹配的、查看者拥有的行(或更小的 limit),并按会话键去重。这仅在 offset 为零或省略时适用;后续页面使用普通分页。拥有的行必须通过与共享页面相同的可见性和列表过滤器。Gateway 从已认证的连接解析查看者;没有任何客户端提供的身份来选择这些行。如果没有已认证的查看者身份,或者 ownerFirst 为 false 或被省略,列表使用普通排序。
共享页面仍然决定 limitApplied、offset、nextOffset、hasMore 和 totalCount。前置的行可能使 sessions.length 和 count 超过共享页面大小。使用 nextOffset 前进并按会话键在页面间去重;不要从显示的行数推导下一个偏移量。
会话消息订阅与叙述¶
sessions.messages.subscribe 将一个连接订阅到某个会话的实时消息。其 key 和可选的 agentId 用于选择会话;这独立于上面的宽泛列表订阅。省略 mode 可获得完整的 chat 和 agent 流,包括前台转录以及由其他客户端启动的运行被动视图。重复请求会替换该观察者的订阅模式。sessions.messages.unsubscribe 移除由相同可选 subscriptionId 标识的观察者;省略该 ID 则选择旧版观察者。
订阅确认包含规范的 key 和解析后的 agentId。客户端保留解析后的属主,以用于后续的 global 请求,因为单独的键无法标识代理。SDK 为每个线上(wire)观察者发送一个稳定的不透明 subscriptionId,并在重新订阅和取消订阅请求中包含它。对于同一连接和会话上的多个 ID,全流兴趣优先,直到其最后一位持有者释放;只要有任何持有者请求,批准(approvals)仍保持启用。省略该 ID 会保留旧版单观察者行为。较旧的客户端与更新的 Gateway 保持兼容;更新后的 SDK 的属主字段需要更新的 Gateway。
后台叙述消费者声明 mode: "narration"。Gateway 将其 token 级别的 chat 增量和原始的 agent 助手事件替换为 session.narration 快照。每个快照包含 sessionKey、可选的 agentId、runId 和 text:当前可见助手尾部的最多 16,384 个字符。在尾部被截断之前,会移除隐藏的推理和内部上下文。空的 text 会撤销之前的叙述。消费者可以从该文本中推导出紧凑的一行,而无需重建 token 增量。
第一次文本更新可能立即到达。随后的快照在每个连接每个会话上最多每两秒到达一次,并使用最新文本,而不会推迟既定的截止时间。终止性聊天事件会在终止事件之前立即刷新最后一个快照,包括最终的文本更正或撤回;此最终刷新不受两秒间隔的限制。较新的工具活动或运行的更改会丢弃旧的待处理文本,因此延迟的快照不能替换较新的工具行,也不能将侧边栏切换回较旧的运行。生命周期、状态、工具、最终、中止和错误事件保持其现有投递。原始思维流和进行中的前言或候选答案文本会被省略;条目完成和答案选择仍然到达。批准事件仍然需要 includeApprovals: true 和正常的批准权限。排队的叙述会在取消订阅、模式更改、连接退役或运行退役时被丢弃,并且投递时会重新检查当前访问权限。
Gateway 客户端 SDK 在本地所有者之间共享匹配的会话地址。Gateway 还会合并解析到同一订阅键的独立标识观察者。如果任何所有者需要完整流,则投递保持完整;只有在最后一个完整所有者释放后,才恢复到叙述模式。共享前台订阅的叙述消费者也必须接受完整流事件。Control UI 在获取历史记录之前会等待前台准入,因此快照涵盖启用完整流之前发出的活动。
捆绑的 Control UI 为侧边栏关注项声明叙述意图。它与自己的 Gateway 版本锁定,并在升级时重新加载。共享的 Apple 聊天客户端对前台会话使用默认的完整模式;Android 和 TUI 保留其广泛的事件投递。现有 SDK 调用方以及省略 mode 的旧客户端保留完整流。因此,免除构建准入的自定义 UI 根、开发 UI 和跨源 UI 在更新之前可以保留完整流叙述。这是协议 v4 的附加契约,不涉及能力协商或协议版本变更。
常用事件族¶
-
chat:UI 聊天的更新,例如chat.inject以及其他仅记录的聊天事件。state: "delta"负载通过deltaText携带追加内容。在一次运行中,发送给接收者的首个文本帧还包含完整的message快照,包括该接收者在运行中途加入或重新连接的情况。后续追加帧省略message。提供的快照是权威的,并且已经包含deltaText;不要重复追加 delta。非前缀替换会设置replace=true,并将deltaText作为整个替换文本,包括用空字符串来清除内容。需要新基线的替换以及画布或媒体更改包含完整快照。客户端在普通文本追加期间保留非文本消息块。最终、中止和错误事件保留其现有的完整消息和有意省略消息的语义。待处理的追加按顺序串联;工具和终端边界在结算前刷新待处理文本。失败运行(state: "error")可能包含errorDetail,同时还有粗略的errorKind和人类可读的errorMessage。这个封闭对象有七个可选字段:provider、model、failoverReason、providerRuntimeFailureKind、providerErrorType、httpStatus和providerErrorMessagePreview。字符串上限为 300 个字符;httpStatus是 100 到 599 之间的整数。详细信息来自失败尝试的已清理提供者观察结果,而不是重新解析面向用户的消息。预览会进行凭据脱敏,并且可能短于协议上限。原始正文、原始预览和诊断哈希永远不会包含在errorDetail中。没有提供者观察结果的运行会省略该字段;成功和已取消的事件不携带该字段。这是协议 v4 的附加字段。 -
agent:助手文本事件使用data.delta进行追加。可选的data.text是该助手条目的权威快照,并且已经包含 delta。首个投递的文本事件、替换/条目边界以及媒体更新在需要时保留快照。请遵守data.replace,包括空替换,并将助手条目文本与显示投影的chat流分开。为一个显示订阅一种文本投影;将两个流都消费到同一个累加器中会重复输出。进程内 agent 观察者保留其累积文本约定。仅从chat渲染助手文本的客户端可以在其连接caps中声明chat-only-assistant-text。随后,Gateway 会从该连接中省略带有stream: "assistant"的含文本agent事件,包括前台和后台叙述订阅。其他 agent 流(工具、条目、使用量、运行状态、生命周期、计划和审批)、不含文本的助手事件以及chat流保持不变。被过滤的帧不会消耗连接序列号。不具备该能力的客户端保留两种投影;将当前条目 agent 文本与累积聊天文本分开显示的原生客户端不应声明该能力。 -
session.message、session.operation、session.tool:针对已订阅会话的记录、进行中的会话操作以及事件流更新。 -
session.narration:为具有叙述意图的订阅提供有界助手文本快照,按上述方式限速并结算。 -
session.approval:为明确选择加入的精确会话订阅者提供已清理的待处理及终局审批真值。子审批使用持久化的祖先受众;事件从不改动记录或唤醒 agent。 -
session.observer:安全的实时会话标题和状态摘要。模型撰写的开场白可以立即更新标题;实用模型评估在可用时稍后替换它。Web、iOS 和 Android 使用相同的运行范围摘要。可选的sessionId和不透明的lifecycleRevision标识会话生命周期;在第一次重置之前lifecycleRevision可以不存在。修订版本在该生命周期内的各次运行之间递增,但重置后可以重新开始。/clear保留sessionId并更改lifecycleRevision。客户端仅在摘要的确切runId存在于activeRunIds中时显示其标题或检查器链接。 -
sessions.changed:会话索引或元数据发生更改。带键的更改在session中携带受影响的行,并针对该连接呈现。sessions.changed和session.message中的嵌套行使用与sessions.list相同的完整预准备元数据、查看者权限和时钟,并启用标题、最后消息和活动摘要富化。这会在事件行中添加目录支持的字段(例如思考选项),并将旧版模型别名替换为规范化模型 ID。Control UI 在本地将这些行应用于现有名册成员,因此它们的值与列表匹配。reason: "patch"事件提交模型、账户或运行时选择时,还会携带catalogChanged: true;客户端可以将其他补丁视为仅会话补丁并保留缓存的目录。顶层生命周期和容量字段仍然是事件回执,包括显式清除值。当嵌套行省略可选字段时,请遵循其顶层清除墓碑;嵌套值存在时优先。当查询的成员身份和分页窗口仍然有效时,在本地合并现有名册成员的快照。Control UI 针对身份、归档、置顶、所有者和父级事实不变以及新近度非递减的持有行,复用生命周期和普通patch、participants、placement、send、steer、agent.run.started、agent.input.settled、run-capacity和chat.title快照。带键的sessions.changed和session.message发布还携带ancestorSessions,这是一个由受影响的导航、控制、请求者和群体祖先的完整行组成的数组,并且可能携带ancestorSessionRefs,用于该连接上已投递的未更改祖先呈现。引用永远不会出现在ancestorSessions内部。投影沿现有父引用向上遍历到根,对物理行身份进行去重,并终止循环。每个祖先都通过与sessions.list相同的按查看者可见性过滤和呈现;不可见的中间祖先不会阻止其上方可见祖先的投递。这两个数组合计最多包含 64 个祖先。如果某个祖先无法解析,或遍历超过该上限,则两个字段都会被省略,以便客户端保留权威刷新行为。ancestorSessions的存在证明两个数组中可见祖先覆盖完整;只有在ancestorSessionRefs也不存在或为空时,空数组才证明没有可见祖先。这些是协议 v4 的附加字段;它们不会改变订阅范围或列表成员身份,也不需要能力协商。完整祖先行携带不透明的ancestorRevision;每个引用包含key、revision和snapshotAt,并在完整行上存在sessionId和agentId时包含它们。在添加修订标签之前,Gateway 会比较精确的、针对特定连接的呈现行,仅排除snapshotAt。引用证明由revision标识的呈现保持不变。客户端仅当仍持有具有匹配身份的精确准入呈现时,才保留被引用的行,然后推进其时钟而不更改其他事实。Control UI 仅在确认其事实与完整呈现行匹配后,才将修订绑定到不可变的准入行。列表替换或本地行更改会失去该证明,即使其采样时钟恰好匹配。缺失行、代际或修订不匹配,以及不确定的呈现归属,都需要现有的权威刷新路径。Gateway 会限制此按连接记录,并在首次投递、成功读取列表、重新连接、重新订阅、重置/删除、呈现或可见性更改、逐出或不确定投递之后发送完整行。取消订阅和断开连接会清除该记录;会话删除会使已记住的祖先失效。仅重述(reason: "activity-summary")事件保留其祖先负载,但这些事件中的完整行会使相应的引用记录退役:共享名册可以跳过重述准入。下一个普通事件会再次提供完整行;客户端已持有的未更改引用仍然可复用。较旧的 Web 客户端会忽略该附加引用字段。由于ancestorSessions仅包含完整行,因此它们不能将引用作为部分行应用并擦除已持有的字段。缺失的祖先快照会触发其现有的权威sessions.list刷新。捆绑的同源 Control UI 构建准入可防止版本偏差;自定义根、开发 UI 和跨源客户端可以使用这条正确但较慢的路径。原生 Apple 和 Android 客户端、TUI 和 SDK 不协调祖先行,并保留其现有行为。客户端将子行和持有的完整祖先行一起应用,遵循每一行的身份和时钟。在完整快照中,省略的可选行事实会清除先前持有的值,包括子链接、群体摘要和后代运行标志。非空的旧版顶层行字段不会填补完整、经过查看者过滤的行中的省略。显式 null 清除回执和独立生命周期回执仍然有效。现有的可选标题/预览富化和思考元数据保留规则仍然适用。Control UI 会针对缺失行或快照、广泛/无键更改、catalogChanged、成员过滤、不完整的祖先快照以及不确定边界(包括提升到共享页面中的所有者优先行)合并权威刷新。与名册读取重叠的事件保留尾随刷新,以便其响应不会丢失更新。带有读取错误的保留列表也会在下一个相关事件时刷新。个人资料身份、运行器可用性和已加载的 cron 绑定可能产生广泛的失效。仅活动摘要的发布会更新已选择加入的 Activity 消费者;共享会话和 agent 名册不会针对这些仅重述更改重新获取。在突发期间,预准备的行发布会在有界切片之间让出。同一会话代际的待处理活动摘要更新共享最新快照;生命周期、容量、记录、删除和清除回执保持独立。发布会在每次让出后重新检查行就绪状态,关闭时会在销毁其投影之前加入已准入的发布。经授权的隐身描述和事件使用来自瞬时进程本地状态的相同行呈现。隐身行仍被排除在会话之外。
名册以及排队的事件不能跨越重置或数据库替换。工具/进度事件在行刷新期间持续投递;其可选的行元数据在就绪之前可能缺失。在带键的 sessions.changed 和 session.message 快照中,完整名册行始终有保证。活动运行字段使用与 sessions.list 相同的聚合和完整精确语义;activeRunIds: null 将缓存的精确标识清除为不可用,省略该字段则使缓存保持不变,而数组则替换它。来自 sessions.delete 和隐身重置的删除通知携带被移除世代的 sessionId,不附带当前行快照。客户端不得删除具有不同 ID 的替换项。仅键的删除事件或无形全局通知会使规范会话列表失效;它不表示当前世代已被删除。
presence:系统在线状态快照更新。tick:周期性保活/存活事件。health:网关健康状态快照更新。heartbeat:心跳事件流更新。cron:cron 运行/作业变更事件。shutdown:网关关闭通知。node.pair.requested/node.pair.resolved:节点配对生命周期。node.invoke.request:节点调用请求广播。device.pair.requested/device.pair.resolved:已配对设备审批生命周期。device.pair.setup.completed:设置码精确交接完成,作用域限定为operator.pairing。device.pair.setup.deliveryUncertain:防重放的设置码作废,其凭据响应的投递无法确认,作用域限定为operator.pairing。voicewake.changed:唤醒词触发配置已更改。plugins.changed:插件运行时发布完成。负载为{ generation };刷新plugins.list以协调已安装状态与运行时状态。config.changed:一次配置写入已持久化(负载携带配置路径、新快照哈希和时间戳——绝不包含配置内容)。限定为操作员读取作用域;客户端通过config.get刷新。skills.changed:网关使其技能快照失效后,连接性、技能目录、配置或资格发生变化。负载的reason为watch、watch-targets、manual、remote-node、config-change或workshop。限定为操作员读取作用域;客户端通过skills.status刷新。exec.approval.requested/exec.approval.resolved:exec 审批生命周期。plugin.approval.requested/plugin.approval.resolved:插件审批生命周期。
节点辅助方法¶
节点可调用 skills.bins 获取当前技能可执行文件列表,用于自动允许检查。
节点执行生命周期事件¶
节点通过节点角色的 node.event RPC 上报 system.run 生命周期,事件为 event: "exec.started"、"exec.finished" 或 "exec.denied"。这些不是操作员的 exec.approval.* 广播,也不使用已退役的 TCP 桥接。
该 RPC 接受 payloadJSON 中的 JSON 字符串或 payload 中的对象。若同时提供两者,字符串 payloadJSON 优先。例如:
{
"event": "exec.finished",
"payload": {
"sessionKey": "agent:main:main",
"runId": "<exec-run-id>",
"host": "node",
"exitCode": 0,
"timedOut": false,
"success": true,
"output": "done"
}
}
当前无头节点包括 sessionKey、runId 和 host: "node"。其他字段包括:
| 字段 | 含义 |
|---|---|
command |
原始或格式化后的命令文本。 |
exitCode, timedOut |
进程完成码和超时标志。 |
success |
生产者结果标志,而非通知门控谓词。 |
output |
有界合并的 stdout、stderr 和错误文本。 |
reason |
exec.denied 的拒绝原因。 |
suppressNotifyOnExit |
抑制此调用的系统通知。 |
回显随 system.run 转发的关联字段;ID 或负载中的 host 字段本身都不授予权限。当调用绑定会话键时,Gateway 会匹配已认证的节点和连接、运行 ID 以及会话键。不匹配的事件返回 handled: false 且 reason: "unmatched_exec_event",并且不产生系统通知。一个狭窄的旧版 macOS 客户端路径可能将缺失或不匹配的运行 ID 仅匹配到该连接/会话上的一个明确调用;新客户端必须发送已下发的运行 ID。
exec.started 保留授权记录;exec.finished 和 exec.denied 在通知过滤之前消耗该记录。tools.exec.notifyOnExit: false 或 suppressNotifyOnExit: true 会抑制通知。被拒绝的事件绝不会入队系统事件或唤醒代理工作。已完成事件仅在超时、非零或未知退出码,或非空压缩输出时通知;成功退出 0 且无输出时保持静默。带有运行 ID 的已完成通知按规范会话和运行 ID 去重。仅在系统事件入队后才请求心跳唤醒。
节点事件投递是尽力而为的,不是持久化的完成账本。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw