跳转至

重启恢复

对话、记录、计划任务、原生子代理记录和排队中的出站消息都保存在磁盘上。网关重启后,回合中途被中断的合格工作会被自动检测并恢复。恢复功能始终开启,通常无需人工干预。基础设施重试耗尽,或缺少持久的消息操作权限声明,可能会将某个会话标记为墓碑状态,直到您检查或替换它为止。

本页说明重启后哪些状态会保留、中断的工作如何被检测,以及自动恢复的具体表现。

重启后哪些状态会保留

状态 存储 重启后的行为
对话历史 每个代理的 SQLite 数据库 不受影响;会话从存储的记录继续
已接受的 Control UI 后续输入 每个代理的 SQLite 待处理输入和浏览器发件箱 浏览器重新连接时,匹配的中断输入会被重新接纳
中断的主会话回合 每个代理的 SQLite 会话行和记录 启动后几秒内自动恢复或协调
子代理运行 SQLite(共享状态数据库) 中断的运行会收尾;由父代理决定如何继续
排队的出站投递 SQLite 投递队列 重启后排空;未投递的回复会重试
计划(cron)任务 SQLite cron 存储 计划保持不变;调度器在启动时重新武装
重启延续 SQLite 重启哨兵 一次性后续操作被派发到请求重启的会话
网关终端 PTY 进程内存 随旧进程结束;终端会话不会被恢复

Control UI 会将其接受的文本和附件保留在发件箱中,直到 Gateway 确认记录已被消费。重新连接后,它会在通过正常身份验证和会话接纳重新提交中断的输入之前,检查已保存的回执。旧的队列和执行权限绝不会被复用。这保持了每个浏览器发件箱的顺序,同时不会再次提交已消费的消息。不同浏览器可以按不同顺序重新连接。

已接受的 Control UI 输入会在 ACP 执行或问题消费之前提交到其被接纳的源会话。如果对话路由到绑定的 ACP 会话,源会话保留原始请求,绑定会话拥有 ACP 记录和回复。输出持久化失败不会使已消费的输入有资格被自动重新提交。

如果浏览器不再拥有匹配的发件箱载荷,已保存的中断输入仍可被显式重发。已取消的输入不会被重放。由旧版本接受但没有可恢复保管权的输入也需要显式重发。不确定的提交将保持未确认状态,直到其结果可以被对账或用户选择重试它。已在记录中的回合的恢复不依赖于浏览器重新连接。

当更新替换了捆绑的 Control UI 时,打开的标签页会在 Gateway 报告新构建版本后重新加载。针对该报告构建的自动恢复和手动重新加载共享一个有边界的文档就绪检查,因此短暂的探测失败不会立即让标签页陷入困境。通用的懒加载块失败会触发一次自动探测,并将进一步的恢复留给可见的重试操作。浏览器仍将自动导航限制为每个目标构建版本一次重新加载。如果 Gateway 仍然不可用,请在它可达后使用可见的重新加载操作。

降级到 v2026.9.2 会保留较新的已接受输入记录,但该版本会在执行前拒绝针对保留的较新回执进行同 ID 重试。这包括排队或中断的输入,以及已消费的已收集输入回执。已消费的回执仍不计入待处理计数。单独消费的输入已经离开待处理输入存储,并保留正常的记录幂等性。再次升级会通过全新的身份验证和接纳来恢复匹配的未消费输入恢复,前提是会话和已接受的输入未被更改或移除。已消费的输入保持已消费状态。不要仅仅为了绕过降级冲突而删除回执或更改消息 ID。

原生 Codex 恢复会在当前的附件策略和上下文限制下重建已保存的文档内容。Codex 插件拥有该原生输入路径:请将其构件与 Gateway 一起更新。有意锁定为旧版本的插件不会从仅核心更新中获得该修复。

分离式 harness 的完成若已进入带有出站通道路由的父回合,则可以保留精确的源完成、最终结果和请求者会话声明。崩溃后,主会话恢复会继续该已接纳的回合,而不会重新启动子进程。原始完成身份与恢复运行保持区分。即使旧的本地监视器不再存在,保留的最终回执也可以结算已接纳的完成;待处理的恢复声明不是已投递的结果。较晚的结果不能授权较早的完成输入或结算其回执。取消、会话替换以及缺失或矛盾的源身份不会授权重放。被持有的接纳或仍然有效的已接纳输入会使恢复保持待处理状态。原生父轮换必须保留当前连接和请求者身份检查。较晚的父注册不能提供缺失的历史所有权。

在父完成接纳之前,原生 Codex 待处理分配会在现有的父绑定元数据中保留其运行、子线程、原生父和已知原生回合身份。已接受的后续输入也可以在那里保留其提交回执。不会创建新的表或 Tasks 记录,也不会导入历史 Tasks 行。当父进程再次注册时,恢复会在新的主机签发完成权限下读取已保存的分配和原生历史。它在恢复观察或路由结果之前检查原始请求者会话、生命周期、连接和子代谱系。原生父线程轮换可以在相同的请求者生命周期和连接内保留该元数据;重置、请求者代际替换或连接策略变更不会授权接管。缺失的历史所有权保持未知。此恢复需要保留的分配和新的父注册;仅凭原生谱系无法重建未记录的完成义务。

上文所述的已受理回合恢复适用于新记录的频道投递声明,不适用于无路由、仅转录或 Control UI 父级。它不会恢复每一个旧的原生完成。进度消息、空或截断的回执以及不确定的发送无法确立最终投递。Gateway 和 Codex 插件都必须更新才能参与恢复。旧版本构建可能会在重写会话元数据时丢弃新增的回执字段,即使没有 SQL 架构更改。在降级前完成待处理的完成恢复;不要删除已消费的输入或更改源 ID 来强制另一次投递。

重启后,待处理的投递行会排空或重试。当投递耗尽重试预算时,恢复机制会收回已过期的生产者持有权。活跃的生产者保留所有权。失败的投递不能再次发送,但会保留结算其所属会话或对话所需的信息。如果该更新失败或 Gateway 崩溃,恢复机制会在不重新发送消息的情况下继续该更新。结算后,失败的行会丢弃其有效载荷。只有可重用或崩溃状态不明确的所有者才会保留一个最小的有界或永久回执,以防止重复投递。投递不确定性通知会保留其确认信息,因此重复结算不会再次通知同一意图。

