dsh-recall-plugin 2.4.11 → 2.4.13

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 CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
4
4
 
5
+ ## [2.4.13] - 2026-10-17
6
+
7
+ ### 兼容
8
+
9
+ - **dsh 0.2.1-rc.2 兼容(零破坏核验,无代码改动)**(升级核查见 [docs/compat-audit.md](docs/compat-audit.md) 头部 0.2.1-rc.2 段):rc.2 自身只有模型适配(DeepSeek-V4.1-Flash-1017)与 DeepSeek Search/Fetch API 两条新特性;跨 alpha.5 / rc.1 的累计变更中,rc.1 汇总版 Breaking(输入区 stats 拆分、移除 `invariant`、移除工具展示 `both`、Agent Team inbox 直投、API Key 优先级、子路径 `package.json`)均不作用于本插件。34 个关键包 `lib/` 逐字节 diff 仅 3 处非功能漂移(`dsh-client-modules` commit 字面量、`dsh-cordis-client-runner` slot 教学目录、`dsh-api-remotes` 账号 bonus schema);`sessions.fork`、slot 定义方、设置面、兼容门禁全部零漂移。
10
+ - **实装核验**:全局升级 `@deepseek-ai/dsh@0.2.1-rc.2` 后 `test:probe` **54/54**、`verify:host` 装配断言全过(inject=shell,sessions,agents、端点 13 项)、`check:dsh` 全绿(六条 `@deepseek-ai/dsh-*` peer 对 rc.2 全满足);`dsh.compatibility.dshReleases` 补 `0.2.1-alpha.5` / `0.2.1-rc.1` / `0.2.1-rc.2` 三项。
11
+
12
+ ### 文档
13
+
14
+ - `docs/reference/` 镜像按 tag `dsh-v0.2.1-rc.2` 九次重拉:13 源全部逐字节相同(首个零差异轮次),仅更新头部「归档日期 / 归档 dsh 版本」两字段。
15
+ - `docs/dsh-contract.md`(版本段 + 沿革 ⑩)/ `docs/compat-audit.md`(新增 0.2.1-rc.2 核验段)/ 新增 [docs/upgrade-assessments/dsh-0.2.1-rc.2.md](docs/upgrade-assessments/dsh-0.2.1-rc.2.md) 与 [docs/upgrade-assessments/dsh-0.2.1-rc.1.md](docs/upgrade-assessments/dsh-0.2.1-rc.1.md) / README 双语兼容范围与徽章同步至 0.2.1-rc.2。
16
+
17
+ ## [2.4.12] - 2026-10-16
18
+
19
+ ### 修复
20
+
21
+ - **用户消息正文在 dsh 0.2.0-rc.x 上静默空白(issue #23)——判据重写为「旧面闭包在场即旧面」**:旧实现以 `renderFactorySlot` 是否存在判新面,而 0.2.0-rc.2 已下发该函数、`conversation.user.*` 槽位却尚未引入(0.2.1-alpha.3 才引入)——正文渲染成空、零报错(气泡只剩内边距,插件动作行照常)。`renderMessageImages` 只存在于 ≤0.2.1-alpha.2,与新槽位引入版本互斥,以其判定两面才可靠;0.2.0-rc.2 归旧面自绘、正文恢复。判据与投影单源在新增的 `src/client/user-content.ts`。
22
+ - **新面内容渲染从「分片投影」收敛为「整条内容单入口」**:文本/图片/文件卡片/未知块/引用摘要一次交给官方 `conversation.chat.user.content` Factory(投影=官方 `contentParts` 三分派的逐字复刻;该函数官方未导出),与官方 `UserMessageNodeView` 同款调用(含 `interactive: true` 对齐)——插件只保留动作行(时间/复制/撤回),气泡外壳/文件卡片/JSON 块/文本四类自绘在新面退场,官方规则链(密钥脱敏等)自动跟随。
23
+ - **兜底不静默**:官方 Factory 返回空或抛错(槽位未注册的中间版本)时回落自绘(文本/文件卡片/JSON;新面兜底无图片入口,跳过)并打 warn——「探测通过但渲染为空」不再可能产出空白正文。旧面(≤0.2.1-alpha.2)行为不变。
24
+
25
+ ### 兼容
26
+
27
+ - **dsh 0.2.1-alpha.4 兼容(零破坏核验,无代码改动)**(升级核查见 [docs/upgrade-assessments/dsh-0.2.1-alpha.4.md](docs/upgrade-assessments/dsh-0.2.1-alpha.4.md) 与 [docs/compat-audit.md](docs/compat-audit.md) 头部 0.2.1-alpha.4 段):双 lockfile 包清单对比(第三方依赖零变化)+ 32 个关键包 `lib/` 逐字节 diff 确认插件消费/注册契约零漂移——`sessions.fork`(`dsh-session` 整包)、`dsh-shell`、`dsh-settings`、`dsh-session-query`、`dsh-session-persistence`、`dsh-cordis-host-runner`(host 插件加载器)、`dsh-client-ui-chat`(slot 定义方)等 22 包零变更。
28
+ - **本版唯一机制变更(`engines.dsh` 优先判定)不作用于本插件**:`dsh-app-boot` 兼容性门禁改为「manifest 声明 `engines.dsh` 则只查它并**短路** peerDependencies;未声明沿用 peer 判定」——本插件未声明 `engines.dsh`(`engines` 仅 `{node:">=20"}`)→ 走 peer 路径,六条 `@deepseek-ai/dsh-*` peer 对 0.2.1-alpha.4 全满足;**维持不声明**(多段逐线窗口无法用单段等价表达,声明即短路 peer 判定且放宽未验证版本,见 I43)。输入链「命令芯片」重构(`ui-conversation` 等 3 包,编辑器内部)不改变 `setDraft` / `createDrafts` / `releaseDraftAttachments` / `addAttachments` 签名与行为;三个 client 基础包(store / primitives / slots)内联化退出运行时树,对插件透明(bundle 仅 require react)。门禁:`test:probe` **53/53**、单测 **499/499**、client **93/93**、`verify:host` 装配断言全过、`typecheck`、`check:dsh` 全绿;`dsh.compatibility.dshReleases` 补 `0.2.1-alpha.4: compatible`。
29
+
30
+ ### 文档
31
+
32
+ - `docs/reference/` 镜像按 tag `dsh-v0.2.1-alpha.4` 重拉:13 源中 12 份逐字节相同、仅 `05-publish.md` 差异(新增 `engines.dsh` 版本声明段,+365 B)。
33
+ - `docs/dsh-contract.md`(版本段与沿革 ⑨)/ `docs/compat-audit.md`(新增 I43)/ `AGENTS.md`(I43 索引行)/ README 双语兼容范围与徽章同步至 0.2.1-alpha.4。
34
+
5
35
  ## [2.4.11] - 2026-10-15
6
36
 
7
37
  ### 兼容
package/README.en.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-blue)
8
8
  ![Build](https://img.shields.io/badge/pure%20JS-green)
9
9
 
10
- [![DSH](https://img.shields.io/badge/DSH-0.2.1--alpha.3-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.3)
10
+ [![DSH](https://img.shields.io/badge/DSH-0.2.1--rc.2-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-rc.2)
11
11
  ![DSH](https://img.shields.io/badge/DSH-Desktop-blue)
12
12
 
13
13
  Under any message you've sent, click "↶ Recall" — **your workspace files and the conversation history roll back to just before that message was sent**.
@@ -73,7 +73,7 @@ Prerequisites:
73
73
 
74
74
  - git CLI: without it the recall button won't appear (a notice shows at the top of the page); DSH itself keeps running.
75
75
  - Shell: PowerShell 5.1 / 7 on Windows; bash on Linux/macOS.
76
- - DSH version: `0.1.2-alpha.1` through `0.2.1-alpha.3`; later versions within a verified minor line are admitted automatically, while unverified new minor lines are blocked by the startup compatibility gate (window mechanics in the fold below).
76
+ - DSH version: `0.1.2-alpha.1` through `0.2.1-rc.2`; later versions within a verified minor line are admitted automatically, while unverified new minor lines are blocked by the startup compatibility gate (window mechanics in the fold below).
77
77
  - **0.1.7-alpha.1 is a breaking release** (the shell execution and settings interfaces changed); the plugin ships both seams side by side — the same release works on every 0.1.2–0.1.6 line and on 0.1.7+, with unchanged behaviour on older DSH versions.
78
78
  - `0.1.1-rc.2` and earlier are not supported: their client runtime lacks the `sessions`/`workspaces`/`uiWorkspace` services, so the plugin UI silently fails to render.
79
79
 
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![npm version](https://img.shields.io/npm/v/dsh-recall-plugin?style=flat-square&label=npm&color=3178C6)](https://www.npmjs.com/package/dsh-recall-plugin)
6
6
  [![npm downloads](https://img.shields.io/npm/dm/dsh-recall-plugin?style=flat-square&label=downloads&color=1F883D)](https://www.npmjs.com/package/dsh-recall-plugin)
7
7
  ![License](https://img.shields.io/badge/license-MIT-blue)
8
- [![DSH](https://img.shields.io/badge/DSH-0.2.1--alpha.3-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.3)
8
+ [![DSH](https://img.shields.io/badge/DSH-0.2.1--rc.2-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-rc.2)
9
9
 
10
10
  在任意一条你发过的消息下方点「↶ 撤回」——**工作区文件和对话历史一起回到那条消息发出之前的状态**。
11
11
 
@@ -70,7 +70,7 @@
70
70
 
71
71
  - git CLI:未安装时撤回按钮不出现(页面顶部会提示安装 git),不影响 DSH 运行。
72
72
  - shell:Windows 上 PowerShell 5.1 / 7 均可;Linux/macOS 需 bash。
73
- - DSH 版本:`0.1.2-alpha.1` 至 `0.2.1-alpha.3`;已核验 minor 线内的后续版本自动放行,未核验的新 minor 线会被启动期兼容门禁拦截(开窗机制见下方折叠块)。
73
+ - DSH 版本:`0.1.2-alpha.1` 至 `0.2.1-rc.2`;已核验 minor 线内的后续版本自动放行,未核验的新 minor 线会被启动期兼容门禁拦截(开窗机制见下方折叠块)。
74
74
  - **0.1.7-alpha.1 是破坏性版本**(shell 执行与 settings 面接口更换),插件内置双分支共存适配:同一发布同时兼容 0.1.2–0.1.6 各线与 0.1.7+,老版本 DSH 上行为不变。
75
75
  - `0.1.1-rc.2` 及更早不支持:那条线的客户端运行时没有 `sessions`/`workspaces`/`uiWorkspace` 服务,插件 UI 会静默不渲染。
76
76
 
package/lib/client.js CHANGED
@@ -1023,6 +1023,56 @@
1023
1023
  var appLog = createLogger("app");
1024
1024
  var recallNodeLog = createLogger("recall-node");
1025
1025
 
1026
+ // src/client/user-content.ts
1027
+ var USER_CONTENT_FACTORY = "conversation.chat.user.content";
1028
+ function projectUserContent(blocks) {
1029
+ const texts = [];
1030
+ const attachments = [];
1031
+ const extraBlocks = [];
1032
+ for (const block of blocks) {
1033
+ const b = block;
1034
+ if (b && b.type === "text" && typeof b.text === "string") texts.push(b.text);
1035
+ else if (b && b.type === "image" && b.attachment !== void 0) attachments.push({ type: "image", image: { attachment: b.attachment } });
1036
+ else if (b && b.type === "file" && b.attachment !== void 0) attachments.push({ type: "file", file: b.attachment });
1037
+ else extraBlocks.push(block);
1038
+ }
1039
+ return { text: texts.join(""), attachments, extraBlocks };
1040
+ }
1041
+ function userContentFacts(referenceLabels, skillNames) {
1042
+ const facts = {};
1043
+ if (referenceLabels !== void 0) facts.sessionLabels = referenceLabels;
1044
+ if (skillNames !== void 0) facts.skillNames = skillNames;
1045
+ return facts;
1046
+ }
1047
+ function resolveUserContentFace(props) {
1048
+ if (typeof props.renderMessageImages === "function") return "legacy";
1049
+ if (typeof props.renderFactorySlot === "function" && typeof props.loadImage === "function") return "factory";
1050
+ return "legacy";
1051
+ }
1052
+ function renderOfficialUserContent(renderFactorySlot, blocks, data, loadImage, interactive) {
1053
+ if (typeof renderFactorySlot !== "function") return { ok: false };
1054
+ const projected = projectUserContent(blocks);
1055
+ const content = {
1056
+ text: projected.text,
1057
+ attachments: projected.attachments,
1058
+ extraBlocks: projected.extraBlocks
1059
+ };
1060
+ if (data.referenceLabels !== void 0) content.referenceLabels = data.referenceLabels;
1061
+ try {
1062
+ const render = renderFactorySlot;
1063
+ const node = render(USER_CONTENT_FACTORY, {
1064
+ content,
1065
+ loadImage,
1066
+ facts: userContentFacts(data.referenceLabels, data.skillNames),
1067
+ interactive
1068
+ });
1069
+ if (node === null || node === void 0 || node === false) return { ok: false };
1070
+ return { ok: true, node };
1071
+ } catch (error) {
1072
+ return { ok: false, error };
1073
+ }
1074
+ }
1075
+
1026
1076
  // src/client/recall-node.ts
1027
1077
  function attachmentRefsFromBlocks(blocks) {
1028
1078
  const refs = [];
@@ -1559,61 +1609,46 @@
1559
1609
  setRecall({ stage: "idle" });
1560
1610
  };
1561
1611
  const bubbleChildren = [];
1562
- if (imageBlocks.length) {
1563
- if (typeof renderMessageImages === "function") {
1612
+ let officialFace = resolveUserContentFace({ renderMessageImages, renderFactorySlot, loadImage }) === "factory";
1613
+ if (officialFace) {
1614
+ const official = renderOfficialUserContent(renderFactorySlot, blocks, data, loadImage, true);
1615
+ if (official.ok) {
1616
+ bubbleChildren.push(React.createElement(React.Fragment, { key: "official" }, official.node));
1617
+ } else {
1618
+ officialFace = false;
1619
+ recallNodeLog.warn("user.content factory unavailable; falling back to own rendering:", official.error);
1620
+ }
1621
+ }
1622
+ if (!officialFace) {
1623
+ if (imageBlocks.length && typeof renderMessageImages === "function") {
1564
1624
  const render = renderMessageImages;
1565
1625
  bubbleChildren.push(React.createElement(
1566
1626
  React.Fragment,
1567
1627
  { key: "images" },
1568
1628
  render({ images: imageBlocks, align: "end" })
1569
1629
  ));
1570
- } else if (typeof renderFactorySlot === "function" && typeof loadImage === "function") {
1571
- const render = renderFactorySlot;
1572
- bubbleChildren.push(React.createElement(
1573
- React.Fragment,
1574
- { key: "images" },
1575
- render("conversation.chat.user.content", {
1576
- content: {
1577
- text: "",
1578
- attachments: imageBlocks.map((b) => ({ type: "image", image: { attachment: b.attachment } })),
1579
- extraBlocks: []
1580
- },
1581
- loadImage,
1582
- interactive: false,
1583
- facts: {}
1584
- })
1585
- ));
1586
1630
  }
1587
- }
1588
- for (let i = 0; i < fileBlocks.length; i++) {
1589
- const info = fileCardInfo(fileBlocks[i], t);
1590
- if (!info) continue;
1591
- bubbleChildren.push(React.createElement(
1592
- "div",
1593
- { className: "dsh-recall-filecard", key: "file-" + i },
1594
- React.createElement("span", { className: "dsh-recall-filecard-icon" }, info.ext),
1595
- React.createElement(
1596
- "span",
1597
- { className: "dsh-recall-filecard-body" },
1598
- React.createElement("span", { className: "dsh-recall-filecard-name", title: info.name }, info.name),
1599
- React.createElement("span", { className: "dsh-recall-filecard-meta" }, info.size ? info.ext + " " + info.size : info.ext)
1600
- )
1601
- ));
1602
- }
1603
- if (text !== "") {
1604
- if (typeof renderFactorySlot === "function") {
1605
- const render = renderFactorySlot;
1631
+ for (let i = 0; i < fileBlocks.length; i++) {
1632
+ const info = fileCardInfo(fileBlocks[i], t);
1633
+ if (!info) continue;
1606
1634
  bubbleChildren.push(React.createElement(
1607
1635
  "div",
1608
- { className: "dsh-recall-bubble", key: "text" },
1609
- render("conversation.user.text", { text, facts: {}, interactive: false, ellipsis: false })
1636
+ { className: "dsh-recall-filecard", key: "file-" + i },
1637
+ React.createElement("span", { className: "dsh-recall-filecard-icon" }, info.ext),
1638
+ React.createElement(
1639
+ "span",
1640
+ { className: "dsh-recall-filecard-body" },
1641
+ React.createElement("span", { className: "dsh-recall-filecard-name", title: info.name }, info.name),
1642
+ React.createElement("span", { className: "dsh-recall-filecard-meta" }, info.size ? info.ext + " " + info.size : info.ext)
1643
+ )
1610
1644
  ));
1611
- } else {
1645
+ }
1646
+ if (text !== "") {
1612
1647
  bubbleChildren.push(React.createElement("div", { className: "dsh-recall-bubble", key: "text" }, text));
1613
1648
  }
1614
- }
1615
- for (let i = 0; i < rest.length; i++) {
1616
- bubbleChildren.push(React.createElement("pre", { className: "dsh-recall-json", key: "rest-" + i }, JSON.stringify(rest[i], null, 2)));
1649
+ for (let i = 0; i < rest.length; i++) {
1650
+ bubbleChildren.push(React.createElement("pre", { className: "dsh-recall-json", key: "rest-" + i }, JSON.stringify(rest[i], null, 2)));
1651
+ }
1617
1652
  }
1618
1653
  const actions = [];
1619
1654
  actions.push(React.createElement("span", { className: "dsh-recall-time", key: "time" }, clockText2(data.time)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-recall-plugin",
3
- "version": "2.4.11",
3
+ "version": "2.4.13",
4
4
  "description": "在 DSH 中撤回自己发过的消息:工作区文件与对话历史一起回到那条消息发出之前,消息的文本与附件自动放回输入框,改完即可重发。文件走独立影子 git 快照(不碰项目自身的 git),对话走官方 sessions.fork,原会话归档可找回。",
5
5
  "icon": "assets/icon.svg",
6
6
  "type": "module",
@@ -70,7 +70,11 @@
70
70
  "0.2.0-rc.2": "compatible",
71
71
  "0.2.1-alpha.1": "compatible",
72
72
  "0.2.1-alpha.2": "compatible",
73
- "0.2.1-alpha.3": "compatible"
73
+ "0.2.1-alpha.3": "compatible",
74
+ "0.2.1-alpha.4": "compatible",
75
+ "0.2.1-alpha.5": "compatible",
76
+ "0.2.1-rc.1": "compatible",
77
+ "0.2.1-rc.2": "compatible"
74
78
  }
75
79
  }
76
80
  },