dsh-rollback

toolsinstall failsnpmMIT

Observational file-mutation rollback for the fs tools: write/edit pre-images are checkpointed as git blobs or snapshots and restored through a model-facing rollback_files tool or a /rollback command.

下载量 / Downloads
735
测试时间 / Tested
2026-11-07
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-rollback

Sandbox test results

StageResult
1 install✗
2 list—
3 mount—
4 boot—
First error
dsh: initialized profile web at /home/node/.dsh/profiles/web

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-rollback

![npm version](https://www.npmjs.com/package/dsh-rollback)

![License: MIT](LICENSE)

[English](README.md) | 中文

DeepSeek Harness 的文件变更回滚插件:将 write/edit 变更在其结果中报告的改前映像记录为检查点,并对黑盒 bash/run_code 调用做前向快照使其文件变更可 diff,把每个改前映像存入工作区 git 对象库或快照存储,并通过面向模型的 rollback_files 工具和面向人的 /rollback 命令提供还原。它不注册任何服务,也不改动循环代码——捕获搭在文档化的 tools/* 扩展点上(tools/result 观察事件与 tools/execute around 钩子),还原直接写文件(绝不经过 fs 策略 seam 或沙箱,因为撤销一次变更不应被该变更当初通过的策略所门控)。

安装

本包是可安装的 bundle(声明了 dsh.bundle),直接接入 profile,无需改动 harness。你只需要一个 dsh CLI;运行时 peer 包(@deepseek-ai/dsh-tools、@deepseek-ai/cordis 等)从 dsh 安装本身解析,无需额外安装任何东西。

前置条件

  • 机器上有 dsh CLI(dsh plugin 内部调用 pnpm,所以 pnpm 需在 PATH 中)。
  • 一个要安装进去的 profile——下面的 demo 首次使用时自动初始化,可换成任意名字。

从 npm 安装(推荐)

dsh plugin --profile demo add dsh-rollback

其他来源用法相同:

# 直接从 git 安装(源码由 prepare 脚本构建;建议锁定 commit)
dsh plugin --profile demo add github:you/dsh-rollback#<sha>

# 或从本地 tarball
dsh plugin --profile demo add ./dsh-rollback-<version>.tgz

验证安装

1. $DSH_HOME/profiles/demo/($DSH_HOME 默认为 ~/.dsh)下的 profile manifest 中,dependencies 和 dsh.profile.bundles 都会出现 dsh-rollback——因为包声明了 dsh.bundle,reconciler 会自动加入。等价的手动补丁层行:

- id: rollback
  name: dsh-rollback
  config:
    mode: auto          # auto | git | snapshot
    storeDir: ''        # '' = <harness home>/rollback
    maxRecords: 200
    gitPath: git

2. 启动一个会话。以下任一现象都说明插件已生效:

  • 模型的工具列表里能看到 rollback_files;
  • 在还没发生任何变更时输入 /rollback,得到 rollback: nothing to restore(而不是"未知命令"报错)。

使用教程

30 秒快速上手

1. 打开一个工作目录在 git 仓库内的会话。

2. write 一个文件 notes.md,内容为 hello。

3. 再次 write 它为 goodbye——第一次的内容已被静默记录。

4. 对模型说"把你刚覆盖的文件还原"(它会调用 rollback_files),或自己输入 /rollback。

5. 查看 notes.md:内容又变回 hello。

面向人 — /rollback [count]

在聊天输入框输入(base web 与 headless profile 挂载了命令所需命令注册表):

  • /rollback —— 撤销本会话工作目录中最近一条被捕获的变更;
  • /rollback 3 —— 撤销最近三条。

输出逐文件列出还原动作:

rollback: restored 2 file mutation(s):
  restored  /ws/src/lib/parse.ts
  deleted   /ws/src/lib/generated.ts

人手动回滚还会告诉模型。命令的生命周期(command/run/command/done)是 log-only、永不进入模型上下文的,因此插件会为下一个 pre-step 排入一条面向模型的 notice(走 Agent.inject):逐条列出它重写或删除了哪些路径,并说明模型手里的这些文件副本已经过期。没有这条链路时,人的回滚会在模型背后改写文件,而模型的 transcript 仍在描述回滚前的世界——它甚至会继续针对已经不存在的路径干活。设 notifyModel: false 可让手动回滚保持静默。

面向模型 — rollback_files

新增一个面向模型的工具 rollback_files {count}(无提示词章节)。它用于让模型撤销自己犯下的 write/edit 错误,而不是回头麻烦用户。还原限定在调用方会话的工作目录内,输出为逐文件摘要——不会回显还原后的文件内容。

捕获范围

同一 store 有两条捕获路径:携带 before 改前映像的成功 write/edit 工具结果,以及根 bash/run_code 调用造成的文件变更(在 git 仓库内前向快照后 diff,见[行为](#行为))。通过 str_replace_editor、裸子进程或非 git 仓库内的 bash 调用产生的变更没有可恢复的改前映像,不会被捕获。会话只能还原位于自身工作目录下或其自身的记录。

效果演示(前后对比)

一次 write 覆盖、一次撤销。同一个文件,四种状态:

步骤 · 动作 · notes.md

1 · 初始状态 · hello

2 · 模型 write 写入错误编辑——改前映像被捕获 · goodbye

3 · 模型调用 rollback_files {"count": 1} · (透明)

4 · 逐字节还原 · hello

完整 transcript:

# 1. 初始状态
$ cat /ws/notes.md
hello

# 2. 模型覆盖文件;tools/result 携带改前映像 "hello",
#    捕获监听器将其记录进 git 对象库
> tool/call   write {"path": "/ws/notes.md", "content": "goodbye"}
> tool/result {"path": "/ws/notes.md", "before": "hello", ...}

# 3. 模型意识到写错了,撤销该变更
> tool/call   rollback_files {"count": 1}
> tool/result "rollback: restored 1 file mutation(s):
               restored  /ws/notes.md"

# 4. 还原为变更前内容
$ cat /ws/notes.md
hello

底层实现:git hash-object -w 将改前映像写入 git 对象库(零索引/分支/工作树污染),同时向持久化 manifest.jsonl 追加一行——因此重启后同一撤销依然可用。

工作原理

flowchart TD
    A["write/edit 工具结果"] --> B["tools/result 观察事件"]
    B --> C{结果带 before 改前映像?}
    C -- 否 --> X[忽略]
    C -- 是 --> S[CheckpointStore 捕获]
    A2["bash / run_code 调用"] --> B2["tools/execute 前向快照 + diff"]
    B2 --> C2{工作区是 git 仓库?}
    C2 -- 否 --> X2[跳过并告警]
    C2 -- 是 --> S
    S --> G{工作区是 git 仓库?}
    G -- 是 --> BLOB["git hash-object -w 存 blob"]
    G -- 否 --> P["写 storeDir/snapshots/ 快照"]
    S --> MF["追加 manifest.jsonl"]
    U["模型调 rollback_files / 用户 /rollback"] --> RS["restore 按 session.cwd 作用域"]
    RS --> RR["git cat-file / 快照 / 删除文件"]

上图的完整示例与前后对比 transcript 见[效果演示](#效果演示前后对比)。

插件(namespace: rollback)

函数/命名空间插件(name / inject / Config / apply),不是服务。它与 dsh-tool-call-timeout-policy 同属循环卫生 guard 家族:在文档化的 tools/* 扩展点之上叠加安全网,而非触碰 agent 循环。

Config

键 · 类型 · 默认值 · 含义

mode · 'auto' \ · 'git' \ · 'snapshot' · 'auto' · git 将每个改前映像记录为 git blob(需要仓库;非仓库路径大声失败且不捕获任何内容);snapshot 始终将改前映像复制到 storeDir/snapshots/ 下;auto 在工作区是仓库时按文件选用 git,否则用快照。

storeDir · string · '' · 持有持久化 manifest.jsonl 与 snapshots/ 的根目录。为空时解析为 Harness home 下的 rollback。

maxRecords · number · 200 · 每个 store 内存记录的上限;超出后丢弃最旧的(持久化 manifest 保留全部)。

gitPath · string · 'git' · Git 可执行文件名或绝对路径。

mutationTools · string[] · ['bash', 'run_code'] · 需要前向快照的工具名——不带 before 改前映像的黑盒变更调用。只对根派发做快照(Code Mode 嵌套子调用由它们的 run_code 父级覆盖),且仅在 git 仓库内。

notifyModel · boolean · true · 人手动 /rollback 之后注入一条面向模型的 notice,让模型知道文件在它掌控之外被改动了。模型自己调 rollback_files 不需要这条通知(其调用与结果本来就在 transcript 里)。

行为

捕获。 tools/result 监听器将成功的 write/edit 结果转换为检查点:结果的 before 字段即改前内容(null 记录文件原本不存在)。blob 改前映像通过 git hash-object -w --stdin 写入文件所在仓库(向上探测 .git 发现仓库根,按目录缓存)——零索引/分支/工作树污染,由 git 自身内容寻址并去重。每条记录以一行 JSONL 追加到 manifest.jsonl;插件加载时的 store 回放恢复内存列表,因此还原在重启后依然可用。只捕获绝对本地展示路径;相对或远程展示路径(非本地文件系统后端)被忽略。

前向快照(bash / run_code)。 tools/execute around 钩子会在根 bash(或 run_code)调用执行前快照工作区。候选集由 git 一次调用枚举(git ls-files -co --exclude-standard):已跟踪文件 + 未被忽略的未跟踪文件——由忽略规则而非手写遍历决定范围,构建产物与 vendor 目录因此零成本。.git 与 node_modules 永不快照(也因此永

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::3,最后更新 2026-09-12

Links

GitHub · Homepage · npm

Same category