在降级前完成待处理的结算。旧版本构建可能会在数据库修复期间丢弃其元数据,或在重写会话记录时丢弃已确认的通知,即使架构版本未更改。降级注意事项请参阅 数据库架构。

优雅重启先排空

启动迁移警告不会阻止 Gateway 启动。它只记录一次警告,然后以降级模式启动。openclaw status 和 openclaw doctor 会显示正在运行的 Gateway 的警告报告。只读操作员会收到修复提示。警告详情仅限管理员和启动日志查看。针对相同的状态/配置运行 openclaw doctor --fix,然后重启 Gateway。未完成的迁移会保留为待处理状态,供后续启动时继续。如果错误导致所需状态无法安全读取,仍会阻止启动。

请求的重启(openclaw gateway restart、需要重启的配置更改或 gateway 更新)不会立即终止正在进行的工作。Gateway 会停止接受新工作,然后等待活动的 agent 回合和后台任务完成,最长不超过排空预算(默认为 5 分钟)。因此,大多数重启不会中断任何工作。

只读 RPC 等待(agent.wait、审批决策等待、question.waitAnswer 和 device.scopes.waitUpgrade)会在其客户端断开连接时停止观察。当关闭排空开始时,已连接的等待者会收到带有原因 gateway-restarting 的可重试 UNAVAILABLE 错误,因此客户端可以重新连接并再次等待。这些等待不会消耗停止排空预算。底层运行、决策和已受理写入保持其正常的排空和恢复行为。一旦正在运行的 Gateway 包含此修复,这也适用于更新重启;安装新文件无法改变已由较旧 Gateway 进程持有的等待。

Cron 关闭为执行清理和持久化结果写入提供相同的清理窗口。完成作业的执行本身并不会结束排空:结果持久化也必须完成,否则 Gateway 会在窗口到期时报告剩余工作。退出监视器故障会在这些排空完成后报告。

在 Linux 和 macOS 上,当启动从不支持的 Node 版本中恢复且服务管理器跟踪 launcher 父进程时,这也适用。launcher 会转发停止信号,并等待正在服务的 Gateway 在共享服务预算内完成排空。托管重启意图针对当前活跃的服务所有者,因此未完成的工作在其排空预算到期时仍会遵循重启恢复流程。当 launchd 驱动停止时,macOS 使用运行中作业自身的 ExitTimeOut,如果路径中存在 launcher,则以 launcher 自身的停止计时器为上限;Linux unit 使用它们自己的停止超时;两者都在下面的截止时间部分中描述。这要求 Gateway 由更新后的 launcher 启动:替换文件无法改变已经在运行的 launcher。下面的停止截止时间是例外:正在服务的 Gateway 会自行推导 launcher 的回收计时器,而不是被告知该计时器,这正是为了确保由已运行的旧 launcher 启动的 Gateway 仍能正确约束自身。

对于这些托管重启,如果 CLI 无法验证服务命令、服务所有者或重启意图记录,它会在发出信号之前拒绝重启,并返回 GATEWAY_RESTART_PREPARATION_REFUSED。恢复服务检查或状态访问,验证 Gateway 状态,然后重试。未经验证的 launcher PID 永远不会作为回退方案。

正在运行的 2026.9.4 Gateway 通过其已发布的 state-local 锁标识保持资格。CLI 在准备重启之前会验证服务安装、活动进程启动标识以及该原生服务中的成员资格。过期或不匹配的标识无法授权运行中服务的重启。一旦先前所有者被证实已终止,非活动服务仍可在没有重启意图的情况下启动。

在 Linux 上,systemd unit 必须使用 KillMode=mixed,以便初始停止信号仅到达 Gateway。当 Gateway 退出或其停止截止时间到期时,systemd 仍会终止剩余的子进程。较旧的 KillMode=control-group unit 会立即向子运行时发送信号,这可能会在排空完成之前中断回合。只要其 Gateway 连接仍然存在,spawn broker 就保持可用,即使它也收到停止信号,因此清理操作仍然可以启动命令并观察子进程退出。这并不能保护其他子运行时;仍然需要 KillMode=mixed。

更新和 openclaw doctor --fix 会刷新过时的 OpenClaw 管理的 Linux unit 策略。维护操作会从 Gateway 状态读取常驻关闭预算。没有该状态信息的旧 Gateway 会遵循短预算路径:栅栏准入并观察生命周期排空,直到空闲或达到更新步骤截止时间。到达截止时间时,未完成的写入持有权会连同其所有者阶段一起拒绝停止;剩余回合可能会被中断,并记录一条警告。操作员拥有的 drop-in 必须单独检查和更新,因为重新安装基础 unit 会保留它们。参见 Linux 服务。

维护托管观察

Gateway status 报告其进程拥有的 shutdownBudget,包含 activeWork 计数和独立的 writeCustody 数组。挂起准备和状态响应也包含可选的 writeCustody 条目,带有 phase 和 count。当前阶段标识迁移、备份、协调器写入、会话生命周期变更和终端持久化。这些由其操作所有者记录;普通 root 请求和 cron 运行不意味着写托管。计数可能重叠。

该可选字段是附加的。较旧的 Gateway(包括已发布的 2026.9.5)可以省略它。缺少托管信息绝不会拒绝维护。如果更新步骤截止时间到期,维护会停止,并带有警告,其中包含最新的 root 请求和 cron 运行计数,解释常驻项缺失的区分,并指明下一个 Gateway 的刷新停止策略。只有已报告的实时写托管阶段才会拒绝该截止停止。

Systemd 停止截止时间

在启动时以及接受关闭时,Gateway 会读取其正在运行的 systemd 单元的有效 TimeoutStopUSec,包括 drop-in。它会在两个时点记录来源和协调后的停止预算,因此修复后的单元无需先重启即可生效。检查以及等待启动完成都会消耗同一个关闭截止时间。活动工作排空最多使用 315 秒,其中最多预留 10 秒用于最终聊天写入和服务器清理,并在 systemd 截止时间之前最多再预留 5 秒。因此,具有默认 90 秒停止超时的单元将获得 75 秒排空和 85 秒 Gateway 关闭截止时间。更短的 supervisor 超时也会限制请求的重启等待。

