dsh-recall-plugin 2.3.16 → 2.3.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/lib/client.js +1694 -1619
- package/lib/routes-core.js +2 -1
- package/lib/snapshots.js +53 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [2.3.20] - 2026-09-14
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
|
|
9
|
+
- **探针:`sessions.fork` 切点推进行为 3 例**(I35 的文档化行为落成机器断言):`tests/probe/api-surface.test.js` 新增「sessions.fork 切点推进行为」组,直钉 `dsh-api-session-controller` 构建产物的三条锚点——boundary 以「seq >= atSeq 的首条 `turn/end`」解析、`cut` 从 boundary+1 推进到下一个 `turn/start` 前、`seed` 取 `slice(0, cut)` 完整前缀。官方若改为「seed 排除未领取 inbox 项」(撤回残留排队消息的根治方向)或重构改名即红,提示复核 G1 清理逻辑是否可退役;0.1.1 旧版安装的实现锚点未核验,整体 skip(fork 签名探针仍覆盖旧包路径)。探针 31 → 34 例全绿,compat-audit I35 的「无直接探针」缺口随之闭环。
|
|
10
|
+
|
|
11
|
+
### 修复
|
|
12
|
+
|
|
13
|
+
- **与同页插件的全局名冲突(issue #14):client 产物顶层声明整体收进 IIFE 闭包**:client bundle 以 classic `<script>` 原文 serve,esbuild cjs 产物的全部顶层声明都会挂到 `window`——顶层 `var CSS`(样式表数组)直接覆盖浏览器原生 `CSS` 对象(`CSS.supports()` 不再是函数),同页的 dsh-image-gen 等插件一调即崩(`CSS.supports is not a function`),DSH 反过来拒绝加载对方。且顶层名共 23 个(`clockText`/`buildTree`/`sizeText`…),逐个改名堵不完。修复:build-client.mjs 打包格式从 cjs 改为 iife——整个 bundle 包进箭头函数作用域,除 `window.__ModuleLoader__.load` 调用外零全局泄漏,对未来新撞名免疫;loader 契约不受影响(`factory: (require) => {...}` 收参注册与 `require("react")` 运行时加载原样保留,esbuild 的 require→require2 改名照旧)。build-client.mjs 新增产物断言回归钉:format 意外回退 cjs 或出现顶层 var/function/class 声明时构建即红。
|
|
14
|
+
|
|
15
|
+
- **撤回残留排队消息的自动清理从未命中:改为按 item id 直删**(0.1.5-rc.1 + 2.3.19 真机复现定位):Host 侧解析与下发都正确(`/api/recall/execute` 响应带出窗口内入队项的 rpcId),失效段在客户端匹配——子会话输入框上方的残留卡片可稳定复现(刷新后仍在),30 秒轮询窗口内始终匹配不到队列行,随后弹「撤回前的一条排队消息未被自动清理…」toast 并打出 `console.warn`。改用官方 `updateQueue` 的寻址键直删:Host 侧 `scanStaleQueueItemIds` 取窗口内 user 来源入队项的 `inserted[].id`(即该消息的 message id)经 execute 响应的 `staleQueueItemIds` 下发;Client 侧对 fork 出的子会话逐项调 `updateQueue(itemId, { kind: 'remove' })`,删除队列快照匹配与 30 秒轮询(改为会话面未就绪时的 5 秒短等待,超时仍 warn + toast)。真机验收:子会话日志出现 `agent/inbox/spliced inserted=[] removed=1`(入队项当场移除),重复删除返回 `queue-item-not-found`,控制台零告警。读取链顺带加固:内存跳改用 `snapshotEvents()`(0.1.5-rc.1 的 `Session` 无 `events` 访问器,旧字段仍兼容),并把 `observeSession` 提到 `readSession` 之前。
|
|
16
|
+
|
|
17
|
+
## [2.3.19] - 2026-09-12
|
|
18
|
+
|
|
19
|
+
### 修复
|
|
20
|
+
|
|
21
|
+
> 本次为 2.3.17 起改动的首次 npm 发布:2.3.17 / 2.3.18 未单独发版,其全部改动随本版本一并上线。
|
|
22
|
+
|
|
23
|
+
- **2.3.17 的「排队消息」自动清理在真机上从未生效**(本机 `~/.dsh/sessions` 会话日志解压实证:两次撤回的残留排队项分别在 fork 后 92 秒与 9 秒才出现移除事件——均为手动删除,自动清理的重试窗口内子会话日志无任何队列事件)。两个静默失败面,各修一半:
|
|
24
|
+
- **Client 侧重试窗口太短**:队列快照走 control 帧,`fork` 解析后还要等 `open`/staging 完成才到达——实测可晚于数秒,原 10×200ms(2 秒)窗口全部落空后静默放弃,残留卡片一直挂着。修复:扩为 30 秒长轮询(250ms 间隔,命中即停);窗口耗尽仍无匹配时不再静默——`console.warn` 留排查痕迹,toast 提示「撤回前的一条排队消息未被自动清理,可点击该卡片右上角的删除按钮手动移除」(toast 按文本节流,同一文案 10 分钟至多一次)。
|
|
25
|
+
- **Host 侧解析只认 live 内存事件**:`resolveStaleQueueRpcIds` 此前在 `sessions.get()` 拿不到对象或其 `events` 字段缺失(版本漂移)时恒返回空数组,Client 收到空集合就什么都不做;而切点解析 `resolveCutSeq` 有磁盘降级链兜底、切点永远正常,恰好掩盖了这个缺失。修复:补齐与 `resolveCutSeq` 同款的两跳降级(`sessionQuery.readSession` → `observeSession`,后者覆盖 seeded 父会话——撤回链的父会话本身可能就是上一次撤回 fork 出的子会话;租约 `Symbol.dispose` 释放),两跳都失败才返回空数组。
|
|
26
|
+
- 顺带核验了匹配键契约:宿主队列帧构造(`queueItemsFromInbox` / `promptRpcId`)对所有 `user` 来源行透传 `source.rpcId`,seed 重放行同样携带,`placement: 'queued'` 与 `{ kind: 'remove' }` 动作均与官方 QueueDock 行为一致,无需扩展。
|
|
27
|
+
|
|
28
|
+
## [2.3.18] - 2026-09-12
|
|
29
|
+
|
|
30
|
+
### 修复
|
|
31
|
+
|
|
32
|
+
- **用户消息里的文件块被渲染成原始 JSON**:插件覆盖了整个用户节点渲染(`conversation.chat.node` keyed 渲染器),此前只对 text/image 两类块做呈现——图片走官方 `renderMessageImages`、文本走气泡,其余块一律落进 JSON 兜底 `<pre>`。于是带文档/表格附件(`type: 'file'`)的消息(含撤回后重绘的那些)整块显示成 `{"type":"file","attachment":{…}}` 原文,而官方渲染是文件卡片。修复:新增 `fileCardInfo` 纯函数(文件名 / 扩展名徽标——大写截断 4 字符、无扩展名回退 `FILE` / 体积文本 `6.7KB` 官方风)与 `.dsh-recall-filecard*` 样式,file 块渲染成与官方 `UserStyleBubble` 同形的卡片(品牌色徽标 + 文件名单行省略 + 「MD 6.7KB」元信息),按「附件在上、文本在下」的官方布局排布,并从 JSON 兜底中剔除。新增 4 例单测。
|
|
33
|
+
- **被撤回消息里的文件附件回填静默缺失**:官方 `session.attachment`(`readAttachment`)只服务图片——Host 侧按 `referencedImage` 找引用、`readImage` 取字节,file 块直接判 `ATTACHMENT_NOT_REFERENCED`;插件读不到字节就填不回输入框,此前静默跳过。修复:撤回成功后若被撤回消息含 file 块,toast 明示「文件附件无法自动回填(官方接口只支持图片回读),请重新选择文件」,不再让用户以为回填是完整的(文本与图片回填照旧)。
|
|
34
|
+
|
|
35
|
+
## [2.3.17] - 2026-09-12
|
|
36
|
+
|
|
37
|
+
### 修复
|
|
38
|
+
|
|
39
|
+
- **撤回后输入框上方凭空多出一条「排队消息」**:官方 `sessions.fork({ atSeq })` 的切点不是「切点事件本身」,而是从该 `turn/end` 推进到**下一个 `turn/start` 之前**的整段事件——排队投递的用户消息,其 inbox 入队事件(`agent/inbox/spliced`,`target: 'next-turn'`)必然落在「上一个 `turn/end`」与「领取它的那个 `turn/start`」之间,正好被复制进子会话 seed;子会话重建 inbox 后,QueueDock 就把这条本该随撤回消失的消息显示成排队消息,与回填到输入框的内容重复(本机 `~/.dsh/sessions` 会话日志解压实证:父会话 seq 93 `turn/end` → 94 入队 → 95 `turn/start` → 96 领取后移除 → 100 `user/message` 落日志,子会话 seed 恰止于 94)。修复分两半:Host 侧新增 `scanStaleQueueRpcIds` 扫切点窗口内的 inbox 入队项、取出 `source.kind === 'user'` 项的 `rpcId`(prompt 提交身份),经 execute 响应新增的 `staleQueueRpcIds` 下发;Client 侧 fork 出子会话并 open 后 `purgeStaleQueueItems` 读 `sessions.binding(childId).session.getSnapshot().queue`,按 `placement === 'queued'` + rpcId 命中逐项调官方 `updateQueue(itemId, { kind: 'remove' })`(即 QueueDock 的「删除排队消息」)。用 rpcId 而非内容比对——撤回后新发的消息带自己的 rpcId,不会被误删。queue 快照走控制流、fork 后可能晚一两拍才到,故做 10×200ms 有界重试;拿不到 live 事件(冷会话)或服务面缺失时静默放弃(残留项仍可在 QueueDock 手动删除),不影响回填与撤回主流程。新增 `tests/unit/snapshots-queue-residue.test.js`(6 例)与 `pickStaleQueueItemIds` 3 例,`routes-stale` 补 stub 与透传断言,compat-audit 新增 I35。
|
|
40
|
+
|
|
5
41
|
## [2.3.16] - 2026-09-11
|
|
6
42
|
|
|
7
43
|
### 变更
|