给 DeepSeek Harness 用的消息撤回:把会话退回到某条
30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/26 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add JochenYang/dsh-rewind-plugin该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · chat
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 8 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件发起外部网络请求
查看扫描证据(共 42 处,展示前 19 条)
- 读取本地文件lib/index.js:156
const info = await stat(path); - 读取本地文件lib/index.js:159
return { kind: "captured", before: await readFile(path) }; - 写入本地文件lib/index.js:191
await mkdir(slot, { recursive: true }); - 写入本地文件lib/index.js:192
const writeManifest2 = (kind) => writeFile( - 写入本地文件lib/index.js:199
await writeFile(join(slot, "created"), present); - 删除本地文件lib/index.js:201
await rm(snapshot.path, { force: true }); - 写入本地文件lib/index.js:205
await writeFile(join(slot, "replaced"), present); - 写入本地文件lib/index.js:208
await mkdir(dirname(snapshot.path), { recursive: true }); - 写入本地文件lib/index.js:209
await writeFile(snapshot.path, snapshot.before); - 读取本地文件lib/index.js:348
const bytes = await readFile2(blobPath(dshHome, hash2)); - 读取本地文件lib/index.js:374
entries = await readdir(dir, { withFileTypes: true }); - 读取本地文件lib/index.js:385
info2 = await stat2(abs); - 读取本地文件lib/index.js:412
info = await stat2(abs); - 删除本地文件lib/index.js:573
await rm2(manifestPath(dshHome, sessionId, turn), { force: true }); - 删除本地文件lib/index.js:583
await rm2(manifestPath(dshHome, sessionId, turn), { force: true }); - 删除本地文件lib/index.js:629
await rm2(manifest.path, { force: true }); - 删除本地文件lib/index.js:671
await rm2(manifest.path, { force: true }); - 删除本地文件lib/index.js:690
await rm2(blob.path, { force: true }); - 发起网络请求lib/client.js:692
const response = await fetch(url, { credentials: "same-origin", cache: "no-store", ...init });
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-ui-chat@deepseek-ai/dsh-client-ui-commands@deepseek-ai/dsh-client-ui-conversation@deepseek-ai/dsh-client-ui-input-trigger@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-client-ui-renderer@deepseek-ai/dsh-client-ui-session@deepseek-ai/dsh-client-ui-settings@deepseek-ai/dsh-client-ui-slots给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 撤回已发送消息
- 文本回填输入框
- 改动文件可恢复
README
由 DeepSeek 最新模型翻译生成dsh-rewind-plugin
English · 简体中文
给 DeepSeek Harness 用的消息撤回:把会话退回到某条已发送消息之前。
已发送的消息改不了也收不回。这个插件把模型可见的对话表面裁到某条用户消息之前:这条消息及其之后的内容退出模型上下文、从对话界面隐藏,原文回到输入框,可以改完重发。被撤回的那些回合改动过的文件会逐字节恢复原样,而每一个被移走的字节都留在隔离区里,可以查看、可以清空。如果这次撤回没能覆盖全部改动,结果会如实说明,而不是谎称已经完整还原。
效果
撤回前的确认框:会改动 1 个文件,附改动前后的差异;被移走的字节进隔离区
撤回前的确认框:这次撤回会改动哪些文件、每个文件的真实前后差异,以及挡路的那些字节最后去了哪里。
不会删除任何东西。
展开完整文档(README 剩余 1 节)
dsh-rewind-plugin
English · 简体中文
给 DeepSeek Harness 用的消息撤回:把会话退回到某条已发送消息之前。
已发送的消息改不了也收不回。这个插件把模型可见的对话表面裁到某条用户消息之前:这条消息及其之后的内容退出模型上下文、从对话界面隐藏,原文回到输入框,可以改完重发。被撤回的那些回合改动过的文件会逐字节恢复原样,而每一个被移走的字节都留在隔离区里,可以查看、可以清空。如果这次撤回没能覆盖全部改动,结果会如实说明,而不是谎称已经完整还原。
效果
撤回前的确认框:会改动 1 个文件,附改动前后的差异;被移走的字节进隔离区
撤回前的确认框:这次撤回会改动哪些文件、每个文件的真实前后差异,以及挡路的那些字节最后去了哪里。
不会删除任何东西。
它带来什么
| 位置 | 说明 |
| 每条消息上的撤回图标 | 把鼠标停在对话里的一条用户消息上 |
| /rewind | 留空列出可撤回的消息;/rewind <n> 撤回第 n 条;/rewind plan <n> 只看预览不动手;/rewind conversation <n> 只撤回消息、文件保持不动(确认框里也提供同样的选择) |
| 输入框回填 | 被撤回消息的原文回到输入框,一次撤回只回填一次 |
| 设置 → 撤回隔离区 | 设置里独立的一页:按会话分组列出每个被移走的文件,逐条或整段清空,清空前有一次确认 |
隔离区这一页注册在 settings.section——设置外壳自己留给功能插件的座位,所以它和内置的「通用 / 模型 / 插件」并列。没有设置外壳的 profile 只是少这一页,其余功能不受影响。
撤回会在最近一次压缩处停下:压缩把被总结的历史换成一条 checkpoint 消息,它之前的提示不再是模型可见表面上的节点,列表也就无法提供它们。/rewind 会把这件事说出来:列表、越界回答、选择器和确认框都会点名这条压缩边界,而不是只给一个更短的列表。
安装
从 registry 装
dsh plugin --profile web add @jochenyang/dsh-rewind-plugin
或者直接用 Release 里的 tgz,不经过 registry
dsh plugin --profile web add https://github.com/JochenYang/dsh-rewind-plugin/releases/latest/download/dsh-rewind-plugin.tgz
要固定版本,把 latest/download/<文件名> 换成 download/v<x.y.z>/dsh-rewind-plugin-<x.y.z>.tgz (需要记录下载地址时用这个形式,链接本身就说明了它指向哪个构建)
或者从仓库装,安装时自动构建
dsh plugin --profile web add github:JochenYang/dsh-rewind-plugin
并把包名追加到 dsh.profile.bundles;重启这个 profile 才会加载它。
官方桌面端没有对应的命令行。 它的 desktop profile 由 Electron 应用独占,CLI 会直接拒绝(error: profile "desktop" is managed exclusively by the Electron application)。请在应用内安装: 设置 → 插件 → 添加插件,在「包名或地址」里填 @jochenyang/dsh-rewind-plugin,安装后重启应用。
这个输入框同样接受 Git 地址、本地目录和 .tgz 压缩包,界面也会提示「已安装,下次启动后加载」。
并把包名追加到 dsh.profile.bundles;重启这个 profile 才会加载它。
peerDependencies 里刻意不写任何 @deepseek-ai/dsh-:安装器会把每一个这类 peer 与当前运行版本做比较,不满足就拒绝安装,那等于把这个插件限制在某一个 DSH 版本上。而这个插件运行起来不需要额外安装任何东西——下面列的运行时包在任何 DSH profile 里都已经存在——所以跳过这项校验,任何运行版本都能装上。
代价是明确的:将来某个 DSH 版本改了这些接口,插件会装上并在调用时失败,而不是在安装时被拒。
为什么不会丢东西
会话日志只追加,它始终是唯一的事实来源。一次撤回只追加一条带
surfaceOp: { op: 'replace', startSeq, endSeq } 的事件,所以日志里每个事件都还在;不理会 surface op
的读取方看到的仍是完整对话。
文件从不被删除。挡路的那些字节先被移进 $DSH_HOME/storages/dsh-rewind-plugin/quarantine/<会话>/<槽位>/(连同写明原路径的 manifest.json),然后才写回改动前的版本。被撤回区间新建出来的文件也是移进去,而不是删掉。
撤回需要知道这个区间的起点长什么样,所以插件会在一个回合里第一个「可能写入」的工具之前,给工作区拍一份有上限的副本,按内容寻址存到 $DSH_HOME/storages/dsh-rewind-plugin/{trees,blobs}。命令行命令改写过的文件之所以能还原,靠的就是这份副本:write / edit 这两条瀑布仍然提供它们能看到的精确改动前镜像,而快照覆盖到的路径以快照的字节为准。
给其他插件
撤回会改动会话表面与工作区。任何镜像过某个回合的插件——看板、归档、任务清单——也得把自己的状态收回来,所以宿主这一半发布了 dsh-rewind-participants 服务。在自己的 inject 里声明它,调用
register({ id, reconcileAfterRewind }),之后每次撤回都会在写入会话表面之后按注册顺序运行你的参与者。
抛错或超过 30 秒截止时间的参与者会被记进日志并跳过,不会让撤回失败;命令按 id 收集每个参与者的结果(随结果返回的是文件还原的那份报告)。文件还原本身就是第一个参与者,这条接缝就是插件自己正在走的路。
运行时需要什么
从 profile 里解析,不打包进产物:
@deepseek-ai/cordis、@deepseek-ai/dsh-commands、@deepseek-ai/dsh-session、 @deepseek-ai/dsh-session-projection、@deepseek-ai/dsh-llm、@deepseek-ai/dsh-home-paths、 @deepseek-ai/dsh-client-connection、@deepseek-ai/schemastery、zod。
浏览器半边只需要 react、react/jsx-runtime、react-dom/client 与 @deepseek-ai/dsh-client-ui-primitives,这四者由外壳自己的模块表提供。它们不写进 dsh.client.external,因为外壳本来就能回答。
使用注意
- 一个会话同时只有一个写入者。 DSH 对每个会话持有一把跨进程独占写锁,直到写句柄关闭才释放。
因此共用同一个 $DSH_HOME 的两个 DSH 进程无法同时写同一个会话:后者的命令执行会被 session/writer-held 拒绝,撤回则会报「请求没能送到内核」。第二个实例读同一个会话没问题,不同会话之间也从不冲突——把那个会话在另一个窗口里关掉,或者换一个会话继续。
- 撤回需要 agent 处于空闲。正在跑的回合会被直接拒绝而不是去抢,因为从回合中间裁剪会让该回合后面的工具结果失去对应的调用。
已知限制
- 由本插件观察不到其写入的工具改动的文件——命令行命令、模型跑的脚本、渲染器——由工作区快照负责,而不是那两条写入瀑布。快照是有上限的(文件数、总字节、单文件字节、截止时间,以及工具调用的 abort signal),所以特别大的目录树、读不了的文件、符号链接或超时的遍历都可能留下没覆盖到的路径。每次撤回都会报告覆盖度——full、partial 或 none——连同原因和涉及的写入工具,缺口是看得见的,而不是静默的。
- 依赖、版本控制与缓存目录(node_modules、.git 等)故意不在覆盖范围内,所以跑过 npm install 的撤回不会报告依赖树;而 dist/ 这类构建产物是会被遍历的。
- size + mtime 都没变就当作没变。一个把旧 mtime 恢复回来、大小又刚好相同的写入者会被漏掉——这是所有同步工具都要做的取舍。
- 占位符是一条内容为空的 user/message。DeepSeek 的请求构造会丢掉空的用户消息,所以它在那边不产生面向模型的回合;换成不丢空用户内容的 provider,一次撤回会多出一条空的用户消息。
开发
npm install
npm run build # 产出 lib/index.js + lib/client.js
npm run typecheck
npm test # 用 esbuild 打包 tests/.test.ts,再跑 node --test
npm run build 把 @deepseek-ai/、@cordisjs/、react、react-dom、zod 标为外部依赖,并把浏览器半边包进 web 客户端要求的 window.__ModuleLoader__.load({ id, factory }) 闭包。不要把任何 @deepseek-ai/* 打进产物:第二份拷贝会让类身份对不上运行中的宿主。
lib/ 已入库,所以 dsh plugin add github:JochenYang/dsh-rewind-plugin 不需要构建就能装——
pnpm 默认拒绝运行 git 依赖的构建脚本,除非该 profile 把它加进允许名单。改过 src/ 之后跑一次
npm run build;prepublishOnly 会在发布到 registry 前做同一件事。
目录
| 路径 | 内容 |
| src/index.ts | host 半边:/rewind 命令、写入记录器、快照与回滚 |
| src/cut.ts | 撤回计算:锚点、替换范围、占位符 |
| src/projection.ts | rewindAnchors 投影,客户端全部的读取面 |
| src/routes.ts、src/quarantine.ts、src/snapshots.ts | 隔离区的 HTTP 路由、列表与清空、改动前的镜像 |
| src/trees.ts | 工作区快照存储与它的保留策略 |
| src/recorder.ts | 哪个文件改动属于哪个回合 |
| src/wire.ts | 两半共用的载荷形状与错误码 |
| src/client.ts | 客户端半边:图标、选择器、回填、隐藏回合、设置页 |
| src/client/ | 组件、中英文案、注入的样式表 |
| cordis.patch.yml | 挂载插件的唯一一行 insert |
更新日志与版本号
每个版本都记在 CHANGELOG.md 里,格式遵循 Keep a Changelog 1.1.0;版本号遵循 语义化版本 2.0.0。中文版即本文件对应的 CHANGELOG.zh-CN.md。
许可
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
JochenYang/dsh-rewind-plugin
给 DeepSeek Harness 用的消息撤回:把会话退回到某条
相关插件 · 对话 / 记忆
上下文中枢oceanbase/powercontext
上下文跟着工作走 —— 跨会话、跨智能体、跨人。
★ 1,487已汉化DSH
73
智能体记忆库plastic-labs/honcho
Honcho 是记忆基础设施,用来构建有状态的智能体。这些智能体能理解不断变化的人、智能体、群组、项目和想法。
★ 7,894已汉化DSH
71.7
长程智能体控制面loopx-project/loopx
给 Agent 一个目标,让工作持续向前。
★ 6,358已汉化DSH
71.3
智能体上下文数据库volcengine/OpenViking
给 AI 智能体用的上下文数据库
★ 41,379已汉化DSH
71.3
长效机器人团队Fei2411/DS-Bot
DS Bot 是一个基于 DeepSeek Harness简称 dsh的 bot 插件,插件将 dsh 改造为 bot…
★ 5,084已汉化DSH
71.1
可携带记忆层EverMind-AI/EverOS
为什么选择 EverOS
★ 13,778已汉化DSH
71.1