当截止时间无法支撑这两项额度时,它们都会受到限制,采用 launchd 预算所使用的相同规则,原因也相同:从一个较短的停止超时中减去两个按 315 秒排空尺寸设定的值,会将其完全耗尽,使活动工作没有任何额度。其 TimeoutStopSec 为 20 秒或更长的单元,保留其已有分配,再减去停止检查本身所消耗的时间,而不是精确到毫秒:20 秒只是能够支撑完整 10 秒预留以及 5 秒排空(在该成本扣除之前)的最小值。低于该值时,截止时间无法同时支撑两者,预留让位以保留排空:15 秒及以下单元现在至少会进行排空,而此前排空为零毫秒,介于两者之间的四个值以部分预留换取此前不足 5 秒的排空。预留永远不会低于关闭预算的一半。退出余量在 20 秒截止时间以下保持完整 5 秒,低于该值时缩小,在 15 秒时为 3.75 秒,更短则为四分之一。排空的工作、顺序和中断行为保持不变,systemd 自身的 90 秒默认值不受影响。

服务子进程清理使用剩余的 Gateway 关闭预算,为最终退出簿记留出时间。强制重启会在同一预算内排空已接纳的工作。当重启调度器已耗尽其延迟预算时,清理会保留 10 秒预留,而不会启动第二次排空。对于 supervisor 的 SIGTERM 重启,更短的请求排空限制的是活动运行被中断的时间,而不是数据库清理必须完成的时间:清理可以使用剩余的原生停止预算。Gateway 仍会在 supervisor 截止时间之前退出。干净的数据库重启证明仅在写者租约、检查点和原生连接关闭稳定后才发布。没有 supervisor 交接的重启使用现有关闭截止时间进行清理。这包括位于另一个服务 cgroup 内的前台 Gateway、带有 OPENCLAW_NO_RESPAWN=1 的重启,以及必须启动自身替代进程的独立更新。仅 cgroup 成员身份并不能提供会替换 Gateway 的 supervisor。普通取消在强制终止前保留其 5 秒宽限期。在关闭期间,中继在其拥有的进程确认消失后需要强制终止时会产生警告。已完成的清理使 Gateway 的退出状态保持为零;未确认的进程清理边界仍会报告失败。

进程的 cgroup 会选择系统或用户管理器,独立于运行 Gateway 的账户或其重启所有者。这也涵盖带有 User=openclaw 的手写系统单元以及外部管理的部署。读取系统单元的超时不需要 sudo 或通知支持。

如果启动时无法检查单元,Gateway 会警告管理器、单元和失败原因,并使用 systemd 的 90 秒默认值作为保守回退。关闭时重新读取失败会保留启动预算,并扣除已流逝时间,而不是假设更长的超时。显式无限超时会保持正常的 Gateway 预算。已在运行的 v2026.9.5 Gateway 在重启前保留其启动读取值;安装较新文件无法更改该旧进程捕获的关闭预算。

已安装的旧单元一旦新 Gateway 启动即可受益于钳制,无需重写服务。这为有序关闭留出时间,而不是将整个停止窗口都用于排空。无法稳定的工作仍使用现有中断和恢复路径;更短的预算无法保证任意清理完成。CLI 安装和引导式 Doctor 服务修复会根据与 Gateway 相同的策略渲染 TimeoutStopSec=330。Doctor 会报告低于该要求的有效停止超时。操作员拥有的 drop-in 仍由操作员负责。

对于手写系统单元,至少允许 排空 + 15 秒。按当前最大排空,创建 /etc/systemd/system/openclaw-gateway.service.d/stop-timeout.conf:

[Service]
TimeoutStopSec=330

运行 sudo systemctl daemon-reload,并使用 systemctl show openclaw-gateway.service -p TimeoutStopUSec 验证。通过你服务的部署所有者重启。对于用户 单元,使用 systemctl --user edit openclaw-gateway.service 以及相应的 --user reload/show 命令。保留上述描述的 KillMode=mixed;更长的 超时并不能保护子进程免受 KillMode=control-group 初始信号的影响。

在排空期间,对待处理节点命令的回复仍会被接受,包括由关闭启动的工作进程清理。每个回复仍必须与其活动调用、节点连接、配对代次和所属生命周期匹配。这使得清理可以在不等待命令超时的情况下完成。它不会重新开放对新请求的准入。

操作员也可以在 Gateway 排空期间检查并回答待处理问题,或解决审批。这些请求必须属于在关闭前已准入且仍待处理的工作;正常授权检查仍然适用。新的问题和审批请求仍保持被隔离。

只有无法在排空预算内完成的工作(或因强制重启或崩溃而中断的任何运行)会被中止——并且在此之前,每个受影响的会话都会被标记为待恢复。

当批量关闭标记无法写入时,重启取消也会保持可恢复性。由同一 Gateway 重启中断的原生运行时准备会被记录为重启取消,而不是提供者失败。显式用户取消和真正的执行超时仍然是终态。恢复启动使用已准入运行的现有截止时间,包括运行时准备以及等待会话或全局容量。在健康队列中等待不会消耗单独的启动失败尝试。

sessions.abort 在确认成功之前会等待取消的会话写入。即使运行的终结器尚未完成,在该确认后立即重启也会保留终态结果。这也适用于在等待派生任务时让出的父任务:成功停止其子任务会在确认之前记录已捕获父任务的取消,而不会覆盖该会话中更新的轮次。如果另一个子任务无法停止,响应仍会报告取消不完整;已捕获父任务的取消会先于该错误持久化。

Launchd 停止截止时间

在由 launchd 驱动的停止中,macOS Gateway 会使用 launchctl print 读取已加载作业的有效 exit timeout。它会检查系统、GUI 和用户域,并且只接受 PID 为 Gateway 或其启动器的作业。重启所有权仍可能是外部的:强制执行停止的监视器,而不是下一次启动的所有者,决定截止时间。直接发送 SIGTERM 不会启动 launchd 的停止计时器,因此它会保持现有的 Gateway 停止策略,除非 OpenClaw 启动器独立执行其自身的子进程回收计时器。

