dsh-undo:按轮次回退 Agent 对文件的修改

前言

在 DeepSeek Harness(DSH)里跑 agent,模型会通过 write / edit 等工具改工作区文件。改错了、改多了,或者你想先预览再决定是否保留,都需要一种可操作的回退方式。社区里已有「对话回退」类插件,会把对话上下文和文件一起切回去,适合「回到某条消息之前」;若你只想把文件退到某一轮对话开始前的状态,且希望模型能自己触发、headless 环境也能用、又不依赖 git,就需要专门做文件层快照与回滚的插件。下面介绍维护者 ruaibeite 的 dsh-undo(分类:admin-security),它在本轮每次写入落盘前快照,并支持工具、/undo 命令与 Web GUI 按钮三种入口。

这是什么

dsh-undo 是 DSH 的 host 侧插件:在每次文件写入或编辑落盘之前保存原内容快照,模型可调用 undo 工具,或你在输入框敲 /undo,或在助手消息动作行点 ↶ 回退本轮,把一整轮对话里改动过的所有文件一次退回(也可按快照 id 做单文件精细回退)。它不 fork shell、不读凭据、不发网络请求、不碰 git 仓库;快照与索引写在 $DSH_HOME/undo/。需要「对话 + 文件」一起回退时,应选用 dsh-rewind-plugin、dsh-recall-plugin 等对话回退类插件;本插件只做文件层,刻意保持轻量。

与对话回退类插件的差异

dsh-undo 对话回退类(如 dsh-rewind-plugin、dsh-recall-plugin)
回滚对象 文件(本轮改动过的全部文件) 对话上下文 + 文件
谁来触发 模型调 undo 工具;人可敲 /undo;Web GUI 有按钮 多在 Web GUI 点按钮或敲命令
运行环境 host 侧;headless、无 Web UI 的 profile 同样可用 主要在 Web GUI
依赖 无(不依赖 git) 多数依赖 git 或会话 fork
沙箱 快照在进程内用 Node fs 写,workspace-write 下无需额外放宽权限 经 shell 写工作区外存储时可能被沙箱拒绝

核心功能

捕获与轮次

两条互补路径只做旁路记录,不改变写入语义:

  1. intent 钩子(主):挂在 fs/write-intent / fs/edit-intent waterfall,能拿到 callId、agent 等工具执行信息;须 prepend: true,否则可能被内置 observation policy 截断。
  2. fs 服务包装(兜底):包装 writeText / editText 所属原型;若 intent 收不到事件,仍会有快照。

同一次写入可能两条路径都命中,插件用跨机制去重(同路径、同事件、250 ms 内、不同机制只记一条)。

轮次边界来自会话日志中最近一条 user/message 事件的 seq(默认扫描尾部 sessionScanEvents 条);读不到会话的写入归入当前进行中的轮次。

undo 工具

模型可调用 undo:

参数 说明
action list:按轮次列出快照;restore:回滚
id 回滚单个快照;给出时优先于 turn
turn 回滚哪一轮:last(默认)、1/2/…(从新到旧),或 list 显示的 turn 键
limit list 时返回多少轮,默认 10
dryRun restore 时只报告每个文件会变成什么,不写盘

回滚语义要点:同一文件在一轮内多次改动时,恢复到该轮开始前最早快照对应的内容;本轮新建的文件回滚即删除;回滚动作本身也会被快照,因此可再次 undo(等效 redo);单文件失败不中止整轮,结果逐条报告。

人工入口:/undo 命令

命令 作用
/undo 或 /undo list 列出最近轮次
/undo restore [轮次] 回滚该轮;省略轮次 = 最近一轮
/undo dry [轮次] 预演,不写盘
/undo id <快照id> 单文件精细回退

命令与 undo 工具共用同一套执行逻辑。若 profile 无命令注册表则只少人工入口;/undo 已被占用时会自动注册为 /undo-turn。

Web GUI:助手消息动作行

已定稿助手消息的动作行(复制、分支旁)会出现 ↶ 回退本轮,点击即回退最近一轮;按钮执行宿主命令 /undo 1,与工具和命令行为一致。预览可用 /undo dry。

安装与启用

从 GitHub 安装(README 推荐;package.json 的 dsh.bundle 供 dsh 自动挂载):

dsh plugin --profile web add github:ruaibeite/dsh-undo

也可在克隆仓库后使用自带脚本(会自动改用桌面版 CLI):

./install.sh
PROFILE_NAME=headless ./install.sh

安装后重启 dsh 生效。插件以当前 dsh 进程权限运行;安装前请自行查看 GitHub 源码 与 MIT 许可证。要求 Node >=20(见 package.json engines)。

存储与配置

$DSH_HOME/undo/
├── index.json                 # 快照索引(id/time/path/existed/bytes/turn/sessionId)
└── files/<快照id>             # 快照内容

$DSH_HOME 内部的写入会被排除。在 cordis.patch.yml 对应条目可配置:

键 默认 说明
maxSnapshots 300 快照条数上限,超出淘汰最旧
maxFileBytes 4194304 超过此大小的文件不快照
onlyToolWrites false true 时只快照带工具 actor 的写入
sessionScanEvents 400 定位轮次边界时会话日志尾部扫描条数
listFilesPerTurn 10 list 每轮最多列出文件数,其余省略汇总
debug false true 时事件写入 $DSH_HOME/undo/debug.log

典型用法

  1. 安装并重启 dsh 后,让 agent 在一轮对话里修改若干文件。
  2. 在输入框执行 /undo list,查看最近轮次与文件摘要。
  3. 确认要退哪一轮:执行 /undo dry 1 预演(1 表示最新一轮),或让模型调用 undo,例如 action: "restore", turn: "last", dryRun: true。
  4. 执行 /undo restore 1 或模型 action: "restore" 完成回滚;若只需改一个文件,用 /undo id <快照id> 或工具的 id 参数。

Web GUI 下也可直接点助手消息旁的 ↶ 回退本轮;再次点击可撤销刚才的回滚(因回滚本身被快照)。

适用场景与注意

适合:需要在 DSH 里按「对话轮次」批量撤销文件改动;headless / workspace-write 等受限会话下仍要快照(进程内 fs,不依赖 shell 写工作区外);不想为回退单独维护 git 影子库;希望模型在改乱代码时自行调用 undo。

限制(README 原文):

  • 只回滚文件,不回滚对话。
  • 只记录经 ctx.fs 的写入;shell 等绕过 fs 直接落盘的改动不会产生快照(之后若再被工具写入,会按当时已存在状态快照)。
  • 单文件超过 maxFileBytes 不快照。
  • 轮次分组依赖会话日志中的 user/message;取不到时退化为按工具调用分组。
  • 回滚既有文件走 ctx.fs.writeText,受文件策略约束(只读会话会被拒);删除本轮新建文件用 fs.unlinkSync,是唯一不经策略的写操作,用于把「新建」还原为「不存在」。

DSH 生态是「一切皆插件」;社区目录 SkillHub 插件页 为独立站点,与 DeepSeek / 幻方无官方从属关系。

结尾

dsh-undo 把「按轮次的文件 undo」做成 host 侧轻量能力:双路径快照、undo 工具与 /undo、GUI 按钮共用一套逻辑,在受限沙箱下也能记录。若你的需求是对话层回退,请选用对口的 rewind/recall 类插件;若主要是 agent 改文件后的可逆操作,可按上文命令安装并重启 dsh。

  • 目录页:https://www.skillhub.cn/plugins/ruaibeite/dsh-undo
  • GitHub:https://github.com/ruaibeite/dsh-undo
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye