🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

文件轮次回滚 ruaibeite/dsh-undo

DeepSeek Harness
ruaibeite24 天前有提交已提供中文文档扫描:含敏感能力

把智能体本轮改过的文件一键还原到改动前

29.9/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/10/9 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add ruaibeite/dsh-undo
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · chat
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 24 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件
查看扫描证据(共 8 处,展示前 8 条)
  • 读取本地文件
    lib/index.js:112
    const parsed = JSON.parse(readFileSync(join(root, 'index.json'), 'utf8'));
  • 写入本地文件
    lib/index.js:122
    writeFileSync(join(root, 'index.json'), JSON.stringify(entries, null, 2));
  • 写入本地文件
    lib/index.js:131
    appendFileSync(join(root, 'debug.log'), `${new Date().toISOString()} ${line}\n`);
  • 写入本地文件
    lib/index.js:302
    mkdirSync(filesDir, { recursive: true });
  • 写入本地文件
    lib/index.js:394
    writeFileSync(file, existed ? content : '');
  • 删除本地文件
    lib/index.js:427
    unlinkSync(dropped.file);
  • 读取本地文件
    lib/index.js:732
    content = readFileSync(entry.file, 'utf8');
  • 删除本地文件
    lib/index.js:779
    unlinkSync(entry.path);
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-fs@deepseek-ai/dsh-tools@deepseek-ai/schemastery
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 一键还原本轮改动文件
  • 写入编辑前自动快照
  • 模型调用工具回滚

DeepSeek Harness 的回合级文件撤销:每次写入和编辑落地前都会快照,模型可调用撤销工具回滚某一轮对话改动的所有文件。

README

由 DeepSeek 最新模型翻译生成
dsh-undo — 按轮次回退智能体对文件的修改 给 DeepSeek Harness 加一个文件回滚能力:每次写入或编辑落盘之前先把原内容快照下来,模型可以调用 undo 工具把一整轮对话改动过的所有文件一次退回 —— 不是只能退单个文件。 定位:它和「对话回退」类插件不是一回事 | | 本插件 | 对话回退类(如 dsh-rewind-plugin、dsh-recall-plugin) | | 回滚对象 | 文件(本轮改动过的全部文件) | 对话上下文 + 文件 | | 谁来触发 | 模型自己调 undo 工具,人也可以直接敲 /undo | 人在界面上点按钮 / 敲命令 | | 运行环境 | host 侧插件,headless、无 Web UI 的 profile 同样可用 | 主要在 Web GUI 里用 | | 依赖 | 无(不依赖 git) | 多数依赖 git 或会话 fork 机制 | | 沙箱 | 不 fork shell:快照在进程内写,workspace-write 会话下无需额外放宽权限 | 取决于实现:经 shell 往工作区之外的存储写入时,会被会话沙箱拒掉 | 需要「把对话切回某条消息之前」请用上面那两个插件;本插件只做文件层,且刻意保持轻量。
展开完整文档(README 剩余 1 节)
dsh-undo — 按轮次回退智能体对文件的修改

给 DeepSeek Harness 加一个文件回滚能力:每次写入或编辑落盘之前先把原内容快照下来,模型可以调用 undo 工具把一整轮对话改动过的所有文件一次退回 —— 不是只能退单个文件。

定位:它和「对话回退」类插件不是一回事

| | 本插件 | 对话回退类(如 dsh-rewind-plugin、dsh-recall-plugin) |
| 回滚对象 | 文件(本轮改动过的全部文件) | 对话上下文 + 文件 |
| 谁来触发 | 模型自己调 undo 工具,人也可以直接敲 /undo | 人在界面上点按钮 / 敲命令 |
| 运行环境 | host 侧插件,headless、无 Web UI 的 profile 同样可用 | 主要在 Web GUI 里用 |
| 依赖 | 无(不依赖 git) | 多数依赖 git 或会话 fork 机制 |
| 沙箱 | 不 fork shell:快照在进程内写,workspace-write 会话下无需额外放宽权限 | 取决于实现:经 shell 往工作区之外的存储写入时,会被会话沙箱拒掉 |

