dsh-all-rewind
Verifieddsh-all-rewind · v0.4.0 · MIT
Conversation-turn rewind for DeepSeek Harness: per-session hidden snapshot repository (before-chat-turn / toolcall anchors), content-addressed directory trees, rewind buttons under each user prompt, configurable snapshot storage and automatic maintenance.
Install
dsh plugin add dsh-all-rewind Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-all-rewind
English · 中文说明
给 DeepSeek Harness(DSH)用的对话轮次回退插件: 每轮对话开始前给工作区打快照,AI 每次改文件后再提交一次,在你发出的提示词下方点一下,就能把工作区还原到那一轮之前的状态。
┌─────────────────────────────────────────────────────────────┐
│ 你:把登录页按钮改成蓝色,顺便删掉旧的 API 代码 │
└─────────────────────────────────────────────────────────────┘
↩ 回退到第 12 轮对话之前
- 位置符合直觉:回退入口就在你自己的提示词下方(和 TRAE 一样)。
- 可逆:每次回退前自动留安全快照,误回退还能再退回来。
- 磁盘有界:未变文件跨版本共享、旧轮次自动修剪、超阈值自动 GC。
- 存储可放别的盘:改一个
dataDir配置就行。
目录
效果预览
安装并重启 DSH 之后,你发出的每条提示词下面都会多出一行小字:
┌─────────────────────────────────────────────────────────────┐
│ 你:把登录页按钮改成蓝色,顺便删掉旧的 API 代码 │
└─────────────────────────────────────────────────────────────┘
↩ 回退到第 12 轮对话之前
鼠标悬停可看:这一轮改了几个文件、消息预览、快照存储位置与占用。 点击 → 二次确认 → AI 在这一轮的文件改动静默撤销,底部浮层显示还原/删除/跳过文件数以及安全快照版本。
功能特性
| 轮次快照 | turn/start 时写入 before-chat-turn-<n>,此时模型还没动任何文件。 |
| 工具级提交 | 每次会改文件的工具结果写入 toolcall-<n>-<callId>,历史可审计。 |
| 文件级还原 | 还原被改文件、删除该轮新建文件、永不删除任何快照都没见过的文件。 |
| 安全 | 回退前留安全快照、检测手动改动冲突、扫描被截断时拒绝回滚。 |
| 省磁盘 | 内容寻址目录树,未改动的文件/目录跨版本共享。 |
| 自动维护 | keepTurns 窗口修剪 + 超过体积阈值自动 GC。 |
| 存储可配 | dataDir / DSH_ALL_REWIND_DIR,支持绝对路径、~/…、相对 $DSH_HOME。 |
| 可脚本化 | Agent 工具 rewind_status / rewind_list / rewind_restore / rewind_prune,以及仅本机可访问的 HTTP API。 |
环境要求
- 支持插件机制的 DeepSeek Harness(桌面端或 web profile)。
- 只有手动/源码安装需要 Node.js 22+;走 DSH 插件管理器安装不需要本机 Node。
- 无运行时 npm 依赖,Windows / macOS / Linux 均可。
安装
无论用哪种方式,装完都要重启 DeepSeek Harness:桌面端在启动时加载插件并注入客户端 UI。
最快安装(GitHub)
现在就能用,不依赖 npm 发布:
dsh plugin --profile web add github:ShaoBei47/dsh-all-rewind
桌面端:在 DSH 会话里让 Agent 安装 dsh-all-rewind(它会走插件管理器的 install_bundle),
或使用插件管理界面。不要在终端对桌面 profile 执行 dsh plugin --profile desktop …——桌面 profile 由应用托管。
从 npm 安装
npm 包发布后可用:
dsh plugin --profile web add dsh-all-rewind
桌面端同样通过 DSH Agent / 插件界面安装,而不是终端。
手动安装(npm 包 → profile)
不想用插件管理器时,可以下载 npm 包,再用仓库里的安装脚本复制进 profile:
# 1) 把 npm 包下载到临时目录
npm install --prefix "%TEMP%\dsh-rewind-pkg" dsh-all-rewind
# 2) 复制进 desktop profile 并写入 cordis.patch.yml
# 在本仓库目录里执行(需要 Node 22+)
node install.mjs --source "%TEMP%\dsh-rewind-pkg\node_modules\dsh-all-rewind"
从源码安装
git clone https://github.com/ShaoBei47/dsh-all-rewind.git
cd dsh-all-rewind
node install.mjs # 装进 desktop profile
node install.mjs --profile web # 装进其它 profile
node install.mjs --data-dir E:/dsh-rewind-snapshots # 快照放到另一个盘
node install.mjs --dry-run # 只预览要改什么
install.mjs 会把运行时文件复制到 <DSH_HOME>/profiles/<profile>/node_modules/dsh-all-rewind,
并向 <DSH_HOME>/profiles/<profile>/cordis.patch.yml 追加一个受管理的配置块(自动生成时间戳备份)。
它不会碰你的快照数据,并且会自动清理旧的 dsh-trae-rewind 条目。
手动等价做法: 把本目录复制到 <DSH_HOME>/profiles/desktop/node_modules/dsh-all-rewind,
然后在 <DSH_HOME>/profiles/desktop/cordis.patch.yml 末尾追加:
- insert:
- id: dsh-all-rewind
name: dsh-all-rewind
config:
keepTurns: 20
验证安装
你的提示词下方出现回退小字;
宿主日志出现
dsh-all-rewind ready (dataDir=…);让 Agent 调
rewind_status,或访问:curl http://127.0.0.1:19387/dsh-all-rewind/api/status
升级 / 卸载
# 从更新的代码重新安装 / 升级
node install.mjs
# 卸载:移除 profile 里的注册条目和副本(快照数据保留)
node install.mjs --uninstall
卸载后同样需要重启 DeepSeek Harness。
快速上手
- 安装并重启(见上);
- 发一条会让 AI 改文件的提示词;
- 几秒内这条提示词下方会出现
↩ 回退到第 N 轮对话之前; - 悬停确认 → 点击 → 二次确认,即可回退。
已经打开的编辑器标签需要重新打开才能看到还原后的内容。
使用
在提示词下方回退
只有有快照的轮次才会出现入口(插件运行期间捕获、且还在
keepTurns窗口内)。点击 → 二次确认 → 工作区还原到该轮对话发起前。
回退前先写
pre-revert-<时间>安全快照,回退后再写post-revert-<时间>基线,所以回退本身可再次回退。文件处理规则:
情况 动作 该轮被修改过 从快照还原 该轮新建 删除 AI 回合之外被手动改过 默认覆写(记为 conflict),或配 onConflict: "skip"跳过任何快照都没见过 不动
让 Agent 帮你回退
| 工具 | 作用 |
|---|---|
rewind_status |
快照仓库 / 回滚点 / 存储位置与占用概览 |
rewind_list |
当前会话的回滚点(all: true 看全部会话) |
rewind_restore |
执行回滚:{ target, dryRun?, onConflict?, force? } |
rewind_prune |
修剪 keepTurns 窗口之外的版本 |
可以直接说:
- “列出我的回滚点”
- “回退到第 3 轮之前”
- “先看看回退到第 2 轮会影响哪些文件,不要真的改”(用
dryRun: true)
target 支持版本号(推荐)、commit/tree 前缀或标签(如 before-chat-turn-3)。
HTTP API
界面用的就是这套仅本机 API,也方便脚本调用。端点只接受回环地址、拒绝跨站请求,POST 需要
Content-Type: application/json。
GET /dsh-all-rewind/api/status # storage.dataDir、diskBytes、会话/回滚点数量
GET /dsh-all-rewind/api/sessions
GET /dsh-all-rewind/api/points?sessionId=<id>
POST /dsh-all-rewind/api/revert {sessionId, target, dryRun?, onConflict?, force?}
POST /dsh-all-rewind/api/prune {sessionId?}
POST /dsh-all-rewind/api/maintenance {sessionId?, keepVersions?}
GET /dsh-all-rewind/ui.js
curl -X POST http://127.0.0.1:19387/dsh-all-rewind/api/revert \
-H "Content-Type: application/json" \
-d '{"sessionId":"session-abc","target":12,"dryRun":true}'
配置
配置写在 profile 的 cordis.patch.yml 里(install.mjs 管理的那段插件条目):
- insert:
- id: dsh-all-rewind
name: dsh-all-rewind
config:
dataDir: "E:/dsh-rewind-snapshots" # 可选,见「快照存在哪」
keepTurns: 20
autoMaintenance:
enabled: true
thresholdBytes: 134217728 # 128 MiB
checkEveryTurns: 3
| 配置 | 默认 | 说明 |
|---|---|---|
dataDir |
$DSH_HOME/dsh-all-rewind |
快照根目录(绝对路径 / ~/… / 相对 $DSH_HOME)。 |
keepTurns |
20 |
保留多少个轮次回滚点;窗口外的工具调用版本一并修剪。 |
autoMaintenance.enabled |
true |
是否启用自动磁盘维护。 |
autoMaintenance.thresholdBytes |
134217728 |
单个仓库超过该大小触发 GC。 |
autoMaintenance.checkEveryTurns |
3 |
每多少轮检查一次(检查很便宜)。 |
autoMaintenance.keepVersions |
200 |
自动 GC 保留的版本数。 |
maxFileBytes |
1048576 |
超过该大小的文件不参与版本化。 |
maxFiles |
8000 |
扫描文件数上限;超出会标记截断并拒绝回滚(避免误删)。 |
maxTotalBytes |
268435456 |
扫描总字节上限。 |
ignoreDirs |
.git、node_modules、dist、.tmp 等 |
追加忽略目录名。 |
ignoreFiles |
.DS_Store 等 |
追加忽略文件名。 |
captureSkipTools |
只读工具(read、grep、web_* 等) |
不触发快照的工具。 |
respectSandboxPolicy |
true |
只读会话拒绝回滚(force: true 可绕过)。 |
onConflict |
overwrite |
快照后被手改的文件如何处理:overwrite 或 skip。 |
http.enabled |
true |
是否注册本地 HTTP API / UI 资源路由。 |
http.requireLocalOrigin |
true |
强制回环 Host、同源与 CSRF 校验。 |
ui.enabled |
true |
是否注入客户端回退入口。 |
快照存在哪
解析优先级:
cordis.patch.yml里的config.dataDir;- 环境变量
DSH_ALL_REWIND_DIR(旧的DSH_TRAE_REWIND_DIR仍兼容); - 默认
$DSH_HOME/dsh-all-rewind(Windows 通常是C:\Users\<你>\.dsh\dsh-all-rewind)。
支持绝对路径、~/…、以及相对 $DSH_HOME 的路径。当前实际位置与占用可在按钮悬停提示、
rewind_status 或 GET /api/status(storage.dataDir / storage.diskBytes)查看。
修改 dataDir 不会自动迁移已有快照,需要的话手动移动或删除旧目录。
每个会话的目录结构(<dataDir>/snapshots/<会话ID>/v2/):
objects/<aa>/<rest> 内容寻址的文件 blob 与目录树节点(zlib 压缩,sha256 命名)
base/content/<rel> 最新快照的工作区镜像
disk/content/<rel> 已删除文件的墓碑副本
version_file_<n>.json 每个版本一个很小的元数据文件:根 tree id、变更集、统计
first_graph.json 仓库身份(会话 id + 工作区)
latest_change.json 指向最新版本
工作原理
session/created → baseline-<时间> 首次观察工作区
turn/start → before-chat-turn-<轮次> ← 「回退到第 N 轮之前」的目标点
+ 修剪 keepTurns 之外的版本
+ 必要时执行自动维护
user/message → 把该轮提示词预览挂到刚才的点上
tool/call → 记住 callId → 工具名
tool/result → toolcall-<轮次>-<callId> (只读工具跳过)
回退 → pre-revert-<时间> → 写回文件 → post-revert-<时间>
每个会话一个隐藏仓库,保持 TRAE 的目录布局与锚点命名,但内部使用自带的内容寻址存储而不是
调用 git:blob 用 zlib 压缩、按 sha256 命名;目录树是内容寻址的 JSON
({"f":{文件→blob},"d":{目录→子树 id}}),未变子树跨版本共享——和 git tree 的结构共享思路一致。
好处是没装 git 的机器、以及禁止 spawn 子进程的受限宿主也能用。
还原时对比「目标版本的文件树 / 当前磁盘状态 / 最新快照」:
| 情况 | 动作 |
|---|---|
| 目标里有、磁盘缺失 | 从 blob 还原 |
| 目标里有、内容一致 | 跳过 |
| 目标里有、内容不同 | 覆写(手改过会记为 conflict) |
| 目标之后新建的 | 删除 |
| 从未被快照见过 | 不动 |
| 扫描被截断 | 拒绝回滚 |
磁盘占用
同一套合成负载实测(v0.1 每版内嵌全量清单 → 现在的共享树清单 + 窗口修剪):
| 场景 | 工作区 | v0.1 快照目录 | 当前快照目录 |
|---|---|---|---|
| 400 文件 / 30 轮(61 版本) | 1.3 MiB | 6.8 MiB | 1.6 MiB(元数据 65 KiB) |
| 2000 文件 / 20 轮(41 版本) | 6.4 MiB | 25.0 MiB | 7.4 MiB(元数据 104 KiB) |
固定成本是 base/content 镜像(≈ 1× 被跟踪源码)和确实改过的文件内容;
keepTurns、忽略规则与自动维护让长会话保持在合理范围。
已知限制
- 只还原文件,不截断对话:和 TRAE 的按钮一致,聊天记录保留。
- 只有捕获到的轮次才有入口:安装前的旧轮次、或被
keepTurns修剪掉的轮次没有。 - 共享工作区:并行会话/子代理改同一目录时各自快照独立;回滚其中一个会写回它的文件状态,可能覆盖另一个的并发修改。
- 大文件与忽略目录不参与版本化(
maxFileBytes、ignoreDirs)。 - 只读会话默认拒绝回滚(可
force: true)。 - 修改
dataDir不会迁移旧快照。
常见问题
提示词下方没有回退小字?
- 安装后是否重启了 DeepSeek Harness?插件和客户端 UI 都在启动时加载;
- 让 Agent 调
rewind_status(或访问/dsh-all-rewind/api/status),确认插件版本与存储路径; - 打开 DevTools 控制台执行
__dshAllRewind:undefined说明客户端脚本没注入;__dshAllRewind.state看识别到的会话/回滚点,__dshAllRewind.reconcile()可手动重扫; - 通过插件管理器安装的,确认该 bundle 在当前 profile 已启用。
较早的提示词没有回退入口?
那一轮没有快照:插件是之后才启动的,或版本已被 keepTurns 修剪。调大 keepTurns、重启后新轮次会保留更多历史。
回退结果里有“跳过”的文件?
这些文件在快照之后被 AI 回合之外的操作改过。默认策略会覆写(有安全快照兜底);
想永远保留手改内容可设 onConflict: "skip"。
磁盘占用高了怎么办?
用 rewind_status 或按钮悬停提示看大小;调小 keepTurns、补充 ignoreDirs,
或调用 POST /api/maintenance(或 rewind_prune)立即修剪并 GC。
桌面端用终端 CLI 装不了?
桌面 profile 由应用托管。请通过 DSH Agent / 插件界面安装,或用手动/源码安装方式;
只有 web profile 支持 dsh plugin --profile web add …。
参与贡献
欢迎提 Issue 和 PR;较大的改动建议先开 Issue 讨论。
开发环境、测试命令、目录结构、发布流程见 CONTRIBUTING.md;
PR 前跑一遍:
npm test # node --test test/(Node 22+) npm run smoke # 端到端冒烟测试