Skip to content

Releases: kyle123740/dsh-message-recall

v0.2.2 — 适配 DSH 0.2.1 线(预发布版本 semver 修正)

Choose a tag to compare

@kyle123740 kyle123740 released this 23 Oct 12:45

修复兼容性声明:原开区间 >=0.1.7-alpha.1 对 0.2.1-rc.2 求值为 false(预发布版 semver 规则),宿主会据此判 incompatible-version。现按线枚举为 ^0.1.7-alpha.1 || ^0.2.0-rc.1 || ^0.2.1-rc.1。已对照真实 0.2.1-rc.2 核心逐符号核过,三套自检全绿。

v0.2.1 — npm 包内的加载名修正

Choose a tag to compare

@kyle123740 kyle123740 released this 29 Sep 05:18

0.2.0 的 npm tarball 里带着旧的加载名(\cordis.patch.yml\ 的 row
ame\ 与 client bundle 的 _ModuleLoader_\ load id 还是 \dsh-message-recall\),从 npm 安装的用户会碰到插件加载失败(route 404)。两个引用都已改为 @kyle123740/dsh-message-recall\,HTTP 路径 /dsh-message-recall\ 不变。

v0.2.0 — 墓碑非空化 + migrate 动作 + 空内容兜底

Choose a tag to compare

@kyle123740 kyle123740 released this 29 Sep 04:46

修复一个真 bug(用户发现):墓碑原先写 \content: []\,在不跳过空 user 消息的 provider 上,整条请求被打回 400 \message content cannot be empty\ —— 表现为「换模型就报错」。DeepSeek 自家路由会跳过空消息,所以只在换 provider/模型时暴露。实测 SenseAudio(deepseek-v4.1-flash)捕获到 400。

三层防御:

  1. 墓碑非空化:占位文本「[已撤回]」。它不会出现在对话里(surface 替换节点不是 append-surface 事件),遮蔽也从不依赖它。
  2. 新增 \migrate\ 维护动作:把会话里残留的旧空墓碑替换成带占位符的新墓碑,保留原
    emoved\ 列表,客户端遮蔽推导不变。并且每次操作都顺带执行 —— 旧会话第一次被碰到就自愈。幂等。
  3. \�gent/pre-step\ 兜底:任何请求发出前,空 user 消息(空数组/空串/纯空 text 块)一律补占位文本 —— 连别的 producer 写的空消息也兜住。

另外:包名改为 @kyle123740/dsh-message-recall\(npm 上裸名 \dsh-message-recall\ 属于无关发布者,裸名发布不了);\inspect\ 新增 \legacy\ 标记;已对照 0.2.0-rc.1 桌面壳的内置核心验证兼容(格式仍 v4,surfaceOp 契约不变)。

v0.1.7 — 遮蔽失效修复(按钮在、行却还在)

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 04:01

症状

悬浮按钮正常,但被墓碑删掉的行依然可见 —— 就是「删除没生效」的观感。

根因(v0.1.4 修「按钮消失」时引入)

为了让一个晚到的服务不至于让插件完全不激活,我把 sessions 从 inject 里去掉,改成乐观读取:

const inject = ["slots", "locale"];
sessions: ctx.get?.("sessions") ?? ctx.sessions

未声明的服务无法通过 context 取到,于是会话事件窗口变成 undefined:

  • 行装饰不需要它 → 按钮一直正常;
  • 读墓碑需要它 → 删除从此静默地不再隐藏任何行;
  • v0.1.6 又去掉了占位行,连一点提示都不剩。

症状组合正好是「按钮在 + 墓碑被忽略」。

修复

  • sessions 重新写回 inject(这才是让 ctx.sessions 真正可用的原因),乐观读取只作兜底;
  • 事件窗口缺失时打一次性 console.warn,这类故障不会再无声无息;
  • 新增测试断言 inject 必须包含 sessions —— 把遮蔽依赖的这条声明钉住,避免以后因别的理由再被摘掉。

只涉及客户端半边:刷新页面即可生效。

顺带补的诊断工具

  • scripts/explain-session.mjs <日志> --rows:加载真实的 lib/client.js,用插件自己的 shadowRanges 算出「这条会话现在应该保留哪些行、隐藏哪些行」,不再靠人肉推断;
  • scripts/list-sessions.mjs <会话根>:列出最近会话的标题、事件数、失败轮次数、墓碑数 —— 用来把截图和日志对上号。

