# 单 Agent 恢复:本地修复与验证 > Historical validation notes. For the current automatic recovery contract and checks, see [AUTOMATIC_RECOVERY_VALIDATION.md](AUTOMATIC_RECOVERY_VALIDATION.md). > 历史记录:本文件的压缩与测试结果属于 v0.2.7 候选流程。v0.2.8 改为移走本次中断的模型可见消息并直接接续;这条新路径发布前未运行测试,以下结果不能作为它的验证证据。 > v0.2.10 当前规则:每次确认唱歌都可在同一会话恢复,不再按用户消息限制次数,也不要求两次恢复之间先完成工具步骤。只对同一次停止去重。任务正常结束不会触发接续,用户停止或新指令仍会中断恢复。下文“每个用户回合最多一次”属于旧版本。本次修改按用户要求未运行测试。 日期:2026-09-29。状态:本地候选;单元、AgentLoop 与隔离 Web 假模型验证通过。真实服务验证仍待完成。 ## 本轮目标 让同一条工作链完成:**停止唱歌 → 等辅助调用完整压缩 → 把摘要写回原 Session → 原 Agent 接着工作**。辅助调用只生成摘要,不接管科研任务,也不生成工作子 Agent。 ### 2026-09-29:只压缩较旧的辅助记录 一次真实 MA1508 恢复已通过历史 `/permission` 核对,但辅助摘要以 `COMPACTION_MAX_TOKENS` 结束,主 Agent 因而没有接续。检查点约 5.2 万字符,其中必须原样保留的用户要求和操作事实约 2.1 万字符。旧实现让模型概括整份检查点,还允许它写到剩余的约 4.3 万字符,容易耗尽 8,192 token 的输出预算。 现在从完整事件日志核对状态后,只将较旧的工具证据、已完成助手进展及旧待办快照交给辅助模型;最近 4 条工具证据、最近 2 条进展、最新待办及全部必须保留的事实直接交接,不由模型改写。摘要目标最多 3,000 字符。若没有较旧部分,不调用辅助模型。被截断的思考尾段仍不进入任何交接文本;若辅助调用仍以不完整摘要结束,仍暂停,不把半截内容送给主 Agent。 同一真实会话的只读回放得到:完整检查点 55,752 字符,旧证据输入 27,378 字符,最近记录 7,731 字符,必须保留事实约 2.1 万字符。这只是输入结构验证,不是新的真实模型恢复成功。局部测试覆盖旧区段单独压缩、最近区段原样保留、无旧区段时跳过辅助调用,以及真实 AgentLoop 的同 Session 接续。当前 DSH 进程尚未安装或加载这一新改动。 单 Agent 基线为 v0.2.7(`93ad469`)。此前未提交的 multi-agent 实现已单独保存在 `feature/managed-agents-experimental`,提交 `fcec939`;该分支仍是未经验证的实验方案。本轮修复位于 `fix/single-agent-recovery`,未推送。 ## 修复了什么 1. **已经排队的消息不会被当作用户刚刚中断恢复。** 恢复开始时记录队列中消息的标识、内容摘要和顺序,原文仍由宿主管理。压缩完成后按 DSH 队列规则处理这些消息。压缩期间新提交指令会中断恢复;编辑或移除原排队消息会阻止旧交接继续。 2. **写回摘要与真正接续分开显示。** 摘要提交之后显示“等待主 Agent 响应”。只有匹配到本次接续消息的实际 AgentLoop 请求,并收到输出或正常结束,才显示接续完成。辅助压缩调用不能充当这个确认。 3. **等待响应时仍能停止。** 用户停止、接续请求报错、没有输出就达到输出上限、等待响应超时,各自记录原因。超时会停止该接续请求,不额外重试。 4. **紧接着再次唱歌不会被旧恢复状态吞掉。** 新检测事件交给现有恢复规则处理;同一用户回合已经使用恢复机会时,明确暂停并说明原因。 已完成的文件修改保留。压缩输入来自核对过的记录,不包含异常思考链。原 Agent 必须等完整摘要写回且维护操作结束后才开始接续。不能把等待实际响应放在维护锁内,否则会阻止宿主启动接续。 ## 本轮本地证据 用户明确授权了本地假模型测试,替代本轮最初的“不测试”限制。没有调用真实模型。 测试使用实际 DSH AgentLoop 和 loopback 假 HTTP 上游,不只是模拟恢复函数。原有 runtime 测试 20 项通过;本轮新增 8 项: | 场景 | 结果 | | --- | --- | | 两条已排队消息 | 压缩后按顺序处理,消息不丢失,不多加一个旧任务接续回合 | | 接续首块延迟 | 保持等待状态,不提前宣布接续成功 | | 压缩时通过公开 send 接口提交新指令 | 中断旧恢复,新指令处理一次 | | 压缩时编辑排队消息 | 阻止旧摘要交接 | | 等待接续首块时用户停止 | 中断实际请求,不执行任务工具 | | 接续首块超时 | 明确暂停,停止请求,不重试 | | 接续无输出即达到输出上限 | 不误报接续成功 | | 刚接续就再次唱歌 | 检测不丢失,按本回合已用恢复机会暂停 | 最终 `npm test`:**76 项通过,0 项失败**(原有 66 项,加本轮 10 项)。第一次尝试曾因外层沙箱禁止监听 loopback 而失败;取得网络权限后重新执行通过。该失败不是模型或插件行为证据。 复现命令(在仓库根目录、现有依赖已安装的环境中): ```sh node --test test/runtime.test.mjs npm test ``` 测试自行启动假上游,并在结束时关闭服务器和 Agent。此次没有留下测试后台进程。 另补充两个分支测试:用户通过 UI 创建的分支可以在同 Session 恢复;带 `origin: subagent` 的真正委派子 Agent 不递归恢复。此前仅凭 `parentSession` 排除会误伤普通分支,已改为核对明确的来源标记。 ### 隔离 Web 补充验证 通过 `integration/run-web.sh` 打包当前候选,用独立 DSH_HOME、假凭据和 loopback 假上游启动实际 Web profile,再在浏览器提交一次 `SCENARIO_C_LONG`。 结果:同一个 Session 依次完成原始工具请求、唱歌请求中止、一次完整压缩、接续工具请求、最终回答,共 **5 次假模型请求**。异常流关闭在压缩开始之前;压缩无工具权限;压缩和接续输入均不含异常思考尾段,保留了合成约束与已完成证据。工具日志中 `counter` 和 `record` 各执行一次。页面显示 `Task recovery · Main Agent resumed` 和 `Compactions 1`。 该 Web 实验在上述分支识别修复之前完成;源代码哈希保留当时快照,不代表后续修复已经过完整 Web 分支验证。证据见 `integration/evidence/` 的请求统计、工具统计、最终状态和源代码哈希;截图为 `integration/single-agent-web.png`。这些文件只含合成数据。没有创建工作子 Agent。 启动命令:`bash integration/run-web.sh`。在显示的隔离 URL 选择合成工作区并提交: ```text SCENARIO_C_LONG. KEEP_CONSTRAINT_17. Run the synthetic counter once, then record the unfinished outcome. ``` 结束时对启动终端按 Ctrl-C;也可在允许读取进程信息的环境运行 `bash integration/stop-web.sh `。本次外层沙箱拒绝 stop 脚本的 `ps` 核验,因此用启动进程句柄发送 Ctrl-C,启动器清理了子进程;宿主和假上游监听端口均已确认关闭。另有假服务总共 10 请求和外层 600 秒期限,只用于测试进程,不写入正常恢复预算。 ## 保留的边界 ### 2026-09-29 MA1508 现场故障修复候选 实际 MA1508 会话在唱歌确认后停止,但因历史 `/permission` 命令而在压缩前报 `UNSUPPORTED_SESSION_EVENT`。后台子 Agent 随后发回结果,原主 Agent 又被宿主唤醒。本地代码现处理这两个故障: - 已结束的 `/permission` 和 `/plan` 只以命令名、结果及参数哈希进入核验记录。原命令不会重跑,当前权限仍由 DSH 的会话投影决定。历史 `/compact` 继续要求摘要与替换事件完整配对。 - 不认识的命令报 `UNRECONCILED_COMMAND`;缺少完成事件报 `COMMAND_NOT_SETTLED`。这两种情况均保持暂停,不能猜测命令是否完成。 - 已核验的子 Agent 登记作为“状态尚待检查的委派工作”进入摘要,绝不当成已完成工作。后台回报在恢复期间进入 DSH 原有的持久 inbox,但不能唤醒主 Agent。完整交接后由一次接续处理;若恢复失败,持续保持暂停,直到用户明确提交新指令或停止。 - 特定的搜索请求遥测不是工具完成证据;其相应工具结果仍按正常工具记录核对。 只读地重放这次真实会话到熔断点,检查点可构造(387 个事件、1 条已结束命令、2 个已登记子 Agent),压缩输入约 53,186 字符,没有复制异常思考尾段。49 项不需要监听服务的本地检查,以及 2 项使用真实 DSH AgentLoop、内存假模型的恢复验证通过。后者覆盖已结束 `/permission`、已有子 Agent 登记、压缩期间回报、压缩成功后同 Session 接续,以及压缩失败后的持续暂停。当前外层沙箱仍拒绝旧 HTTP 假上游测试在 loopback 监听;真实 DSH 宿主也尚未重启加载这一修复候选,不能把本段称作生产恢复成功。 - 每个明确的用户回合最多自动恢复一次;同一 Session 后续用户回合可再次恢复。本轮没有放开为无限恢复。 - 现有工具、后台任务、附件核对条件仍适用。核对失败或摘要不完整时暂停,不猜测完成情况。 - `resumeTimeoutMs` 默认 5 分钟,分别约束交接提交和等待首次接续响应,不限制主 Agent 后续工作的总时长、模型请求次数或工具次数。 - “已接续”只证明原 Agent 开始了接续生成,不证明用户任务已完成。 - 本轮没有修改 detector、升级依赖或增加服务商专用桥接。 ## 尚未验证与下一步 本地真实 AgentLoop 和隔离 Web 路径通过,**不等于生产恢复已经可靠**。用户随后明确授权安装本地候选、重启 DSH,并在指定 SLURM 会话的分支中沿用原 DeepSeek Flash 路由进行真实验证。此授权本身不是成功证据;安装、运行和实际恢复应分别核验。未修改版本号或推送修复。与 Billion Context 等其他插件的共存尚未验证。 安装阶段已完成:通过 DSH Plugins 的正常安装界面安装本地 tarball,页面报告 `Installed; it loads at the next start`,安装后 7 个关键源码文件均与候选哈希一致。版本仍为 `0.2.7`,这是本地候选覆盖,并不是新发布的 GitHub 标签。已在公开仓库之外保存旧包与配置的回滚副本。 真实验证随后在 SLURM 会话的新分支中提交了一次 `continue`。旧宿主实际上由此前的 Codex 执行进程启动,并非用户自己的 Terminal。新宿主在当前外层沙箱内启动后,Web 页面可用,但 DSH 的 workspace-write 命令因嵌套沙箱无法启用而失败。已拒绝放宽 DSH 沙箱的工具请求并停止该轮;没有观察到完整唱歌恢复,不能计为真实恢复成功。 此次检查还发现普通 UI 分支被误判为委派子 Agent,已修复并通过两项针对性测试。最新本地 tarball 已通过 DSH 插件 CLI 安装,显式沿用系统 pnpm store;关键源码哈希匹配,其他直接依赖声明未变。安装过程中 Web UI 曾因 store 路径冲突失败,后续 CLI 安装成功。版本仍为 0.2.7,未提交或推送 GitHub。 当前执行策略拒绝在外层沙箱之外启动 DSH;电脑操作工具亦禁止操作 Terminal。因此正常宿主启动需要用户在自己的终端完成。下一步先恢复正常宿主,再观察指定分支的一次正常任务;不得把安装完成或本地假模型结果当作生产恢复证据。当前假模型验证已结束。