就地回退已完成的回合:点你自己消息旁的按钮即可把这条消息及之后的内容从模型上下文中撤回,原文回到输入框可直接改完重发,并可一并恢复该回合经 DSH 文件系统服务改动过的文件。
安装
# npm 包(预构建)
dsh plugin --profile web add dsh-session-rewind
# Release 预构建包
dsh plugin --profile web add "https://github.com/2310770803/dsh-session-rewind/releases/latest/download/dsh-session-rewind.tgz"
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:2310770803/dsh-session-rewind
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
A DSH Web plugin that puts Rewind on every finished Turn: a per-Turn control in the Chat timeline plus one entry in the Session header, branching the conversation at that Turn and restoring the files that Turn changed.
在 DSH Web 里回退到任意已完成的回合:对话时间线的每个回合旁有 回退 按钮,会话标题栏还有一个
总入口,列出本会话所有可回退的回合;回退会从该回合处分叉出新会话,并把该回合改动过的文件恢复原状。
两个入口 · Two entry points
| 位置 | 作用 |
|---|---|
| 你发出的那条消息旁的 ↶(唯一入口,常显) | 点开选「仅回退对话」或「回退对话并恢复文件」。就地回退:这条消息及之后的对话内容从模型上下文里撤回,原话立刻回到本会话输入框,改完直接重发。回合结束(含自动终止/停止)后按钮自动出现。 |
核心行为(就地回退):选中你某条消息 → 追加一条 surfaceOp: replace 标记,把模型可见的
surface 从这条消息起整体撤回 → 被撤回的行从对话里消失(轨迹面板仍可查,日志从未删改)→
这条消息的原文放进输入框,你可以修改后重新发送。会话不换、不新建。
What it does · 功能
Choosing a Turn opens a small panel with two independent halves:
- Rewind the conversation. A committed Turn cannot be edited in place: the
session log is append-only and branching is the supported operation. The
panel therefore creates a new session that ends exactly at that Turn
(
sessions.forkwith the Turn'sturn/endseq), opens it, and pre-fills its composer with the Turn's original user prompt so you can edit it and send it again. The original session is left untouched. - Restore the files that Turn changed. While a Turn runs, every file the model creates or rewrites is captured before the mutation and stored as a content-addressed blob. The panel lists those files; the checked ones are put back to their pre-Turn content (a file the Turn created is removed).
The two halves can be used together (回退对话) or separately
(仅恢复文件).
选中某个回合后:
- 回退对话:会话日志是只追加的,所以“回退”= 在该回合的
turn/end处分叉出一个新会话并打开它, 同时把该回合原本的用户输入写回新会话的输入框,你可以直接修改后重新发送;原会话完全不动。 - 恢复文件:该回合运行期间,每个被创建或改写的文件都会在写入前抓取一份原始字节存档。 面板会列出这些文件,勾选的会被恢复为修改前的内容;该回合新建的文件会被删除。
就地撤回 vs 分叉:两个都给了
- 就地回退(主):同一会话内撤回,靠一条
surfaceOp: replace标记实现——这是 DSH 自己 压缩(compaction)用的同一机制,日志只追加、不删改。 - 分叉(次):
sessions.fork在该回合turn/end处新建一份会话,内置「在新对话中分支」 按钮用的就是它,升级最稳。
唯一的妥协在前端隐藏被撤回的行:DSH 没有为用户消息提供动作槽,也没有公开「隐藏某段行」
的接口,所以这里按参考插件 SiriLee/dsh-rewind 的做法在前端补:给每条用户消息注入一个 ↶
(锚点取行内最后一个按钮的父节点),回退后按 data-chat-turn 隐藏该行及之后的行。全部特性
检测、卸载即还原;DSH 若在未来提供动作槽,这里会切成槽位实现。
参考插件为什么没有全盘照搬
社区里的 SiriLee/dsh-rewind 提供就地回退(同一会话内
把模型可见 surface 切回更早的用户消息,靠追加一条 surfaceOp: replace 标记实现)。我们借鉴了它的
入口与交互(每条消息/每轮旁的 ↶、模式选择、文件影响清单、↑↓/Enter/Esc、标题栏候选列表),但没有
采用它的就地撤回,原因是:
- 就地撤回不仅要追加标记,还必须在前端把被撤回的行隐藏起来,而 DSH 没有为此提供扩展点——
参考插件是通过包装私有的
uiConversation.viewsregistry 做到的,其源码自己注明 "The seam is private … not an extension point",随版本升级会破。 sessions.fork是 DSH 给 UI 的官方分支能力(内置「在新对话中分支」按钮用的就是它), 升级稳定,且天然满足“上下文被截断 + 可改可重发”。
如果你更希望要就地撤回(同一会话、不产生新会话),可以另装 dsh-rewind-plugin;两者不冲突。
Install · 安装
从插件市场安装(推荐):左侧 插件 → 发现(Discover) → 索引源 → 添加
https://raw.githubusercontent.com/2310770803/dsh-session-rewind/main/index.json
→ 搜索 rewind 或 会话回退 → 安装。索引格式与发布流程见 docs/publishing.md。
本机(profile desktop)已把这个索引源写进 cordis.patch.yml,打开「发现」即可看到条目。
从源码目录安装(本机当前就是这种方式):
// plugin_manager
{ action: 'install_bundle', target: 'E:\\DeepSeek Harness\\dsh-session-rewind' }
How it is put together · 实现
| half | mechanism |
|---|---|
| conversation | Client sessions.fork({ sessionId, atSeq }) + uiWorkspace.openSession, the same idiom as the built-in branch button; the draft is pre-seeded through the Conversation store's per-session localStorage key so the composer already holds the original prompt |
| files (capture) | fs/write-intent / fs/edit-intent waterfalls ({ prepend: true }), with the tool execution mapped back to its Session through a tools/pre-execute WeakMap — the same coupling workspace-changes uses |
| files (restore) | text pre-images through the fs service (path resolution, locks, version guard); binary pre-images and deletions through Node, on the archive's own recorded absolute path |
Where data lives · 数据位置
$DSH_HOME/plugin-data/dsh-session-rewind/
index.json per-session, per-Turn file records
blobs/<sha256>.bin the pre-Turn bytes
Nothing leaves the machine: the four Host routes are loopback-only,
same-origin /api routes with DSH Web's own Host/Origin trust fence.
GET /api/session-rewind.selfcheck reports what the running Host has actually
wired up (archive totals, whether the Web half is in the boot graph, the route
list). It is read-only and never exposes session content.
Limits · 限制
- Only mutations that go through the DSH filesystem service inside a Turn are
captured. Files changed by a shell command, by
git, or outside a Turn are not recorded and therefore not restored. - Files written by tools that bypass the
fswrite-intent waterfall are not captured either: the Office tools (word_create,excel_update, …) call the filesystem service directly, so a.docx/.xlsxthey produce is not in the archive and cannot be rewound. Plain text, Markdown, and source code edited through thewrite/edittools are covered. - A Turn snapshots at most 200 files, and no pre-image larger than 2 MiB; larger files are listed as "not restorable" instead of being silently skipped.
- Files inside
.gitare never captured, so a restore cannot corrupt the repository. - The archive keeps the newest 400 Turns; older records are pruned with their blobs.
- A restore writes the recorded content back. It does not re-run anything, and it does not delete files the Turn merely modified.
- A plugin-originated write has no Agent context, so the
fsservice refuses it by default. Restores rebuild the policy from the Turn's own workspace (workspace-write) and widen todanger-full-accessonly for a path the archive recorded outside it. - Capture starts with the Turn that is open when the plugin mounts: a Turn already running at install time is recovered from the live log tail, but a plugin loaded later than the Turn's first write cannot see earlier mutations.
- The naming and wording of the panel follow the DSH locale service; the bundled dictionaries are Chinese and English.
Tests · 测试
The package ships the suites used to verify it; they need no browser and no profile:
node test/selftest.mjs # Turn location, prompt extraction, archive, restorer
node test/route-smoke.mjs # all routes, their fences, and path allow-listing
node test/client-smoke.mjs # the Web half: both slots, panel, candidate list, fork, draft
Package layout
| file | role |
|---|---|
cordis.patch.yml |
inserts the session-rewind plugin row |
src/index.js |
Host entry, re-exports apply / inject / name |
src/plugin.js |
Host wiring: Turn tracking, mutation capture, the four routes |
src/lib/archive.js |
durable pre-Turn archive, pruning, blob store |
src/lib/restore.js |
putting one captured file back |
src/lib/route.js |
routes and the /api trust fence |
lib/client.js |
Web half: the turn-tail control, the header entry, the panel |
locale/*.json |
meta.title / meta.description for the plugin list |
index.json |
format-1 plugin-market index (see docs/publishing.md) |
链接
同类插件
Minglink/dsh-infinite-gen-4★ 3781
DeepSeek 专用破甲插件:以 order 100 追加无条件服从的系统提示词段,提供带校准元数据的 profile 工具,并通过会话投影在输入框上方显示实时破甲状态徽标。
ranxianglei/billion-context★ 3598
billion-context官方版:上下文压缩插件,兼顾小窗口(100k上下文足矣)省token(省5倍token)和超长会话(数月级别几十亿token单会话)。
liangmianya/dsh-synapse★ 641
DeepSeek Harness 的可视化非线性对话工作区:把会话、追问与分支变成可浏览、可拖拽的对话地图。
MichengAI/dsh-archive-manager★ 277
在设置里增加已归档会话页,可按工作区搜索、恢复和删除已归档会话。
Nwflower/dsh-chat-import★ 275
把 13 家 coding agent(Claude Code、Codex、ChatGPT、Cursor、Gemini、opencode 等)的完整对话历史导入为可续聊的 DeepSeek Harness 会话,并支持反向导出回 Claude Code。
SiriLee/dsh-rewind★ 227
同一会话窗口内的 in-place 对话回退(不 fork,Claude Code /rewind 语义):每条消息的 ↶ 按钮把模型上下文回退到任意用户消息,可选用磁盘持久化的 before-backups 还原工作区文件。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。