需要「把对话切回某条消息之前」请用上面那两个插件;本插件只做文件层,且刻意保持轻量。

沙箱表现:为什么它在受限会话里照样能记录

它的快照与索引是用 Node 的 fs 在进程内写的,不 fork shell、不调用 ctx.shell ——所以在 workspace-write 这类受限会话里不需要任何权限放宽就能持续记录。这一点在真实环境里验证过:同一台机器上,靠 shell 调 git、把仓库放在工作区之外的实现,其 git add 在 workspace-write 下会因写工作区之外而失败(影子仓库对象数为 0);本插件在同样的会话里正常产出快照并带回轮次标记。

边界(便于评审核对,逐条对应代码):

- 快照与索引($DSH_HOME/undo/):fs.writeFileSync,进程内写入,不经过命令沙箱。
- 回滚既有文件:走 harness 的 ctx.fs.writeText,受文件策略约束(只读会话里会被拒 —— 这是期望行为)。
- 删除本轮新建的文件:harness 的文件系统服务没有删除接口,这一步用 fs.unlinkSync,因此不受沙箱策略约束(只读会话里同样会执行)。这是本插件唯一不经策略的写操作,回滚语义需要它:把「本轮新建」还原成「不存在」。
- 不读凭据、不发网络请求、不碰你的 git 仓库。

它怎么工作

两条互补的捕获路径,都只做旁路记录、不改变任何写入语义:

| 路径 | 位置 | 说明 |
| ① intent 钩子(主) | fs/write-intent / fs/edit-intent waterfall | DSH 的官方扩展点,能拿到 actor(callId、agent)等工具执行信息 |
| ② fs 服务包装(兜底) | writeText / editText 的实现原型 | 若 ① 因任何原因收不到事件,② 保证仍然有快照 |

三条经验性的结论,都已落在代码里(细节见 lib/index.js 注释):

1. intent 钩子必须 prepend: true。 cordis 的 waterfall 语义是「不调用 next() 的监听器会否决其后的整条链」,而 @deepseek-ai/dsh-fs-observation-policy 正是这样一个监听器(它直接决定 intent 并返回)。profile 补丁里的插件加载晚于内置插件,所以不 prepend 的话钩子永远轮不到执行 —— 实测跑完整个智能体会话,零事件到达。
2. 服务包装是必需的冗余。 真实链路是 SandboxedFileSystem → LocalFileSystem → FileSystem,而 ctx.fs 每次访问都返回 新的 traceable Proxy,所以包装 ctx.fs 上的方法没有意义,必须包装方法所属的原型。
3. 两条路径会同时命中同一次写入,因此有跨机制去重:同一路径 + 同一事件 + 250 ms 内、且来自不同机制时只记一条。同一机制内的连续两次写入不会被误合并。

轮次是怎么分出来的

工具执行上下文里只有 callId / rootCallId / agent,没有轮次号,所以轮次边界取自 会话日志中最近一条 user/message 事件的 seq(只扫描尾部 sessionScanEvents 条,并按 (session, seq) 缓存,代价有界)。读不到会话的写入(例如 fs 服务兜底路径)会归入「当前正在进行的轮次」,而不是自成一派。

提供的工具

模型可调用 undo:

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

人工入口:/undo 命令

模型不是唯一的使用者 —— 你也可以在输入框直接敲:

| 命令 | 作用 |
| /undo 或 /undo list | 列出最近的轮次(每轮最多 listFilesPerTurn 条,其余省略汇总) |
| /undo restore [轮次] | 回滚该轮;省略轮次 = 最近一轮,轮次用 list 里的序号(1 = 最新) |
| /undo dry [轮次] | 只预演:报告每个文件会变成什么,不写盘 |
| /undo id <快照id> | 只回滚单个快照(单文件精细回退) |

命令与工具共用同一套执行逻辑(命令只负责把输入解析成工具参数),所以两边行为不会漂移。
命令注册是机会式的:profile 里没有命令注册表时照常加载(只少一个人工入口), /undo 已被别的插件占用时自动改注册为 /undo-turn,而不会加载失败。

界面按钮:助手消息动作行里的 ↶

Web GUI 里还有一个按钮:已定稿助手消息的动作行(复制、分支旁边)会出现 ↶ 回退本轮。
点击即回退最近一轮;因为回退本身也会被快照,再点一次就能撤销这次回退(等效 redo)。
只想预览时用 /undo dry。

按钮不直接碰文件,而是执行宿主命令 /undo 1 —— 所以三条路径(模型工具、/undo 命令、界面按钮)共用同一套执行逻辑与同一份结果呈现,不存在「按钮和命令行为不一致」的可能。

实现要点(便于评审核对):

- 客户端半边是 client/client.js,由 package.json 的 dsh.client + exports["./client"] 声明;
它是手写的预构建入口(window.__ModuleLoader__.load({ id, factory }),React 与官方客户端包由宿主 require 提供),不需要打包步骤。
- 注册进 conversation.chat.assistant-actions(官方反馈条目用的同一插槽),order: 20 排在它之后。
- 通过插槽的按会话 inject 拿到绑定了 sessionId 的 runUndo,组件不需要自己解析会话。

回滚语义

- 一轮里同一文件被多次快照(改了两遍)→ 取最早那条,即该文件在本轮开始前的状态。
- 本轮期间新建的文件 → 回滚 = 删除它。
- 回滚动作本身也会被快照 → 回滚可以再撤销(等效 redo)。
- 单个文件失败不会中止整轮:报告里逐条列出成功/失败。

安装

从 GitHub 安装(推荐;package.json 里的 dsh.bundle 让 dsh 自动挂载)
dsh plugin --profile web add github:ruaibeite/dsh-undo

或本地克隆后用脚本(自动改用桌面版自带的 CLI) ./install.sh
PROFILE_NAME=headless ./install.sh

安装后重启 dsh 生效。

存储与配置

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

$DSH_HOME 内部的写入会被排除(那是 harness 自身状态,不是工作区文件)。

cordis.patch.yml 里该条目可配:

| 键 | 默认 | 说明 |
| maxSnapshots | 300 | 保留快照条数上限,超出淘汰最旧的 |
| maxFileBytes | 4194304 | 超过此大小的文件不快照 |
| onlyToolWrites | false | true 时只快照带工具 actor 的写入 |
| sessionScanEvents | 400 | 为定位轮次边界而扫描的会话日志尾部条数 |
| listFilesPerTurn | 10 | action: "list" 每轮最多列出多少条文件,其余用一行省略汇总(list 带上 turn 则打印该轮全量清单) |
| debug | false | true 时把每个事件写入 $DSH_HOME/undo/debug.log |

限制(如实说明)

- 只回滚文件,不回滚对话。 对话层回退请用专门的插件。
- 只看得见经 ctx.fs 的写入。 通过 shell 等绕过 fs 直接落盘的改动不会产生快照;不过这些文件在被工具再次写入时,会以「当时已存在」的形态被快照。
- 超过 maxFileBytes 的单个文件不快照。
- 轮次分组依赖会话日志里存在 user/message 事件;取不到时退化为按工具调用分组。
- 快照只写 $DSH_HOME,不改工作区、不碰你的 git 仓库、无网络请求。

测试

npm install # 官方包从 npm 安装(devDependencies,0.2.0-rc.2 线)
npm test # 业务逻辑:mock fs/tools/agent,36 项断言
npm run test:integration # 真实 cordis 容器 + 真实 waterfall 分发 + 真实 Config 校验,15 项断言

两套测试都用隔离的 DSH_HOME(仓库内 .test-home / .cordis-test-home),跑完自动清理。

许可

MIT
预览分享卡片长什么样 ↓ruaibeite/dsh-undo
分享 · DeepSeek Harness Hub
ruaibeite/dsh-undo

把智能体本轮改过的文件一键还原到改动前

回到顶部