停止预算 是监视器能够终止进程之前可用的时间。Gateway 会预留一个退出余量(最多 5 秒),并在剩余时间内规划其关闭。排空 是该关闭的第一部分:停止准入新工作,并等待活动轮次和后台任务稳定。其余部分预留给最终写入和服务清理(最多 10 秒)。Gateway 会记录所选来源、排空、关闭截止时间、预留和退出余量。

对于已安装的 20 秒 LaunchAgent 作业,名义分配为 5 秒排空、10 秒清理,以及 launchd 截止时间前的 5 秒。用于检查作业的时间会被扣除。对于更短的自定义截止时间,退出余量最多为四分之一,并且清理预留会让步,以便为活动工作保留最多 5 秒的排空(当剩余关闭预算非常短时,最多为剩余关闭预算的一半)。例如,5 秒作业在扣除其 1.25 秒退出余量后,名义上分别为排空和清理各保留 1.875 秒;15 秒作业保留 5 秒排空、6.25 秒清理和 3.75 秒余量。这会减少 20 秒以下自定义作业的清理时间。 默认 systemd 90 秒截止时间不受影响。探测最多可消耗三次 2 秒调用;非常缓慢的检查可能导致没有排空时间。

如果 Node 恢复或编译缓存启动器是该作业的 PID,其自身的子进程回收计时器可能短于 launchd 的截止时间。只有当其重启标记能够证明该启动器启动了它时,Gateway 才会将其预算限制为该计时器;无关的父进程不会缩短预算。

向已标记启动器发送直接信号可能会启动其自身的计时器,而此时 launchd 仍报告 running。Gateway 无法将该转发信号与直接发送到其子进程的信号区分开来,在后者中启动器没有计时器,因此它不会限制这种情况。直接停止启动器的操作员应改用已加载作业的停止,以获得可执行且可报告的截止时间。

ExitTimeOut=0 表示没有 launchd 停止截止时间,而不是值缺失。在没有启动器计时器的情况下,Gateway 会保持其常规策略,并且不会将该作业归类为原生截止时间。不可读取的作业会保留现有停止策略并发出警告,而一个已确认正在停止但超时缺失的作业会使用 launchd 的 20 秒默认值并发出警告。

该值来自 launchd 已加载的作业,而不是磁盘上的 plist。编辑 ExitTimeOut 或运行 launchctl kickstart -k 不会重新加载它;先运行 launchctl bootout 再运行 launchctl bootstrap 会重新加载它,并重启 Gateway。每次停止时读取可避免过期的启动快照,而不是过期的已加载作业。在 macOS 27 上,即使 plist 要求更长,launchd 也最多报告 60 秒;请检查已加载作业以确认有效值。

主机睡眠与进程冻结

当 Gateway 主机从睡眠中唤醒、虚拟机恢复,或进程在长时间暂停后继续时,Gateway 会在大约 30 秒内检测到冻结。一旦受跟踪的 Gateway 工作处于空闲状态,它会重启通道连接,然后刷新缓存的健康状态和存在状态。当繁忙的 Gateway 仅延迟通道重启时,健康状态和存在状态刷新仍会运行。当繁忙的事件循环导致计时器间隙时,这可以避免过期套接字等待其正常过期,同时不中断活动回复或代理启动。

macOS 应用和 Linux 伴侣通过在主机关机前准备一个短暂挂起租约,并在唤醒后恢复它,与本地 Gateway 协作。当应用主机睡眠时,远程 Gateway 不会被挂起。通过 gateway.suspend.* 进行的有意挂起会保持恢复延迟,直到控制器恢复 Gateway。

更新失败后的恢复

在交互式更新或修复失败后,OpenClaw 会完成清理和任何服务恢复,然后打开 openclaw triage。Triage 会立即按以下顺序启动第一个可直接启动的编码代理:Claude Code、Codex、OpenCode,然后是 Pi。它会在执行新的 Doctor 检查或归档收集之前传递已捕获的失败信息,并要求代理诊断、修复并验证安装。代理会收到已捕获的安装路径,并保留其正常的身份验证、沙箱和审批设置。

对于失败的 Control UI 或无人值守更新,请使用 Gateway 主机上打印的特定于安装的命令,或者在该主机上使用相同的 OpenClaw 配置文件以及状态/配置路径运行 triage。使用 --agent 可选择特定的编码代理:

openclaw triage
openclaw triage --agent codex

JSON、--yes 和非交互式更新调用会收集诊断信息,而不会启动外部编码代理。openclaw triage --non-interactive 也会在不启动代理的情况下准备诊断信息。--update-result <path> 会包含更新器保存的失败工件。打印的交接命令会保留安装选择器,并在 Windows 上使用 PowerShell,在 macOS、Linux 和 WSL 上使用 POSIX shell。

暂存和验证在旧 Gateway 继续提供服务时运行。候选版本会运行 Doctor lint、配置和插件规划,以及针对已复制的配置和已验证数据库快照的隔离金丝雀启动。在这些副本上执行的迁移会预演升级,而不会更改实时状态。验证失败可以在旧 Gateway 继续提供服务时进入有界的无人值守修复。激活要求失败的检查通过。否则候选版本会被丢弃。already-current 空操作永远不会停止 Gateway。 早于迁移续接的旧目标会将运行时验证记录为不可用,并使用现有的降级完成路径。 分离的辅助进程也会等待 activating 阶段,然后再将其父 Gateway 挂起。第一个激活窗口包含切换、必需的实时迁移和服务启动。插件包下载和同步在核心 Gateway 提供服务时运行。已更改的插件快照需要第二个可测量的激活窗口:在独占维护下执行完整的 Doctor 迁移,然后重启并验证。未更改的插件不会再次运行完整的 Doctor 检查。

