dsh-reply-forge-v3

sessiontested: passnpmMIT

Edits assistant replies and thinking chains in DSH sessions so the model sees the edited text, live via message projection or on disk.

下载量 / Downloads
1,052
依赖体积 / node_modules
0.29 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-reply-forge-v3

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

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

README

dsh-reply-forge v3

[English](./README.en.md) · 简体中文
把 AI 说过的话改成你想让它说的,改完立即生效,不用重启。
思维链也能一起改。

DSH 桌面端(DeepSeek Harness)插件。会话里每条 AI 回复旁边多一个按钮,

点开就能改正文或思维链;改完写进去,这个会话之后的每一轮,模型都只认你改过的版本。

已收录 awesome-dsh-plugin(DSH 生态精选列表,PR #7845 已合并)。

和 v1 / v2 的区别

· v1 · v2 · v3

改的是什么 · 页面显示 · 会话落盘文件 · 活会话对象

模型看得到吗 · 看不到 · 看得到 · 看得到

生效时机 · 立即(只对本机) · 要重启 DSH · 立即

能改思维链吗 · 不能 · 不能 · 能

实现 · localStorage 覆盖 · 改写 .jsonl.zstd · message projection

v3 把 v2 的落盘改写包成了兜底分支:会话在内存里就直接写活会话(立即生效),

不在内存里才落盘。

前两代是独立仓库,与 v3 完全独立(独立插件 id / 前缀,可分别启用):

三代可以分别启用,但不要同时启用——它们共用同一条消息操作条,同时开会挂出多条
(安装脚本默认会把更早的两代从 bundles 摘掉,见下)。

怎么用(快速版)

1. 鼠标移到任意一条 AI 回复上,点「伪造」按钮;

2. 在编辑器里选目标(可选本会话任意可编辑块——包括页面上看不见、折叠着的思维链)、

选写入方式(默认「自动」);

3. 改文本,点写入伪造。写入前会弹一次确认(复述这次会怎么生效),写完成功立刻可见。

改过的块还能后悔:点编辑器里的「历史版本」,能看到这个块的完整版本链

(原稿 → 每一次改写),选中旧版「还原到此版」→ 内容填入编辑器 → 再点一次

「写入伪造」即可。思维链同样有这条链。

两个出口:仅本机渲染(Ctrl+Enter,只改这一页的样子、模型看不见、可逆)与

写入伪造(改「模型看到的对话」,在线写活会话立即生效 / 落盘重启后生效,不可逆)。

完整的界面说明、写入方式对照表、常见问题见 [使用说明.md](./使用说明.md)。

看效果

写入成功,下一轮立即生效 · 思维链 · 三种写入方式

![写入活会话](./docs/screenshots/01-write-into-live-session.jpg) · ![编辑思维链](./docs/screenshots/02-edit-thinking-chain.png) · ![写入方式](./docs/screenshots/03-write-modes.png)

三张连起来是一个真实片段:把一句 AI 回复改成「因为我是吃白饭的大肥鱼。」写进去,

块上立刻亮绿色回执——「下一轮对话模型就看到改后的内容(不用重启),焦点已交还输入框」。

下一轮随口问一句「你去干嘛」,模型接的是「虽然是个吃白饭的」——它真的信了。

中间那张是思维链的编辑面板:目标下拉里的 seq=38 块0 就是页面上折叠着、看不见的那串

内心戏,和正文一样能改。右边那张是写入方式三选:自动(会话在内存里就直接写活会话、

不在才落盘,推荐)、仅在线(立即生效)、仅落盘(重载后生效)。

关掉插件会不会把会话弄坏

标记齐全就不会——这个「不会」有前提。

reply-forge/patch 是宿主不认识的事件类型,宿主持久化层的准入闸门

validateStoredEvents 规定:未知类型且信封没带 ignorable: true → 抛错、

整条日志拒读(会话打不开);投影层(插件在场时)对不认识的事件则直接跳过、

不报错,伪造退回原文。宿主自己的注释写明了这条路(known-event-types.js):

The persisted SessionEvent.ignorable marker is the compatibility mechanism.

于是分三种情况:标记正确 + 冷启动(插件不在)→ 会话照常打开、伪造静默退回原文;

缺标记(早期写入或写盘损坏)→ 被闸门拒读,用修复工具补标记;**插件在 DSH

运行时被热卸载** → 宿主抛 `session message projection "reply-forge/patch" was

removed`(所以正常用法都要求重启 DSH,重启后干净重放,碰不到这条)。

v3 的 P0 工作就是把「标记正确」钉死:在线/落盘两条写路径进入宿主持久化前统一补

ignorable: true;每次真写前核验写句柄(WRITER_NOT_ROUTED 可安全回落盘、

APPEND_UNSAFE 一律拒绝,失败码表见 [PLAN.md](./PLAN.md));真宿主持久化闭环

测试 create → append → flush → 重读 → 闸门放行全过。

致谢与借鉴

dsh-chat-thinking-editor

是本插件重要的学习对象——它是 DSH 生态里最早做「思维链编辑」的探索者,

「改完立即生效」这个目标与它对用户的坦诚(PR 说明里明确写

「编辑过的会话包含插件自有事件,卸载插件后该会话可能无法读取」)都给了我们

直接的启发。两条路线的差别在技术选择:它早期版本把编辑挂在 assistant/message

上并带 surfaceOp,那类事件在插件缺席时仍会被宿主做 surface 校验,校验不过就抛;

v3 因此走了另一条路——自有事件 + 同类型 projection + ignorable 标记

(见上一节)。没有高下之分,只是约束下的不同取舍;站在它的肩膀上,

我们把「卸得干净」这件事做得更彻底了一点。

万一会话已经被弄坏:退出 DSH,双击 修复会话.bat 先看诊断(只读,不动文件),

确认后再 修复会话.bat apply 补上 ignorable: true——工具先备份、只改有毒行、

写完自读复验、修不好自动还原、修不了的旧伤明确报出而不是硬改。关插件的完整说明与

安全排障见 [使用说明.md](./使用说明.md)。

安装

前提:已装 DSH 桌面端。两条路线选一条,装完都要完全退出并重启 DSH

(静态配置只在启动时加载)。

路线一:npm(最省事,不需要额外装 Node.js)

在 DSH 的「插件管理器」里输入包名 dsh-reply-forge-v3 点安装;或者关掉 DSH 后

在终端执行:

dsh plugin --profile desktop add dsh-reply-forge-v3

插件管理器还支持切换安装源(npm 官方源 / 中国大陆镜像源 / 自定义地址),

内网或私有源环境也能装。

路线二:zip 解压(需要 Node.js)

双击 安装插件.bat。

安装/启用/关闭/修复四个 bat 靠 Node.js 改静态配置——bat 会自动探测 PATH、

C:\Program Files\nodejs、%LOCALAPPDATA%\Programs\nodejs 与 DSH 自带的 node

(%USERPROFILE%\.dsh\dsh-runtimes\...\node\bin\node.exe,装了 DSH 就有)共四处,

一般装过 Node 或 DSH 就能直接用。没有 Node 也不挡路:bat 会印出完整的手动四步

(改 package.json / cordis.patch.yml / 建 junction),照做即可。

插件本身跑在 DSH 内部,不要求你额外装运行时。

脚本只做三件事:建 junction、写 package.json 的 bundles 与 dependencies、

手工登记 lockfile 条目。它不执行 pnpm install —— 在 DSH 运行时跑 pnpm

会清空 node_modules。

安装时会默认把更早的两代(dsh-reply-forge-v2、dsh-reply-forge)从 bundles

里摘掉:三代共用同一条消息操作条,同时开会挂出多条。它们的依赖和链接都保留,

随时可以用各自的「启用插件.bat」开回;想并存做对比就用

node scripts/install.mjs --keep-v2 --keep-v1。

其他脚本:

脚本 · 用途

安装插件.bat · 首次安装(含链接、依赖、lockfile、bundles)

启用插件.bat · 已装过但被关掉时,把插件加回 bundles

关闭插件.bat · 从 bundles 摘掉。启动异常时用它一键回滚

三个脚本都只改静态配置,都不碰 pnpm,都不会删你的覆盖数据;

改完都要重启 DSH(原因见上一节)。

工作原理

宿主不允许对 assistant/message 做 surface replacement ——

assertSourceEventReferences 要求 replace 事件用 sourceEventSeqs 覆盖全部被遮蔽节点,

同一个函数又硬性禁止 assistant/message 携带 sourceEventSeqs。两条约束互斥,

必然抛错。

宿主为此留的正门是 message projection:按事件类型注册的纯函数,折叠时决定

「某个 seq 的消息模型看到的是什么」。所以 v3 追加一条自有的

reply-forge/patch 事件(纯日志记录,不进 surface),再由同类型的 projection

把目标消息改写掉。

  • 事件不带 surfaceOp / sourceEventSeqs,所以不触发任何 surface 校验;
  • 插件被禁用时,事件被静默跳过,伪造退回原文;只要标记齐全,会话照常打得开;
  • 重启后伪造仍在:会话恢复用的是已注册的 projection 列表,而本插件在

apply() 里同步注册;

  • 在线写入的事件最终同样落进 session.v4.jsonl.zstd,所以「在线写的」和

「落盘写的」一样耐久。但宿主持久化层会校验事件类型:未知类型必须带

ignorable: true 否则拒读整条日志,而 Session.append() 不接这个字段——

所以 v3 在写路径上包了 shim,进入宿主持久化前统一补标记

(详见上面「关掉插件会不会把会话弄坏」)。

关于宿主升级(v3.4.1 起):宿主 0.2.1 在读 v3 世代的旧会话文件时会做格式迁移,

把「未知类型 + ignorable」的事件改名成 plugin:<原类型> 写盘——我们的

reply-forge/patch 会变成 plugin:reply-forge/patch。投影是按类型精确匹配的,

所以本插件两种类型都注册、读侧都接受(projectedTextsOf 投影基准与版本血统

推导同样双类型);新事件仍写旧类型,不触发改名。升级过 DSH 的老用户,旧会话里的

伪造与版本链都还在。

完整论证、宿主源码证据、失败码表见 [PLAN.md](./PLAN.md)。

三条写入出口,按代价从小到大

出口 · 改的对象 · 副作用

改待发送队列 · 还没发给模型的消息(inbox) · 无:消息未进日志,无新事件、不动已提交字节、sessionId 不变、provider 缓存不受影响

在线 patch · 会话日志里的历史消息 · 追加自有事件;改动点之后的 KV 缓存作废

落盘改写 · 会话记录文件 · 需备份还原;运行中的宿主不读盘,改了要下次载入才生效

仅本机渲染 · 只有页面看得见 · 模型看不见,纯障眼法

第一条是 v3.1 之后补的,走官方 `session.updateQueue(itemId, { kind: 'edit',

content: [...] })。入口挂在带 data-pending-steering` 的气泡上(回合进行中

以 steering 形式插入的消息;next-turn 排队消息不渲染气泡,用户看不见,所以

不提供入口)。

定位方式是文本回匹,这是宿主的限制不是偷懒:气泡 DOM 上没有任何 id 类

属性(只有 data-pending-steering / data-submission-echo 两个布尔),

itemId 只能拿气泡文本去比队列行文本。因此:

  • 匹到多条一字不差的相同文本

…(truncated)

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

Versions

Published on npm::4,最后更新 2026-10-26

Links

GitHub · Homepage · npm

Same category