v0.1.6 — 删除后不留任何痕迹

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 03:36

行为变更(按你的要求):不再显示占位行。

原来那行「已删除此处及之后的全部内容 · 原文预览」既关不掉,又会把你想删掉的文本片段长期留在对话里。

  • 占位层整个移除:没有提示行、没有原文预览、没有「复制原文 / 重新编辑」,对应的文案与样式一并删掉。留下来的就是功能本身 —— 行被隐藏。
  • 遮蔽行为不变:仍以墓碑的 surfaceOp.startSeq/endSeq 为准(deleteFrom 覆盖到墓碑自身为止),刷新与重启表现一致。
  • 孤立操作栏规则保留:某一轮的内容行全没了,它的操作栏也一起去掉。
  • _MCR_DEBUG_\ 的字段
    otes\ 改名 \ ombstones\。
  • README(中英)新增「删除后在界面上留下什么」:什么都不留;并说明被删文本仍在 append-only 会话日志里,可用仓库自带的 \explain-session.mjs\ / \scan-replace.mjs\ 只读脚本找回。
  • 客户端测试改成反向断言:不得渲染任何占位行、被删文本不得出现在 DOM 里、清掉墓碑后所有行恢复。

只涉及客户端半边:刷新页面即可生效。

v0.1.5 — 插件卸载时清掉注入的 DOM

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 03:13

0.1.4 的后续:悬浮按钮簇与占位行都是直接插进官方消息行的原生 DOM,插件卸载时没人清理它们 —— 于是会出现「占位行还在、它描述的那些行却回来了」这种自相矛盾的画面。

  • teardown 现在会移除本插件注入的所有元素(.dsh-mcr-tools\ / .dsh-mcr-note\ / .dsh-mcr-error\):插件卸载(设置里开关)与控制器卸载(切换会话)两条路径都覆盖
  • 回归测试:卸载后不残留任何按钮簇与占位行
  • 顺带修 \scripts/scan-replace.mjs\:它检测墓碑时还在看 0.1.3 之前的 \source.plugin\ 字段,导致真实墓碑被当成普通 replace 报告

v0.1.4 — 切换插件后悬浮按钮消失(修复)

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 03:01

症状:提示词与 AI 回复上的「撤回 / 删除」悬浮按钮消失。

原因(我自己的回归):v0.1.2 加了一个页面级 apply 守卫(DSH_MESSAGE_RECALL_APPLIED)防止同一份 bundle 被加载两次时重复注册槽位 —— 但它永不复位。在设置里 disable/enable 插件时,客户端会卸载后重新 apply,守卫直接返回,槽位就再也注册不回去,按钮消失直到整页刷新。我排查持久化 bug 期间反复切换过插件,正好踩中。

修复

  • 守卫由它自己的 teardown effect 释放:卸载→重新 apply 能正常注册,同时两个同时存活的副本仍会去重。
  • 行装饰不再依赖 sessions 查询:sync() 原本在拿不到会话面时整段早退,把操作按钮一起带走了。现在只有「占位行」需要事件窗口。
  • sessions 改为防御式读取,不再写进 inject,避免因一个可选服务晚到导致插件完全不激活(=没有按钮)。
  • MCR_DEBUG 新增 eventSource / decoratable 两个字段,「没有按钮」和「没有占位行」从此一次粘贴就能区分。
  • 两条回归测试:dispose 后重新 apply 能再注册;没有会话面的控制器仍然装饰行。

这一步只涉及客户端半边:刷新页面即可生效。Host 半边(v0.1.3 的 source-kind 落盘修复)仍需重启一次桌面版。

v0.1.3 — 删除现在真的落盘(v4 source-kind 修复)

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 02:53

修复:删除现在真的会落盘(0.1.3)

症状:在任意会话(包括全新会话)里删除消息 —— 行立刻消失、占位行出现 —— 重启桌面版后内容在界面和模型上下文里都回来了。

根因(先解码磁盘日志、再把墓碑送进真实持久化后端往返,两步定位):

// dsh-session-format-v3-to-v4
if (typeof value["kind"] !== "string" || value["kind"].length === 0 || value["kind"] === "plugin")
    throw new SessionFormatError("format v4 message requires a producer-owned source kind");