激活后,更新器会验证受管服务正在运行并拥有其端口、Gateway hello 握手与预期的版本/构建标识匹配、12 次探测的健康稳定通过、插件和通道正常,并且 /readyz 返回 HTTP 200。更新验证不使用模型推理。 启动会获得更新现有的每步 --timeout 预算(默认 1800 秒),包括迁移和监听器初始化,随后是 12 次探测的稳定窗口。在从旧版本进行的第一次更新中,旧更新器会调用新安装的 CLI,但不会传递该就绪预算。候选版本会识别现有的更新标记,并在受管进程运行后,使用五分钟启动看门狗,而不是独立的 60 秒截止时间。迁移、监听器和健康状态转换不会重置此限制。旧更新器的子进程超时仍然有效。等待耗尽时会报告最后观察到的启动阶段。独立重启使用进度门控的就绪等待。 验证事实和测量的停机时间会保留在更新运行报告中。

当包验证失败时,更新器会将共享的以及受影响的按代理 SQLite user_version 值以及配置内容与激活前的值进行比较。如果它们未发生变化,并且之前的运行时在激活前已验证,它会恢复之前的包、命令垫片、服务定义和配置写入器戳,然后启动该运行时并重复 CLI 验证检查。成功恢复会使该 Gateway 保持运行,并以 rolled-back 结束,失败的检查保留为原因,停机时间覆盖从服务停止到已验证恢复的整个过程。写入器戳保护不会阻止这种有意恢复。其允许范围仅限于回滚服务命令,并且永远不会持久化。参见 自动回滚。 如果配置内容或架构版本已更改,自动回滚会被拒绝(state-migrated-no-rollback),更新器会尝试对已安装的候选版本执行有界修复。当回滚本身失败时,也可以运行相同的修复槽。 代码回滚无法逆转状态迁移。不可用的架构比较也会阻止自动回滚(rollback-state-unverified)。迁移后, 新的候选进程会完成验证和相同的持久运行报告。 旧更新器不会重新打开较新的数据库。在实时迁移之前的 Git 激活失败可以恢复 之前的源和保留的已构建运行时。之后的 Git 失败会保留候选版本以供诊断。

更新失败本身不会授权重启候选版本。候选版本激活仍然需要验证成功。阻塞性的实时 Doctor 结果 不会成为重启授权。之前的运行时在更新前已验证,因此跨未更改的配置和架构的回滚可以在该先前验证下重启它,并且之后必须再次验证它。分离的 辅助进程或 Windows 任务自动启动不能绕过此决定。

在激活后修复期间,编排器会在每一轮之后启动或重启一次已停止或不健康的服务,然后重新运行验证。检查通过即可使运行成功。如果回滚已恢复之前的版本, 成功修复会以 rolled-back 结束,并且命令仍以非零退出。 否则,它会以原始原因和修复摘要失败。代理不能发出服务生命周期命令。

在 Windows 上,已捕获的计划任务自启动会保持挂起,直至 Doctor 完成。更新器会启用该任务以进行激活,并在最终验证失败时恢复挂起状态,包括在迁移状态交接之后。原生任务控制失败会显示在更新报告中。挂起失败绝不会触发对被拒绝安装的自动重新启用。

在 macOS 上,已终止的更新辅助程序可能使所选的 Gateway LaunchAgent 保持已安装但未加载且跨登录禁用的状态。openclaw doctor 和 openclaw doctor --fix 会诊断此状态。在独立修复成功后,--fix 会启动并验证一个已停止的受管 Gateway,其服务指向当前安装。更新期间的 Doctor 将激活留给更新器。如果更新被中断或安装安全性不确定,请重新运行 openclaw update,或在使用 Doctor 进行分诊后再手动启动它。验证完成后,运行 openclaw gateway start(或 openclaw --profile <profile> gateway start)以重新启用并启动该服务。保持相同的 state/config 和自定义标签覆盖。Doctor 会打印所选标签和恢复命令。交互式 Doctor 可以提供引导修复。

在包变更之前取消,可以在其现有交接所有权下恢复原始服务。只有当 Gateway 通过常规重启健康检查,并报告经过验证的安装版本,以及对于 Git 恢复,报告精确恢复的构建 ID 后,恢复才会成功。仅匹配的包版本无法区分两个 Git 构建。服务管理器接受启动请求或报告一个存活 PID 是不够的。一旦分离的辅助程序启动更新器,缺失、格式错误、过大或被中断的直接结果会将激活留给操作员。这比旧版辅助程序在无法分类的失败后重启更为严格。安装新目标不会改变已在运行的历史辅助程序。这些检查适用于启动更新的辅助程序版本。

激活前跳过的更新不会停放或重启 Gateway。如果中断发生在停放之后,辅助程序会使用子进程的已验证恢复决定,并保留原始原因。仅当所需恢复成功或子进程已验证时,零退出码才会被保留。

更新器退出码 79 仅在无法安全恢复并验证上一代时保持 Gateway 停放。当更新器已在未更改的配置和架构上恢复上一代,并提供已验证的恢复决定时,辅助程序会启动并验证它,而不是让它保持停止。辅助程序恢复会验证服务存活状态、版本/构建身份、插件激活和通道健康。它不会重复单独的 /readyz 探测。该报告字段保持未验证。随后运行以 rolled-back 结束,并带有上一版本和测量的停机时间。缺失的恢复证明、迁移状态或失败的恢复仍要求在重启前修复。观察到已停止的服务会被记录为已停止。报告不会复用其激活前的运行状态。

一个最终失败更新在后续服务恢复或分诊修复成功时仍会以非零退出。错误和跳过通知会在恢复前尝试发送。在恢复中的 Gateway 消费它们之后,辅助程序不会重新创建它们。检查最终 CLI 结果和交接日志以了解恢复结果。

在重启前修复失败的 Doctor 或安装检查。分诊可以检查 openclaw gateway status --deep 和更新诊断。避免在新版本发布已迁移配置或数据库后盲目安装旧代码。参见 更新和恢复。重启哨兵会报告结果。复制其中一个并不会授予重启服务的权限。

如何检测中断的工作

启动会协调仍显示为 running 但没有存活运行、任务、准入或恢复所有者的旧子代理会话行。它在一个事务中记录诊断转录回执,并将该行标记为 interrupted。结束时间戳记录启动观察到中断的时间,而不是推断的执行完成时间;原始活动时间戳保持完整。回执写入失败会变成警告,并使该行仍可在后续修复。

