dsh-recall-plugin 2.4.10 → 2.4.12
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 +32 -0
- package/README.en.md +2 -2
- package/README.md +2 -2
- package/lib/client.js +91 -25
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
|
|
4
4
|
|
|
5
|
+
## [2.4.12] - 2026-10-16
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
|
|
9
|
+
- **用户消息正文在 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`。
|
|
10
|
+
- **新面内容渲染从「分片投影」收敛为「整条内容单入口」**:文本/图片/文件卡片/未知块/引用摘要一次交给官方 `conversation.chat.user.content` Factory(投影=官方 `contentParts` 三分派的逐字复刻;该函数官方未导出),与官方 `UserMessageNodeView` 同款调用(含 `interactive: true` 对齐)——插件只保留动作行(时间/复制/撤回),气泡外壳/文件卡片/JSON 块/文本四类自绘在新面退场,官方规则链(密钥脱敏等)自动跟随。
|
|
11
|
+
- **兜底不静默**:官方 Factory 返回空或抛错(槽位未注册的中间版本)时回落自绘(文本/文件卡片/JSON;新面兜底无图片入口,跳过)并打 warn——「探测通过但渲染为空」不再可能产出空白正文。旧面(≤0.2.1-alpha.2)行为不变。
|
|
12
|
+
|
|
13
|
+
### 兼容
|
|
14
|
+
|
|
15
|
+
- **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 包零变更。
|
|
16
|
+
- **本版唯一机制变更(`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`。
|
|
17
|
+
|
|
18
|
+
### 文档
|
|
19
|
+
|
|
20
|
+
- `docs/reference/` 镜像按 tag `dsh-v0.2.1-alpha.4` 重拉:13 源中 12 份逐字节相同、仅 `05-publish.md` 差异(新增 `engines.dsh` 版本声明段,+365 B)。
|
|
21
|
+
- `docs/dsh-contract.md`(版本段与沿革 ⑨)/ `docs/compat-audit.md`(新增 I43)/ `AGENTS.md`(I43 索引行)/ README 双语兼容范围与徽章同步至 0.2.1-alpha.4。
|
|
22
|
+
|
|
23
|
+
## [2.4.11] - 2026-10-15
|
|
24
|
+
|
|
25
|
+
### 兼容
|
|
26
|
+
|
|
27
|
+
- **dsh 0.2.1-alpha.3 兼容(首个破坏性版本:chat.node 图片渲染入口换代)**(升级核查见 [docs/upgrade-assessments/dsh-0.2.1-alpha.3.md](docs/upgrade-assessments/dsh-0.2.1-alpha.3.md) 与 [docs/compat-audit.md](docs/compat-audit.md) 头部 0.2.1-alpha.3 段):官方移除 `chat.node` 的 `renderMessageImages`、把 `conversation.message.images` 拆为 `conversation.chat.message.images` + `conversation.chat.user.images`,图片渲染改经 `renderFactorySlot`(`PropsRenderFactories`)+ `loadImage` 的 `conversation.chat.user.content` Factory——插件原图片重绘链在新版静默失效(用户消息图片无声空白,issue #9 的第二次形态,零报错)。
|
|
28
|
+
- **修复为运行时双面探测**(`src/client/recall-node.ts`):旧面(≤0.2.1-alpha.2)沿用 `renderMessageImages` 闭包;新面(≥0.2.1-alpha.3)经 `renderFactorySlot('conversation.chat.user.content', …)` 只渲染附件行(`content.text` 传空串保官方 `showBubble=false`,正文仍由插件自绘气泡、不重复出官方气泡),`loadImage` 原样透传——peer 全窗口(0.1.2–0.2.x)行为一致。
|
|
29
|
+
- 探针改双面契约断言(`test:probe` 52/52),`test:client` +2(旧面闭包 / 新面 Factory 载荷与 `text:''` 契约要点 / 两入口双缺降级);其余契约零漂移:`sessions.fork`(仅 `create` 加可选 `agentPreset`)/`updateQueue`/`setDraft`/`archiveSession` 均未动,事件集 60 → 62(仅增 `job/registered`/`job/settled`,零删除)。观察项:exact Fetch 路由明确「仅 owner Peer 可达」——远程非 owner 访问场景插件 API 403(本机 web/desktop 零影响)。
|
|
30
|
+
- **密钥脱敏对齐(同日跟进)**:官方新增 `dsh-client-ui-secret-mask`(`userText.register` 规则 + `conversation.user.text.node` key=`secret`)只在官方文本投影层打码,插件自绘气泡原本绕过、密钥明文。文本行改经新面 `renderFactorySlot('conversation.user.text', { text, facts: {}, interactive: false, ellipsis: false })` 投影——仅继承脱敏(`facts` 空对象令官方 whitespace 折叠/裁剪规则读不到 flag 即禁用、显示与旧面逐字一致;引用 chip 仅在显式 wire 格式下出现);旧面(≤0.2.1-alpha.2,无 `renderFactorySlot`)保持纯字符串渲染。探针 +1、`test:client` +1(**probe 53/53、client 93/93**)。
|
|
31
|
+
|
|
32
|
+
### 文档
|
|
33
|
+
|
|
34
|
+
- `docs/reference/` 镜像按 tag `dsh-v0.2.1-alpha.3` 重拉:13 源中 2 份差异(`05-publish.md` 的 `--from-default-profile` 重置语义;`11-cookbook-conversation-node.md` 的 `exclusive` 认领与 `groupTree` 位置表)。
|
|
35
|
+
- `docs/dsh-contract.md` / `docs/compat-audit.md`(I2 重写)/ `AGENTS.md`(I2 行)/ README 双语兼容范围与徽章同步至 0.2.1-alpha.3。
|
|
36
|
+
|
|
5
37
|
## [2.4.10] - 2026-10-07
|
|
6
38
|
|
|
7
39
|
### 修复
|
package/README.en.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|

|
|
8
8
|

|
|
9
9
|
|
|
10
|
-
[](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.4)
|
|
11
11
|

|
|
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.
|
|
76
|
+
- DSH version: `0.1.2-alpha.1` through `0.2.1-alpha.4`; 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
|
[](https://www.npmjs.com/package/dsh-recall-plugin)
|
|
6
6
|
[](https://www.npmjs.com/package/dsh-recall-plugin)
|
|
7
7
|

|
|
8
|
-
[](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.4)
|
|
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.
|
|
73
|
+
- DSH 版本:`0.1.2-alpha.1` 至 `0.2.1-alpha.4`;已核验 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 = [];
|
|
@@ -1359,6 +1409,8 @@
|
|
|
1359
1409
|
function UserRecallNode(props) {
|
|
1360
1410
|
const node = props && props.node;
|
|
1361
1411
|
const renderMessageImages = props && props.renderMessageImages;
|
|
1412
|
+
const renderFactorySlot = props && props.renderFactorySlot;
|
|
1413
|
+
const loadImage = props && props.loadImage;
|
|
1362
1414
|
const sessionId = props && props.sessionId;
|
|
1363
1415
|
const data = node && node.data ? node.data : {};
|
|
1364
1416
|
const messageId = node ? String(node.id || node.key || "") : "";
|
|
@@ -1557,32 +1609,46 @@
|
|
|
1557
1609
|
setRecall({ stage: "idle" });
|
|
1558
1610
|
};
|
|
1559
1611
|
const bubbleChildren = [];
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
{ key: "
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
const info = fileCardInfo(fileBlocks[i], t);
|
|
1570
|
-
if (!info) continue;
|
|
1571
|
-
bubbleChildren.push(React.createElement(
|
|
1572
|
-
"div",
|
|
1573
|
-
{ className: "dsh-recall-filecard", key: "file-" + i },
|
|
1574
|
-
React.createElement("span", { className: "dsh-recall-filecard-icon" }, info.ext),
|
|
1575
|
-
React.createElement(
|
|
1576
|
-
"span",
|
|
1577
|
-
{ className: "dsh-recall-filecard-body" },
|
|
1578
|
-
React.createElement("span", { className: "dsh-recall-filecard-name", title: info.name }, info.name),
|
|
1579
|
-
React.createElement("span", { className: "dsh-recall-filecard-meta" }, info.size ? info.ext + " " + info.size : info.ext)
|
|
1580
|
-
)
|
|
1581
|
-
));
|
|
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
|
+
}
|
|
1582
1621
|
}
|
|
1583
|
-
if (
|
|
1584
|
-
|
|
1585
|
-
|
|
1622
|
+
if (!officialFace) {
|
|
1623
|
+
if (imageBlocks.length && typeof renderMessageImages === "function") {
|
|
1624
|
+
const render = renderMessageImages;
|
|
1625
|
+
bubbleChildren.push(React.createElement(
|
|
1626
|
+
React.Fragment,
|
|
1627
|
+
{ key: "images" },
|
|
1628
|
+
render({ images: imageBlocks, align: "end" })
|
|
1629
|
+
));
|
|
1630
|
+
}
|
|
1631
|
+
for (let i = 0; i < fileBlocks.length; i++) {
|
|
1632
|
+
const info = fileCardInfo(fileBlocks[i], t);
|
|
1633
|
+
if (!info) continue;
|
|
1634
|
+
bubbleChildren.push(React.createElement(
|
|
1635
|
+
"div",
|
|
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
|
+
)
|
|
1644
|
+
));
|
|
1645
|
+
}
|
|
1646
|
+
if (text !== "") {
|
|
1647
|
+
bubbleChildren.push(React.createElement("div", { className: "dsh-recall-bubble", key: "text" }, text));
|
|
1648
|
+
}
|
|
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
|
+
}
|
|
1586
1652
|
}
|
|
1587
1653
|
const actions = [];
|
|
1588
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.
|
|
3
|
+
"version": "2.4.12",
|
|
4
4
|
"description": "在 DSH 中撤回自己发过的消息:工作区文件与对话历史一起回到那条消息发出之前,消息的文本与附件自动放回输入框,改完即可重发。文件走独立影子 git 快照(不碰项目自身的 git),对话走官方 sessions.fork,原会话归档可找回。",
|
|
5
5
|
"icon": "assets/icon.svg",
|
|
6
6
|
"type": "module",
|
|
@@ -68,7 +68,10 @@
|
|
|
68
68
|
"0.1.7-rc.2": "compatible",
|
|
69
69
|
"0.2.0-rc.1": "compatible",
|
|
70
70
|
"0.2.0-rc.2": "compatible",
|
|
71
|
-
"0.2.1-alpha.1": "compatible"
|
|
71
|
+
"0.2.1-alpha.1": "compatible",
|
|
72
|
+
"0.2.1-alpha.2": "compatible",
|
|
73
|
+
"0.2.1-alpha.3": "compatible",
|
|
74
|
+
"0.2.1-alpha.4": "compatible"
|
|
72
75
|
}
|
|
73
76
|
}
|
|
74
77
|
},
|