墓碑原先写成 source: { kind: "plugin", plugin: "message-recall", … }。Session.append 不校验这一项,所以事件能进内存日志、客户端拿到它并把占位行画出来;但 flush 时持久化编码器对整批抛错,一条都没写进磁盘。重启从磁盘重建,删除就蒸发了。compaction 的替换不受影响,是因为它声明的是已注册的 compact-checkpoint —— 这也是我一开始误判「机制没问题」的原因。

修法:墓碑改声明 kind: "user"(它本来就是 user 角色的空消息;v4 里 user/message 不要求处于开启的 turn/step 内,所以追加在 turn/end 之后合法),插件身份放到 producer: "message-recall"。客户端两种形状都认,页面上残留的旧墓碑不会失效。

本次改动

  • 新增 scripts/verify-persistence.mjs:把墓碑写进真实 @deepseek-ai/dsh-session-persistence-jsonl 后端的临时目录 → flush → 从磁盘读回,断言「事件全数往返 / 墓碑在盘上 / replace 语义保留 / 重放 surface 与实时一致 / 重开会话不再派生被删文本」,外加一条负向断言把 kind: "plugin" 会被拒绝这件事钉死。npm test 现在三套一起跑。
  • 只读 inspect 动作:不需要活着的 Agent,同时报告「内存日志」与「磁盘日志」(事件数、maxSeq、所有 replace 事件及其 source、按 seq 的类型流水)。这次就是靠它把范围收窄的。
  • 中英 README:写清 v4 的 source-kind 约束、它为什么制造出「重启前正常」的假象,以及这个诊断接口。
  • 移除 profile 侧的热更新行:两个模块实例同时注册 /dsh-message-recall 会被 webserver 以 duplicate exact route 拒绝,导致整条目激活失败。现在只保留插件自带的包名行,Host 改动一律靠重启生效。

升级

dsh plugin --profile desktop add github:kyle123740/dsh-message-recall#v0.1.3

必须重启一次桌面版(Host 半边的模块在进程内缓存,改动不会热加载),然后刷新界面。重启后之前那些「删了又回来」的消息需要重新删一次 —— 旧墓碑从未落盘,磁盘上没有可迁移的东西。

v0.1.2 — 诊断钩子硬化 + bundle 版本自查方法

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 01:36

把「到底在跑哪一版」变成两端都能自查的事。

为什么需要:上一轮排查里,我确认了 Host 已经在发新 bundle,而页面 Console 的 __MCR_DEBUG__ 是 undefined —— 这两件事同时成立只有一种解释:页面没重载。但如果诊断钩子在提前返回时什么都不写,undefined 就有两种读法(没跑到 / 跑了但放弃了),排查就会往错的方向走。

改了什么

  • __MCR_MOUNTED__:控制器一渲染就打标,值是版本戳。
  • __MCR_DEBUG__:现在连早退路径也会写,带 blocked: "no sessions service" | "no document"。于是 undefined 只剩一种含义 —— 这份 bundle 没在跑。
  • README 新增「服务器到底在发哪一版 / Which build is the server actually sending」:Host 用 combo 路由 /plugins/??<id>/client.js&rev=<rev> 发插件客户端,rev 是 sha1("plugin-artifact" \0 mtimeMs ctimeMs size) 的前 12 位(dsh-client-modules 的 artifactRevision,只取文件元数据、不哈希内容)。据此能把「Host 没重读文件」和「页面没重载」这两种完全不同的原因分开,不用再靠截图争。

功能面与 v0.1.1 一致:遮蔽区间仍以事件自带的协议字段 surfaceOp.startSeq/endSeq 为准,source.removed 只作补充。

自检:node scripts/verify.mjs 与 node scripts/verify-client.mjs 全绿。

v0.1.1 — 重启后内容回来(重载路径遮蔽失效)修复

Choose a tag to compare

@kyle123740 kyle123740 released this 27 Sep 01:04

修复:\删除此处及之后\ 点完当场生效、重启后内容又回来。占位行还在、遮蔽没了 —— 因为遮蔽区间原先只信墓碑 \source.removed\(插件自定义字段),而历史分页是日志的再编码投影,正是这类字段会出问题的地方。现在区间取自事件自带的协议字段 \surfaceOp.startSeq/endSeq\,\source.removed\ 只作补充。

新增:\window.MCR_DEBUG\ 快照(版本戳、墓碑、每行解析到的 seq 与遮蔽判定),出问题时贴一行 JSON 即可定位。中英 README 已补「出问题时先看这个」章节,并加了重载路径的回归用例(
pm test\)。