三种互补机制会标记回合未完成的会话:

  • 在回合准入时: 对于现有主会话上的普通文本回合,网关会在模型或 before_agent_reply 钩子执行之前,在一个 SQLite 事务中追加用户消息、将会话标记为运行,并记录其恢复投递声明。控制 UI 在返回 started 确认之前执行此操作。通道分发在准备好的回合采用代理运行时执行此操作。命令、附件、每回合覆盖、待处理投递、先前中止提示、插件拥有的会话以及带有执行钩子的回合保留其专用准入路径。如果安装了 before_agent_reply 钩子,准入会记录足够的阶段状态,以区分已完成的静默结果和模糊的副作用窗口。恢复会分发一个普通用户触发的代理回合,因此当前加载的 before_agent_reply 钩子会在其正常触发规则下运行。先前模糊的钩子结果会使用重启安全工具恢复,而不是重放不受限制的副作用。
  • 在关闭时: 在重启排空期间,每个具有活跃运行的会话都会在运行中止前,在会话存储中被盖上恢复标记。
  • 在启动时: 网关会扫描会话存储,查找仍声称正在运行但在新进程中没有任何存活所有者的会话。这会捕获没有运行关闭代码的硬崩溃和强制终止。过期的转录锁文件也会同时清理。

存储扫描失败会使该存储仍符合计划重试条件,而其他存储继续恢复。openclaw status 和 openclaw doctor 会显示正在运行的 Gateway 中未完成的启动恢复失败;当存储扫描成功时,警告会清除。

如果旧版 Gateway 在运行中、失败或无状态的会话中遗留了一个失效的写入器和未完成的恢复周期,sessions.recover 会协调该写入器,并在同一会话中启动续接。新的 Control UI 消息也会在准入前协调此状态,因此被拒绝的发送不会将对话困在"对话已更改"的重试循环中。两条路径都保留会话键和对话记录。活动的运行实例或云工作进程仍会阻止此修复。已墓碑化的会话保留其独立的恢复路径以进入新会话。

如果恢复在 Agent 启动前的准备阶段失败,Gateway 会还原被中断的状态,并释放该次尝试的投递声明。下一次恢复尝试使用全新的运行 ID,同时保留原始的被中断轮次和重试预算,因此被拒绝的待处理输入不会让对话永久处于繁忙状态。

自动恢复

启动几秒后,Gateway 会重新派发每个已标记的会话,并附带一条合成系统消息,告知 Agent 其上一个轮次因重启而中断,需要从现有对话记录继续。如果最终回复已生成但尚未投递,该回复文本也会包含在内,以便 Agent 直接投递而不是重做工作。

重启不会取消用户的任务。Agent 检查当前状态,核实结果未知的工具结果,并继续执行,而无需用户重复请求。准备新消息不会消耗中断标记;恢复持有者会保留该标记,直到工作被接管或结算。

恢复会在开始另一次运行前读取被中断轮次的来源,即使已有最终回复待处理。如果对话记录无法读取,已保存的回复和任何已接受的完成声明仍可供后续尝试使用。委托请求和未经验证的内部输入不能在没有存续授权的情况下自动恢复。缺失或无效的来源信息不会为内部声明确立人工发送者。传统渠道和 Control UI 轮次保留其现有的恢复检查。子完成后续操作仍使用其现有的恢复和投递所有权检查。

当恢复的轮次具备合格的渠道投递路由时,OpenClaw 会向该对话发送恢复通知,并保留其账户和主题。最终回复使用相同的投递路由。仅对话记录的轮次保持私密,已完成的轮次不会收到迟到的恢复通知。通知发送失败不会重启或重放已恢复的工作。主会话恢复通知是尽力而为且仅实时生效的:在渠道发送前会立即重新检查自动投递权限和恢复持有者。它们不会从出站队列重放;终态失败通知的重试以及正常的最终回复投递均保持不变。

Telegram 会在恢复的轮次运行期间续期其打字指示器。当轮次结算、其恢复持有者变更或 Gateway 关闭时,打字指示停止,并且遵循 typingMode: "never"。其他渠道可以通过受保护的打字钩子选择启用;不受支持的渠道仍会收到恢复通知。

渠道插件通过 heartbeat.sendTypingGuarded(...) 选择启用。核心在恢复投递目标之外,还提供了一个 AbortSignal 和一个 assertPlatformSendAuthorized 回调。插件必须通过排队发送响应取消操作,并在异步准备之后、平台请求之前立即调用该回调。恢复不会回退到无保护的 heartbeat.sendTyping(...) 钩子。

启动协调对瞬时失败最多重试三次,并采用指数退避。另外,每个被中断的主会话周期都拥有一个持久预算,包含三次计入的自动派发尝试,该预算在 Gateway 重启后仍然保留。OpenClaw 在派发前计入一次尝试;当 Gateway 在接受前明确拒绝请求时退还该次计入;当派发后结果不确定时保留该次计入,以避免重放工作。已拥有会话的前台任务会阻止自动恢复,直到该任务结算为止。

持久预算耗尽后,会话会被墓碑化,而不是永远循环。检查失败的会话,并使用 /new 或 /reset 启动替代会话。openclaw doctor --fix 可以修复与墓碑冲突的过期中止标志,但不会重新启用该恢复周期。

如果你在渠道中再次向失败的会话发送消息,OpenClaw 会通过该渠道发送一条简短的恢复提醒,并以 warn 级别记录每条被拒绝的消息,包含会话键、恢复原因和恢复命令。重复提醒会在有界内存缓存中被抑制。重置或删除会话,或重启 Gateway,会清除该抑制。模型选择被锁定的会话则会引导你在 WebChat 中使用 在新会话中恢复。

每次重试都复用同一个持久派发标识符,因此模糊的连接失败不会两次启动相同的恢复。已完成的 Control UI 轮次也保留有界的持久幂等墓碑,允许重新连接的发件箱在不重新执行请求的情况下将其注销。

