Add conversation recovery to dsh web GUI: edit last user message to regenerate, auto-retry failed rounds on recoverable errors (up to 5 with exponential backoff), all paths use fork, original session unchanged.
$ dsh plugin --profile web add github:zhu1090093659/dsh-web-ui/packages/dsh-chat-recoveryRun the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial
为 dsh web GUI 加对话恢复能力:编辑最近一条已结束的用户消息让它从原位置之前重新生成;对失败轮次自动按可恢复错误最多 5 次指数退避重试(或给出手动重试按钮)。两条路径都走 fork,原会话始终不被改动。
react ^18.2.0(peer,宿主运行时注入)、@deepseek-ai/dsh-client-ui-conversation(提供 SlotMap 与 turnTail/input.dock 槽位)、@deepseek-ai/dsh-client-ui-slots(slot 注册与翻译运行时)、@deepseek-ai/dsh-client-runtime(sessions/workspaces/snapshot 服务)src/index.ts 是 host 半区(apply 为 no-op,纯注册用占位让 bundle 可见);src/client/index.ts 是 browser 半区(apply 时先做 globalThis 装载守护,再注册 locale 与两个 slot:conversation.chat.turnTail 放编辑+手动重试按钮、conversation.input.dock 放监督器状态行)。跨半区共享的纯逻辑放在 src/core/(transcript / retry-policy / retry-supervisor),框架无关便于单测packages/dsh-chat-recovery/src/index.ts(host 入口,apply 为 no-op)、packages/dsh-chat-recovery/src/client/index.ts(client 入口,apply 注册槽位与监督器);bundle 声明在 packages/dsh-chat-recovery/cordis.patch.yml:5-7(插入 id ui-chat-recovery),浏览器依赖在 packages/dsh-chat-recovery/package.json:24-37(注入 4 个官方 @deepseek-ai/dsh-client-* 模块 + platform: web)| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH 宿主 | 0.1.0-rc.6+ 推荐 | package.json 未在 dsh.engines 声明;devDependencies 统一锁到 @deepseek-ai/dsh-* ^0.1.0-rc.8 |
| Node.js | ^22.19.0 或 >=24.0.0 | package.json engines.node |
| 平台 | 跨平台 | host 半区 apply 为 no-op;client 半区跑在浏览器,无 OS 限制 |
| 原生模块 | 无 | 全 TypeScript / React,不引入 native 绑定 |
| React | ^18.2.0 | peerDependency,由宿主注入 |
dsh plugin --profile web add github:zhu1090093659/dsh-web-ui/packages/dsh-chat-recovery
本插件无需用户配置(无 dsh settings 卡、无环境变量、无 Schema 声明)。
源码内仅 src/core/retry-policy.ts 暴露两个运行时常量,调整需改源码后重新构建:
| 内部常量 | 位置 | 默认值 | 作用 |
|---|---|---|---|
MAX_EXTRA_RETRIES | core/retry-policy.ts:18 | 5 | 自动重试在原失败之外的额外尝试上限 |
BACKOFF_DELAYS_MS | core/retry-policy.ts:21 | [1_000, 2_000, 4_000, 8_000, 16_000] ms | 第 1..5 次自动重试前的退避延迟 |
Q: 安装之后按钮没出现怎么办?
A: 三步排查。第一,确认 dsh web 重启过,bundle 激活是启动时一次性做的,刷新浏览器不足以触发;第二,看一眼浏览器控制台是否有 chat-recovery 相关的报错;第三,编辑按钮只在"最近一条已结束用户消息"的尾部出现——会话正在跑、或最近一条消息含附件/图片,会让编辑按钮不渲染。turnTail 槽位的选择器只能拿到 turn/seq/openFile 三个字段,无法读会话快照,所以入口会在每个已完成轮次上都尝试挂载,由组件内部用快照决定是否显示。证据:packages/dsh-chat-recovery/src/client/TurnActionsView.tsx:11-15、56-60、packages/dsh-chat-recovery/README.zh.md:64-66。
Q: 编辑了首轮消息,结果会怎样?
A: 首轮之前没有可切的 turn/end 前缀,分叉退化为在同一工作区下新建一个空白会话,并把编辑后的文本发过去——原会话不受任何影响。如果当时没有匹配的工作区,会先创建再 connect。代码:packages/dsh-chat-recovery/src/client/wiring.ts:20-31(connectBlank 工作区回退)+ packages/dsh-chat-recovery/src/client/wiring.ts:82-94(forkAtSeq 为 null 时走空白分支)。
Q: 自动重试和手动重试怎么触发?
A: 自动重试仅在满足四个条件时启动:错误码/消息命中可恢复正则(timeout/network/5xx/429/rate-limit/empty/no-response 等)、轮次不含 tool-result/command 节点、不是用户主动停止、不是输出 token 上限;并且 host 端没有同轮次 model-retry 在调度。一旦命中,监督器在 dock 上显示「自动重试 1/5,约 1s 后」并按指数退避自动发起 fork+prompt。其它任何失败——包括不可恢复错误(401/403/402/422/quota/auth/key/permission/invalid/cancel 等)、含工具/命令的轮次、用户停止、max-tokens——都不会自动重试,只在尾部放一个「重试」按钮,每次点击重放一次。判定:packages/dsh-chat-recovery/src/core/retry-policy.ts:114-122、154-164、172-182。
Q: 自动重试正在跑的时候我想取消,怎么做?
A: dock 上的「取消重试」按钮随时可点,点了之后不再发起后续尝试;同一会话若再次出现新失败才会重新进入自动流程。auto 等待阶段还有「立即重试」按钮,跳过剩余退避直接发起下一次。取消后界面停在失败轮次,不会被强制跳到子分支。代码:packages/dsh-chat-recovery/src/client/RetryDock.tsx:36-58、packages/dsh-chat-recovery/src/core/retry-supervisor.ts:215-219、222-226。
Q: 5 次自动重试用完了会怎样?
A: 监督器进入 exhausted 状态("已重试 5 次仍失败:原因"),停在原失败轮次上,dock 给出「手动重试」按钮;点一次只会跑一次 fork+prompt(手动模式不走指数退避,也不自动重试)。同时若有自动重试期间的子分支,会自动 open 回原会话,避免用户停留在一个无意义的中间分支上。代码:packages/dsh-chat-recovery/src/core/retry-supervisor.ts:160-167、305-313、packages/dsh-chat-recovery/src/client/RetryDock.tsx:75-90。
Q: 会不会在原会话里堆出重复消息?
A: 不会。每次重试都从失败轮次"之前"的 turn/end 前缀切一个全新子分支(首轮失败退化为同工作区空白会话),仅在该子分支里把原始文本重发一次。原会话保持不变,失败轮次的流式片段也不会进入下一次模型请求。同时如果用户在重试子分支里又自己发了新消息,监督器会立即让位(cancel),避免覆盖用户意图。代码:packages/dsh-chat-recovery/src/core/retry-supervisor.ts:263-303、packages/dsh-chat-recovery/src/core/retry-supervisor.ts:146-149。
Q: 关闭浏览器标签页会取消自动重试吗?
A: 会。自动重试依赖浏览器侧的 setTimeout 调度(与任务看板的 cron 同模型),关闭/刷新标签页即丢失本次监督;下次重新打开会话只能看到手动重试按钮,不会自动继续尝试。原会话和失败轮次的历史由 host 持久化保留,不会丢失。说明:packages/dsh-chat-recovery/README.zh.md:66-68。
Q: 含图片/附件的消息能编辑吗?
A: 不能。判定逻辑要求 user 消息的所有内容块都是文本块;只要有一条非文本块(图片/附件/文件等),就返回 null 不给编辑。这是出于"无法安全复制进重新提交的提示词"的考量——图片二进制不能简单重发。代码:packages/dsh-chat-recovery/src/core/transcript.ts:44-52、61-94。
Q: 主机自身已经在重试,本插件会重复发起吗?
A: 不会。turn 上若已有 model-retry 节点且处于 scheduled/started,verdictFor 直接返回 {action:'none'} 让本插件静默;只有 host 让出之后,本插件才会接管后续判断。代码:packages/dsh-chat-recovery/src/core/transcript.ts:168-172、packages/dsh-chat-recovery/src/core/retry-policy.ts:176。
Q: 卸载插件会不会留下残留?
A: 不会。本插件不在 host 上写任何持久文件,编辑/重试都通过 fork 创建新会话,原会话不变,失败历史由 host 自己保留。卸载 dsh plugin --profile web remove @linxin666/dsh-chat-recovery 后无插件自留状态。说明:packages/dsh-chat-recovery/README.zh.md:29-39。
入门 — 一行命令安装,重启 dsh web 即可见尾部「编辑」按钮与 dock 状态行;无需任何配置,原会话始终不受影响。
packages/dsh-chat-recovery/src/client/TurnActionsView.tsx:11-15)packages/dsh-chat-recovery/src/core/transcript.ts:44-52)packages/dsh-chat-recovery/src/client/index.ts:80-82、packages/dsh-chat-recovery/README.zh.md:66-68)packages/dsh-chat-recovery/src/core/retry-policy.ts:154-164)packages/dsh-chat-recovery/src/core/retry-policy.ts:158-162)packages/dsh-chat-recovery/src/core/retry-supervisor.ts:160-167)packages/dsh-chat-recovery/src/client/wiring.ts:82-94)__dshChatRecoveryApplied 做单实例守护;hot-reload 时由 fiber cleanup 释放,但页面级 reload 之前只能有一个生效工厂(packages/dsh-chat-recovery/src/client/apply-guard.ts:21-30)