快速开始
会话状态感知
当多个会话处理同一个问题时——例如管理者将任务委派给子会话、人工直接进入工作会话,或两个智能体通过 sessions_send 进行协调——每个会话都会形成对其他会话的假设。一旦另一参与者介入,这些假设就会过时。会话状态感知机制用于检测这种介入,向受影响的会话通知一次,并为其提供一种低成本的方式,以便在采取行动前掌握最新情况。
三个部分协同工作:
- 持久信号日志按会话记录选定的状态变更。
- 监视器保存各目标的游标,并接收一条合并后的状态过时通知。
- 协调同步通过带有
changesSince的session_status拉取精确增量。
信号日志
当受监视的会话发生实质性变更时,OpenClaw 会将一个类型化事件追加到共享状态数据库(session_state_events)中。事件包含元数据和单行摘要,但绝不包含消息内容。
| 类型 | 记录时机 | 通知监视器 |
|---|---|---|
human_direct_message |
人工直接向受监视的会话发送一个轮次 | 是 |
upstream_missing |
已接管会话的上游来源消失 | 是 |
goal_changed |
创建、更新或清除会话的目标状态 | 是 |
child_spawned |
创建子智能体或 ACP 子会话 | 否(初始化游标) |
run_completed |
子运行成功结束 | 否(仅记录日志) |
run_failed |
子运行失败、超时或被取消 | 否(仅记录日志) |
compacted |
会话历史记录被压缩 | 否(仅记录日志) |
adopted |
目录会话被接管到 OpenClaw 中 | 否(仅记录日志) |
每个事件都会标明其参与者(human、agent 或 system)。被取消和超时的子运行会记录为失败,同时在事件载荷中保留精确结果(cancelled、timeout 或 error)。
会话的状态版本就是其日志中的最大序列号,并由一个持久的按会话记录的头部跟踪,该头部在日志清理后仍然保留。当会话记录过变更时,sessions_list 行会包含 stateVersion;session_status 则始终报告该值。
仅记录日志的类型用于协调同步历史记录,而不是通知:常规的子运行完成通知仍由子智能体通知负责,信号日志绝不会重复发送。
监视器
监视器是持有目标游标(session_watch_cursors)的会话。游标有两个来源:
- 隐式(生成边)。 当会话生成子智能体或 ACP 子会话时,父会话的游标会自动初始化为子会话生成时的版本。父会话从不手动订阅。
- 显式(
sessions_send watch: true)。 任何协调者都可以监视并非由其生成的目标:在sessions_send上传入watch: true,发送成功分派后,发送方会被注册为实际收到消息的会话的监视器。注册从目标当前的状态版本开始——之前的历史记录绝不会产生通知。设置该参数后,工具结果会报告watched: true|false。
监视器身份必须是包含智能体限定信息的会话键。在 session.scope="global" 下,共享的 global 键在不同智能体之间存在歧义,因此此类会话会获得持久日志和 changesSince,但不会收到主动通知。
监视关系会自行清理:游标行会随信号日志保留期到期,在监视器会话重置时移除,并在任一会话被删除时一并删除。v1 中没有取消监视的操作。
从会话目录接管的受监视会话会按固定周期检查上游人工直接活动。检测到的活动会像其他人工直接轮次一样进入同一信号日志和监视器流程。
如果已接管会话的上游来源被外部删除,连续三次检查未找到该来源(约三个监控周期)后,会为其监视器生成一条 upstream_missing 信号,并移除上游链接。再次继续该目录会话时,会创建新的链接。
通知:一条,而非多条
当可触发通知的事件写入,而监视器的游标落后时,监视器会在下一轮收到一条系统通知:
会话 "agent:main:subagent:child" 已发生变更(其他参与者)。请在采取行动前协调同步:session_status sessionKey "agent:main:subagent:child" changesSince 12。主会话监视器还会通过 Heartbeat 唤醒立即被唤醒;嵌套的子智能体监视器则会在下一轮收到通知。
该协议特意采用防刷屏设计:
- 每个监视器/目标组合仅有一条待处理通知。 通知文本在待处理期间保持字节级稳定,系统事件队列会据此去重,因此即使同一目标快速发生二十次变更,监视器的提示词中仍只会出现一行。
- 冻结水位线。 通知进入队列时,游标会冻结其已通知位置。后续实质性事件只推进实质性变更水位线,不会再次发出通知。
- 消费时确认,仅在工作交错时重新开启。 当监视器的轮次消费通知时,游标会前移。如果从通知入队到被消费之间又发生了更多实质性事件,则会为剩余事件恰好开启一条新通知。
- 自我抑制。 监视器绝不会收到由自身引发的事件通知。
- 重启恢复。 待处理通知保存在内存队列中;Gateway 网关重启后,启动扫描会根据持久游标重新生成这些通知。
协调同步
通知会明确告诉监视器该如何处理。带有 changesSince: <version> 的 session_status 会返回该版本之后的类型化事件(最多 200 个),且不会推进任何游标:
{ "stateVersion": 19, "stateChanges": { "events": [ { "sequence": 14, "kind": "human_direct_message", "actorType": "human", "summary": "人工通过 telegram 发送消息" }, { "sequence": 19, "kind": "goal_changed", "actorType": "human", "summary": "目标已更新" } ], "historyGap": false }}historyGap: true 表示请求的版本早于所保留的历史记录——此时应刷新完整会话状态(sessions_history、session_status),而不是将响应视为精确增量。历史缺口信号是精确的:它来自按会话记录的已清理水位线,而不是通过序列号运算推断得出。
存储和限制
历史记录存储在共享状态数据库中,保留上限为 30 天和 50,000 行;日志清理后,按会话记录的头部仍保持单调递增。记录采用尽力而为方式——追加失败时只会写入日志,绝不会导致发起该变更的轮次失败——因此 stateVersion 是信号日志头部,而不是事务性变更数据捕获版本。
当前限制:
- 通知投递假设由单个 Gateway 网关进程拥有共享状态数据库。多个 Gateway 网关可以共享持久日志和
changesSince,但 v1 不会跨进程推送通知。 - 压缩事件覆盖嵌入式运行时的压缩所有者;仅由原生 harness 执行的压缩尚未被完整记录。
- 目前只有 ACP 子运行会生成被取消结果的详细载荷;原生子智能体的取消会显示为通用失败。
- 上游自回显检测会比较规范化后的用户文本。如果外部提示词与会话最近 10 条 OpenClaw 侧用户消息之一相同,则会被视为自回显。
- 单条本地 Claude JSONL 记录如果大于每周期 1 MiB 的扫描上限,就会在 v1 中阻塞该会话的游标;未分类的字节绝不会被跳过。
- 配对节点的 Claude 检查会在每个周期对最新 50 个对话记录项进行分类。更大的突发数据量可能会超出 v1 的扫描窗口。
- 配对节点的 Claude 历史记录读取不会提供明确的线程未找到结果,因此在 v1 中,远程 Claude 删除不会被归类为
upstream_missing。 - 尚未接管的目录会话在 v1 中仍不属于状态感知层。
- 在此功能推出前接管的会话没有上游链接;从目录中继续运行一次这些会话,即可开始上游监控。
- 上游链接假设每个已接管会话键只映射到一个所属智能体(接管操作使用默认存储智能体)。v1 不会监控多个智能体接管同一外部线程的情况。