仅消息工具的回复使用第二个持久关联。在终态的同会话发送到达渠道之前,Gateway 会在确切的会话和来源轮次上记录一个未解决的投递意图。确认的提供商成功将其转化为持久的已投递回执。确认的失败将其清除。恢复会在不重新运行工具的情况下完成已投递回执。如果崩溃导致提供商结果未知,恢复会使用重启安全的工具继续,以便模型能够检查并报告这种模糊性,而不重放外部效果。

已投递的回复也会连同其来源消息 ID 镜像到对话记录中。终态镜像使用不同的回执键,因此带有相同提供商幂等键的进度发送无法掩盖终态标记。来自较旧轮次的进度发送和回执无法完成当前轮次。只有持久的渠道入口声明才能恢复消息操作权限。恢复的运行保留原始的来源投递模式和来源关联,包括请求者身份以及任何同渠道/线程限制,因此即使在恢复期间再次发生重启,同一回执仍然具有权威性。不具备可重建渠道权限的仅消息工具轮次会被墓碑化,因为 OpenClaw 在没有原始渠道入口声明的情况下无法安全地生成消息操作权限。终态通知会引导用户使用 /new 或 /reset 启动替代会话。

已恢复的 Control UI 回合可以使用被中断回合的确切会话和恢复声明完成固定仪表板组件。浏览器连接无法在重启后存活:恢复会保留仪表板创建,而内联和设备展示仍需要其正常客户端能力。

在恢复之前,网关会对转录尾部进行分类,以为继续选择工具限制。中止的回合本身就是中断,因此它会基于尽力而为的原则恢复,无论提供商或工作进程与其记录了何种中止细节:部分流式文本保留在转录中,继续从该文本下方的消息开始,而悬空的工具调用会从下一个提供商负载中丢弃。提供商故障、已完成的助手尾部、空转录和过期的待批准项也继续从现有转录开始。具有模糊副作用的状态通常使用重启安全工具。具有有效 Full Access 的会话(包括继承的 Full Access 默认值)会保留其常规工具,以便检查结果并完成任务。恢复不会自动重放被中断的调用,也不会将其缺失的结果视为成功。现有工具限制和当前权限仍然适用。待回复投递、模糊的回复钩子结果以及明确可重放安全的 Code Mode 重建会保留其更窄的恢复限制。

OpenClaw 还可以重建被中断的只读 Code Mode 工作。Code Mode 将这些运行标记为重启安全,并在执行前拒绝具有副作用的目录或命名空间工具调用。如果重启落在 wait 控制上,新网关会根据其转录重建该回合,并强制重建的执行保持重启安全,即使模型省略或清除该标志。宿主将重建的整个回合过滤为经过审计的只读核心工具和明确可重放安全的插件工具,包括重启后 Code Mode 被禁用的情况。其他被中断的 Code Mode 工作恢复以进行模型协调:Full Access 保留其配置的工具表面,除非当前回合具有明确可重放安全的检查点。其他会话保留重启安全限制。旧的进程本地运行和审批句柄不会被重新激活。

子代理

子代理运行持久化在共享 SQLite 状态数据库中,因此子代理注册表在进程重启后仍然存在。启动时,被中断的子运行通过其正常完成路径结算。它们不会被自动重新启动。父级接收中断结果,并负责完成用户的任务。其恢复输入列出当前未完成的子会话和运行标识,包括被重启中断的子项。被较新的子运行取代的旧运行会被省略,来自其他存储、父会话或父生命周期修订的记录也是如此。重置会保留会话 ID,但会更改其生命周期修订,因此该重置之前保留的子工作无法进入新父级的可操作恢复清单。大型列表显示前 32 个子项,并提示父级检查其余子项。

父级必须将每个未完成的子项与其保存的历史记录和原始请求进行协调。如果子项的任务仍然需要,它应优先使用 sessions_send 在该保留会话中进行后续操作。它首先确认旧执行已停止,并检查任何不确定的工具效果。它可以利用已完成的工作、分配替代项,或自行完成剩余工作。恢复不会自动重放子命令或重复正在运行的工作。仅中断本身不是阻塞项;父级会继续,直到请求完成,或特定阻塞项需要用户输入或不可用的权限。现有清理和保留设置仍然适用。

如果父级在等待子项时让出,其保存的批次会收集已完成和被中断的结果,并在批次结算后唤醒父级。已经处理这些结果的父级通过普通主会话恢复继续。子项结果或 announce: 运行标识不会使未完成的父级工作可丢弃。两种恢复路径都会向父级提供被中断子项的标识和相同的协调指导。重启中断在历史中保留为中断结果,而不是子执行失败。真正的执行和投递失败仍需关注。

保存的批次唤醒保留其原始批次标识和完成投递契约。其可操作恢复清单仅包括捕获的父级所有权仍然匹配的子项。较旧或过期的记录仍为普通完成历史;它们不能添加新指令来继续那些子会话。当前重置已撤销其存活的保存唤醒。记录的结果和历史子元数据会被保留。

升级时,保留类型化重启恢复所有者的保存中断通过相同的启动路径进行协调。没有该所有权证据的历史失败运行仍保持失败:仅其错误文本无法区分重启和真正的失败。在继续这些保留会话之前,请检查它们;恢复不会重写模糊的历史。

已完成的子项可能仍欠其请求者一个最终后续。如果该后续正在等待重试或被重启中断,保存的义务会存活并在启动后恢复。重启准入拒绝不会消耗一次尝试,取消已准入的尝试也不会耗尽该义务。现有投递重试限制仍然适用。结算让出回合的唤醒会保留其未完成的原生运行和最终投递。已完成的取消会将其唤醒和清理簿记保留在原生子代理记录中;它不需要单独的 Tasks 行。恢复在重试其请求者唤醒之前,会协调过期取消的保留标记,保留原始清理记录。存活的子项取消可以在正常清理协调完成时唤醒等待的请求者。延迟的取消回调不能重新打开已完成的清理。

Agent 请求的重启

当 Agent 自身触发重启(应用配置变更、更新 Gateway 或显式重启请求)时,会在进程退出前将重启哨兵写入 SQLite。启动后,Gateway 会将结果回发到来源聊天,并分发任何请求的一次性续接回合,使 Agent 恰好在同一频道和线程中从它中断的地方继续。

对于更新,哨兵携带 stats.runId,将分离的更新器链接到其持久化的 update_runs 记录。新的 Gateway 会在那里记录其观察到的运行版本、构建和启动事实。它会保留更新器已写入的终态结果,并在受管交接仍待处理时等待。在准备通知或续接之前,它会协调同一运行和交接的较新最终哨兵。即使账本已处于终态,待处理哨兵仍保留其现有的有界重试窗口;无关的替换保持不变。如果辅助程序从未发布其最终哨兵,过期处理会报告已记录的终态结果而不更改它。仍在运行的 Gateway 拥有的行会以 restart-unhealthy 失败结束;CLI 拥有的运行保留其更新器的权限和结果。 重启后的通知从该行渲染,使用与 openclaw update status 相同的报告。消费哨兵不会删除运行历史。旧版本遗留的哨兵保留其现有投递路由。

任何具有现有内部来源会话的更新运行,包括 Control UI 和 webchat,都会将其报告直接追加到该会话的转录中,即使调用方只提供了 sessionKey 而没有 deliveryContext。没有续接的已完成更新不会唤醒模型来投递报告。

哨兵的带类型 SQLite 列是重启处理的权威来源。其 payload_json 值只是重放/调试影子。运行时读取、写入和清除 SQLite 状态,没有文件回退。Doctor 和重启恢复共享 restart-sentinel.json 的有界导入器。重启恢复仅在就绪后导入更新通知;无关的旧版修复仍需要 Doctor。

2026.6.1 RPC 更新器在候选 Doctor 完成后写入其通知。其受管更新器可以在重启健康检查成功后发布最终结果。恢复通过其现有重试窗口检查同一待处理交接,并保留其投递路由和续接。最终旧版结果只能替换其自身导入的待处理通知;较新的规范状态优先。当源文件重新出现时,不会重放已记录的源代次。不完整的通知保留在磁盘上,用于恢复或显式 Doctor 修复。2026.6.34 和 2026.9.2 更新器改为写入原生 SQLite 状态。对于六月之前的安装,请使用桥接升级程序。

安全阀与可观测性

  • 崩溃循环断路器: 5 分钟内 3 次非正常启动会触发断路器,该断路器会在下次启动时抑制自动启动的侧服务,因此崩溃的 Gateway 不会自我放大。持续稳定的安全模式 Gateway 会在完整的非正常启动窗口耗尽后重新检查断路器,然后恢复延迟的频道自动启动,而无需再次重启 Gateway。

当断路器触发时,控制平面仍会启动,但频道插件(以及其他自动启动的侧服务)保持关闭,直到操作员手动覆盖抑制,或完整窗口耗尽且没有非正常启动。恢复会保留操作员手动停止的频道以及任何单独的开发模式抑制。Gateway 日志类似: channel autostart suppressed by crash-loop breaker; refusing automatic start for <channel>… Start a channel manually with: openclaw gateway call channels.start --params '{"channel":"<id>"}'

操作员恢复 SOP:

  1. 确认 Gateway 进程正在运行(openclaw gateway status / LaunchAgent 或 systemd 单元仍在运行)。“频道断开连接”症状通常意味着自动启动被抑制,而不是 Gateway 已停止。
  2. 检查频道状态:openclaw channels status(在有用时添加 --probe)。在 Gateway 本身健康时,查找已停止/未连接的账户。
  3. 在强制恢复频道之前,修复非正常启动的根本原因(配置错误、插件启动时崩溃、缺少密钥)。
  4. 在抑制激活期间手动启动频道:

    openclaw gateway call channels.start --params '{"channel":"<id>"}'
    # optional: {"channel":"<id>","accountId":"<account>"}
    

    channels.start 是手动覆盖。它不会禁用其他频道的断路器。

  5. 或者让健康的 Gateway 继续运行,直到完整的非正常启动窗口耗尽。同一进程会记录重启循环断路器已恢复,并启动延迟配置的频道。 如果该消息在窗口加一个健康监控间隔后仍未出现,请检查 Gateway 日志,并在重启前运行 openclaw doctor。

另请参阅 Gateway(安全模式段落),了解相同的控制平面与频道自动启动拆分。

  • 主会话尝试预算: 每个中断周期三次计费的自动分发尝试。耗尽会将该会话标记为墓碑,直到其被检查并替换。
  • 指标: 恢复活动通过 Prometheus 导出为 openclaw_session_recovery_total 和 openclaw_session_recovery_age_seconds。
  • 日志: 恢复决策记录在 main-session-restart-recovery 和 agents/subagent-registry 子系统下。
  • 回复钩子: 恢复的回合在正常用户触发规则下运行当前已加载的 before_agent_reply 钩子。自动投递的回复在频道投递前也会运行正常的 reply_payload_sending 钩子,并带有恢复的会话、运行、账户和对话上下文。

验证更新后的恢复

健康的 Gateway 确认的是可用性,而不是中断工作的完成。 检查每个先前活动的会话及其子任务:它应当在关闭前完成、恢复执行,或达到可见的最终结果或恢复错误。单独检查排队输入,确认其是否已被转录消费或处于显式未解决状态。

主会话恢复日志将执行恢复与后续的 main-session restart recovery terminal 事件区分开来。启动时的恢复计数表示执行已恢复。它并不能证明助手回复已送达。请使用会话转录和记录的送达结果来验证完成。

未恢复的内容

  • 因其他所有者已处理而被排除在主会话恢复之外的会话:子代理会话(已回落到其父会话)、cron 会话(调度器会按计划重新运行)以及 ACP 管理的会话(已连接的 IDE 或客户端拥有恢复权)。
  • 从未被接纳的工作:在排空窗口期间到达的消息会以显式重启错误被拒绝,而不是被静默排队到一个正在终止的进程中。
  • Gateway 终端 PTY,包括操作员和代理拥有的终端。它们是进程本地的,并会在 Gateway 重启时结束。
  • 独立嵌入式回合无法接管具有待处理重启恢复的主会话,因为它们不共享 Gateway 的生命周期所有者。请通过 Gateway 运行该回合,或在那里使用 /new 或 /reset 重置它。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw