dsh-recall-plugin 2.4.5 → 2.4.7

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,42 @@
2
2
 
3
3
  本文件格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循语义化版本。
4
4
 
5
+ ## [2.4.7] - 2026-09-30
6
+
7
+ 质量加固专项批次([docs/plans/completed/plan-quality-hardening.md](docs/plans/completed/plan-quality-hardening.md) A1–A8,八项分四波:质量基建 → 数据安全 → 测试基建 → i18n)。2026-09-30 该计划连同 `plan-warmup-unhandled-rejection` 已归档到 `docs/plans/completed/`(活体冒烟第九节 R-1〜R-6 全过)。
8
+
9
+ ### 新增
10
+
11
+ - **i18n 双语层(A4)**:插件自绘界面全双语(撤回按钮与确认面板、toast、设置三张卡、快照树、最近错误),由新配置项 `locale`(`auto` / `zh` / `en`,默认 `auto` 按 `navigator.language` 判)控制,配置卡片新增「界面」分组的下拉、保存即生效。实现分三层:`src/client/locales/`(zh 事实源 + en + 纯逻辑 `t/translate/resolveLocale`,缺 key 依次回落 zh、key 本身);host 文案键控化(错误响应按 `code` 查 `err.*`、最近错误按 `kind` 渲染、构建产物 root 停用提示改下发命中段名 `artifactSeg` 由 client 本地取词,旧中文字段保留一周期);`tests/unit/locales-parity.test.js` 钉两语言 key 集合 / 占位符集合一致 + 静态扫描 client 源码里的字面量 key 漏配,`tests/client/i18n.test.ts` 钉 zh/en 两条渲染链与语言下拉的保存载荷。v1 明示限制(README「界面语言」节):宿主官方设置表单自己渲染的 `Schema.description()` 仍是中文;带动态细节的 host 错误(回退失败的救援结果、具体校验原因、原始异常)原样展示 host 原文,不套本地化短句。
12
+ - **磁盘格式版本守卫(A3)**:影子仓库新增 per-store `format` marker(`SUPPORTED_FORMAT = 1`,缺席视为 1),读到更高版本或内容损坏时**拒写放行读**(fail-closed)——快照、撤回、tag 清理、索引落盘全短路并 `recordError`,快照列表 / 状态查询等只读路径不受影响,新错误码 `FORMAT_BLOCKED`(client 有对应文案);marker 补戳挂在索引落盘路径(稳态零额外进程),读取带短确认缓存且只缓 affirmative 结果。配套通用 `fileReadCmd`(契约 + 双平台模板),替 A2 与后续小文件读取省掉逐个特化命令。
13
+ - **操作意图 journal 与崩溃恢复(A2,吸收 U4)**:撤回的 both 链「安全快照 → reset → 救援」中途断电时,安全快照 tag 已在磁盘却无任何记录指向它——新增 `src/host/intent-journal.ts`:execute 动磁盘前先写 `recall-intent.json`(messageId / safetyId / safetyOk / phase),回退成功即清;启动预热与 init 端点两处挂 `recover`:先做幂等判定(工作区与目标 tag 一致即只清记录不 reset),需救援时复用 H1 救援路径自动 reset 回安全快照并 `recordError` 留痕,`agentBusy` 忙时延后到下次 init。救援失败提示同时附上意图文件路径(U4 验收语义:中断点可查)。
14
+ - **client UI 测试体系(A1)**:vitest + jsdom 独立配置(`npm run test:client`,CI 新增同步骤),`tests/client/` 6 文件 82 例——撤回节点主链(25)、快照管理(17)、配置卡片(17)、排除卡片(13)、logger 开关(7)、i18n 双语链路(3)。断言纪律:只断行为与结构(className / aria / 调用次数 / fetch 载荷),不为文案字面量上锁(i18n 让路)。期间实测发现并修复一处真实缺陷(见「修复」)。
15
+ - **client 命名空间 logger(A5)**:`src/client/log.ts` 的 `createLogger(ns)` 前缀 `[dsh-recall:<ns>]`,error/warn 恒输出、info/debug 由 `localStorage['dsh-recall.debug']`(`*` 或逗号分隔命名空间,每次调用重读)开关——现场排查无需重启;localStorage 不可用时静默降级。替换全部 5 处裸 `console.*`,装配完成补一行 info(「Host 活 Client 死」类症状可一眼确认)。
16
+ - **`docs/format.md` 磁盘格式 spec(A7)**:逐文件固化存储布局与格式(tag 命名、index/lineage 的原子写与损坏语义差异、stamp 文件、format marker、意图 journal)、兼容纪律(读取侧字段可选化 / 未知字段忽略 / 高版本拒写),并写死「代码与本文漂移即 bug」约定;同时提前实施 plan-p2 的 FORMAT 半(SECURITY 半仍留 P2-2)。
17
+
18
+ ### 变更
19
+
20
+ - **开启 `noUncheckedIndexedAccess`(A6)**:编译期堵索引越界,修 14 处(含 `Map.get(...)!.push` 类断言),顺带清掉无注释的既有非空断言;由 CI 既有 typecheck 步骤自动成门禁,不连带开其他严格 flag。
21
+ - **catch 理由注释纪律(A8)**:协作规约新增「catch 必须附降级理由(为什么吞、为何安全、谁兜底)」,全 src 143 处 catch(host 99 + client 44)逐处审计补注;未发现「吞错 + 无 recordError + 无注释」的真问题;不设 CI 启发式门禁(误报高,靠规约 + review)。
22
+ - 配置 schema 增至 10 字段(新增 `locale`,不列进 `cordis.patch.yml`——它按行覆盖、默认值随 schema 下发);`init` 响应与 `config-get` 的 config 子集同步下发 `locale`(老 Host 缺字段时 client 按 `auto` 解析)。
23
+ - 「最近错误」区按 host 的 `kind` 渲染本地化提示(原来的中文原文保留在条目 `title` 上),未分类错误仍原样展示 host message。
24
+
25
+ ### 修复
26
+
27
+ - **win32 上磁盘格式守卫会把正常快照库锁死(严重,随本批次实弹掘出)**:新增的通用 `fileReadCmd` 用 `Get-Content -ErrorAction SilentlyContinue` 读文件,而 PowerShell 对**缺席文件**即使吞掉报错仍以退出码 1 收尾(pwsh / Windows PowerShell 5.1 双实测)——runShell 的退出码门禁把它当执行失败抛出,格式守卫便把一个还没有 `format` 标记的正常 store 判成「标记读不到」;由于守卫同时挡在快照捕获与索引落盘上,补戳永远写不下去,**快照、撤回、快照列表载入全部停摆且不可自愈**(POSIX 侧靠 `cat … || true` 无此问题)。修复=读取命令改用 `Test-Path` 分支让缺席以成功收尾(与 POSIX 同语义),并补契约文本钉防回归;沉淀为 compat-audit **I41**。
28
+ - **宿主退出期插件可能中断宿主(fatal load failure)**:启动预热是 fire-and-forget 的异步 IIFE,宿主在预热途中卸载(`dsh --profile headless --help` 打印帮助后立即退出、HMR、装配门禁 dispose)时访问 `sessions` 会抛「inactive context」,未接的 promise rejection 被 cordis 加载器记成 fatal。修复=IIFE 整体接 catch 并保留诊断行(`recall warmup skipped: …`);该项同时关闭了既有待办 [plan-warmup-unhandled-rejection](docs/plans/completed/plan-warmup-unhandled-rejection.md)。
29
+ - **格式守卫的读失败文案不再指认「文件损坏」**:读失败(多为宿主启动早期 shell 未就绪)与标记内容非法此前共用一句「不可读或内容非法」,会把用户引向一个没有问题的文件;现按「高版本 → 请升级插件 / 内容非法 → 检查 format 文件 / 读不到 → 环境未就绪、下一条消息自动重试」三分。
30
+ - **i18n 切换语言的三处半截生效(浏览器实弹掘出)**:① 英文确认句里「发送时状态」与「共 N 个文件将变更」两句拼接缺空格(`sent.1501 files`);② 语言切换后设置卡片的两个分区折叠头(排除配置 / 快照管理)停在挂载时语言,要离开再回来才跟上;③ 保存成功提示用切换**前**的语言。修复=英文词条补句首空格、配置表单在语言落地时回调设置外壳重渲染、语言补丁先本地落地再报成功;三处均补进 client 组件测试。
31
+ - **回退失败时不再吞掉救援结果**:此前的码文案表把 `ROLLBACK_FAILED` 一律显示成「回退失败」四字,会盖掉 host 侧更有用的信息(「已自动恢复到安全快照,请重新预览后重试」或手动救援命令)。A4 起 `err.*` 词典只收 host 文案静态的错误码,`ROLLBACK_FAILED`(连同 `BAD_TYPE` / `SETTINGS_WRITE_FAILED` / `PARTIAL_DELETE` / `ERROR`)回落 host message 原文——中文用户看到的是更完整的救援结果与逃生命令,代价是英文界面下这几类错误可能中英混排(策略与理由见 README「界面语言」)。
32
+
33
+ ## [2.4.6] - 2026-09-29
34
+
35
+ ### 变更
36
+
37
+ - **兼容声明同步 dsh 0.2.0-rc.1(零破坏核验,无代码改动)**:全局实装 `@deepseek-ai/dsh@0.2.0-rc.1`(npm dist-tag `next`,0.2.0 系列首个候选版本、汇总自 `v0.1.7-rc.2`;`latest` 仍是 0.1.7-rc.2,**装最新版必须显式写版本号**)后,以**全树逐文件哈希 diff** 核验——npm 清理失败留下的 `.dsh-EBhnoWNL` 残留正好是 0.1.7-rc.2 整包副本(298MB),直接用作基线(比对后已删除);528 包中 310 包仅版本号 lockstep 变更,类型面 65 改 / 23 增 / 0 删,运行时 JS 71 改 / 11 增 / 2 删。插件消费面**零破坏**:`dsh-shell`/`dsh-settings`/`dsh-session-query`/`dsh-agent`/`dsh-client-connection`/`dsh-host-webserver`/`dsh-sandbox-policy`/`dsh-client-ui-slots`/`dsh-client-ui-renderer`/`dsh-client-ui-settings-plugins`/`dsh-pwsh-local`/`dsh-cordis-host-runner`(插件加载面)等内容字节级一致,消费面符号计数 11 项 0 差异(`readAttachment`/`updateQueue`/`archiveSession`/`openSession`/`createDrafts`/`releaseDraftAttachments`/`setDraft`/`addAttachments`/`stopActivity`/`increaseTitle`/`atSeq`),`plugins.bundle.config` 槽位定义与渲染调用逐行一致(`settings.plugin.item` 两版均 0 命中)。**唯一接口增量**是 `sessions.fork` 的可选 `onCreated`(向后兼容、插件不传);真增量集中在插件管理页 UI(刷新反馈/安装输入清洗)、`config-editor` 配置继承值计算、会话标题「未命名」语义、消息提交埋点,以及 `dsh-session` 的工具结果修复链重构(`ToolCallRecovery`,插件 `cutSeq` 恒取平衡前缀、不触发补闭合分支)。门禁:`check:dsh` 四层全绿、`test:probe` 52/52、`verify:host` 装配断言全过、`typecheck` 通过、`npm test` 436/436。**活体冒烟(真宿主 + 浏览器实弹,link 模式 / `D:\tmp\recall-h0`)逐项通过**:插件经启动期兼容门禁激活(同 profile 的第三方 `dshmarket@1.65.1` 因 peer 只到 `^0.1.2-alpha.2` 被跳过,反证门禁真实生效);撤回全链(面板清单、文件回退、安全快照 `snap-pre-rollback-*`、lineage、子会话打开、标题继承无递增、文本回填、无残留排队项、原会话归档)与设置页(配置卡完整渲染、`保存`/`恢复默认` 双向回读一致、快照树两级展开与版本家族聚族、`立即 gc`「gc 完成」)均正常,dsh stderr 零插件报错。同步项:**peer 范围必须新增 tuple** `|| >=0.2.0-rc.1 <0.2.1`(npm 的 prerelease 门槛要求同 tuple 且带 prerelease 的比较器,既有 `<0.1.8` 段放行不了 `0.2.0-rc.1`;不扩范围则启动期兼容门禁会整行跳过本 bundle)、`dshReleases` 补 `0.2.0-rc.1: compatible`、`docs/reference/` 镜像按 `dsh-v0.2.0-rc.1` tag 重拉(13 源中仅 `09-architecture.md` 有实质差异:新增一行「失败步骤会记录缺失的工具结果」,净 +121 字节)、`docs/dsh-contract.md` 对应版本与 compat-audit 头部核验段同步、README 双语徽章与兼容范围同步,评估实证见 `docs/upgrade-assessments/dsh-0.2.0-rc.1.md`。观察项(不阻塞):`fork.onCreated` 可更早拿到 childId(插件当前不需要);**自动化任务改由可选 bundle `dsh-experimental-schedule-bundle` 提供、默认关闭**,用户如需须在插件管理页开启。
38
+
39
+ - **兼容声明同步 dsh 0.1.7-rc.2(零破坏核验,无代码改动)**:全局实装 `@deepseek-ai/dsh@0.1.7-rc.2`(npm dist-tag `next`,rc.1 后 346 提交)后,以**聚焦消费面的内容级 diff** 核验——`npm pack` 下载 28 个包(全部 peer + 全部消费点所在包 + 官方 bundle)的 rc.1 版本,与 rc.2 树做全文件 SHA256 比对并逐行核对契约/实现差异;叠加**符号级复核**(13 包 × 23 符号在新旧树计数逐项相等,0 差异)。插件消费面**零破坏**:`api-session-controller` 的客户端契约目录(`lib/types/client/**`:fork/updateQueue/readAttachment/binding/archiveSession)**不在变更清单**,`dsh-shell`/`dsh-settings`/`dsh-sandbox-policy`/`dsh-host-webserver`/`dsh-pwsh-local`/`dsh-client-connection`/`dsh-client-modules`/`dsh-client-ui-renderer`/`dsh-client-ui-session`/`dsh-client-store`/`dsh-host-plugin-inventory`/`dsh-api-gateway` 12 个核心接缝包仅动版本号与 i18n 元数据;真增量集中在本版新能力与官方 UI 层——模型选择/账号(`initializeDefaultModel`/`hasProviderApiKey`/`requireModel`)、工具热更(`dsh-session` 纯新增 `ToolHistoryProjection`、`ui-chat` 新 node kind `developer-message`)、工作区快捷键与归档筛选三态、额度提示链、`app-boot` 的跳过报告时机(判定逻辑未变)、`ui-primitives` 样式重构,均为插件不消费处。门禁:`check:dsh` 四层全绿、`test:probe` 52/52、`verify:host` 装配断言全过、`typecheck` 通过、`npm test` 436/436。同步项:`dshReleases` 补 `0.1.7-rc.2: compatible`(peer 范围 `>=0.1.7-alpha.1 <0.1.8` 天然覆盖,无需新 tuple)、`docs/reference/` 镜像按 `dsh-v0.1.7-rc.2` tag 重拉(13 源中仅 `09-architecture.md` 有实质差异:3 处替换、净 +55 字符,含工具热更新增说明)、`docs/dsh-contract.md` 对应版本与事件集来源标注、compat-audit 头部核验段与 I33 增补、README 双语徽章与兼容范围同步,评估实证见 `docs/upgrade-assessments/dsh-0.1.7-rc.2.md`。观察项(不阻塞):`session-query.read()` 新增显式抛 `SESSION_QUERY_CORRUPT_SESSION`(降级链保持 try/catch);新 node kind `developer-message` 不被插件 key `['user','steering']` 命中;归档筛选三态不改 `archivedSessionIds` 语义(I37 判据继续有效);`initializeDefault(signal)` 签名破坏性变更(插件零消费);升级操作陷阱——Windows 上 npm 替换全局 bin shim 遇占用只留哈希名半成品,需手工改回标准名恢复 `dsh` 命令。
40
+
5
41
  ## [2.4.5] - 2026-09-24
6
42
 
7
43
  ### 新增
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.1.7--rc.1-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.7-rc.1)
10
+ [![DSH](https://img.shields.io/badge/DSH-0.2.0--rc.1-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.0-rc.1)
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**.
@@ -72,7 +72,7 @@ Prerequisites:
72
72
 
73
73
  - git CLI: without it the recall button won't appear (a notice shows at the top of the page); DSH itself keeps running.
74
74
  - Shell: PowerShell 5.1 / 7 on Windows; bash on Linux/macOS.
75
- - DSH version: `0.1.2-alpha.1` through `0.1.7-rc.1`. peerDependencies open a window per minor line (`>=0.1.2-alpha.1 <0.1.3 || >=0.1.3-alpha.1 <0.1.4 || >=0.1.5-alpha.1 <0.1.6 || >=0.1.6-alpha.1 <0.1.7 || >=0.1.7-alpha.1 <0.1.8`, consistent with the `dsh.compatibility.dshReleases` declaration): each line is anchored at its first verified version with an upper bound at the next minor, so later prereleases/releases within a verified line are admitted without touching the peer declaration, while unverified new minor lines remain blocked.
75
+ - DSH version: `0.1.2-alpha.1` through `0.2.0-rc.1`. peerDependencies open a window per minor line (`>=0.1.2-alpha.1 <0.1.3 || >=0.1.3-alpha.1 <0.1.4 || >=0.1.5-alpha.1 <0.1.6 || >=0.1.6-alpha.1 <0.1.7 || >=0.1.7-alpha.1 <0.1.8 || >=0.2.0-rc.1 <0.2.1`, consistent with the `dsh.compatibility.dshReleases` declaration): each line is anchored at its first verified version with an upper bound at the next minor, so later prereleases/releases within a verified line are admitted without touching the peer declaration, while unverified new minor lines remain blocked (the 0.2.0 line needs its own tuple — npm's prerelease gate only admits a prerelease from a comparator with the same tuple, so the existing `<0.1.8` segment cannot admit `0.2.0-rc.1`).
76
76
  - **0.1.7-alpha.1 is a breaking release** (`ShellExecutor.run`/`start` became `execute().result()`, and the settings surface became `SettingsForms`, addressed by profile entry id with `.volatile()` marked fields); 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.
77
77
  - `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.
78
78
 
@@ -119,9 +119,16 @@ All options can be edited visually in the "**Settings → Plugin Config → Reca
119
119
  | `refillDraft` | true | Refill the recalled message (text and attachments) into the input box after a recall |
120
120
  | `snapshotEnabled` | true | Master snapshot switch (off = no new snapshots; existing snapshots remain recallable) |
121
121
  | `archiveOriginal` | true | Archive the original session after a recall (off = the original session stays in the session list) |
122
+ | `locale` | auto | UI language: `auto` follows the system (`navigator.language` starting with `zh` → Chinese, otherwise English), `zh` / `en` pin it explicitly. See "UI language" below |
122
123
 
123
124
  The settings card also offers "Restore defaults" (one-click reset of all fields) and a "Recent errors" viewer/clearer.
124
125
 
126
+ ### UI language
127
+
128
+ Every UI surface the plugin draws itself (recall button and confirmation panel, toasts, the three settings cards, the snapshot manager tree, recent errors) is bilingual and driven by `locale` — switch it in the "Interface" group of the config card; saving applies immediately (the settings page switches at once, chat-side wording follows on the next page reload).
129
+
130
+ Two explicit v1 limits: (1) field descriptions rendered by the host's own settings form (i.e. the `Schema.description()` texts) stay in Chinese — the plugin cannot localize those; the plugin's own config card is unaffected. (2) For host-side errors that carry dynamic detail (a failed rollback's rescue outcome, a specific validation reason, a raw exception), the panel and "Recent errors" show the host text verbatim instead of a localized short phrase — we prefer mixed-language output over swallowing the detail you need to troubleshoot.
131
+
125
132
  ## Snapshot Maintenance & Cleanup
126
133
 
127
134
  The plugin manages disk usage automatically — no manual housekeeping needed:
@@ -154,6 +161,8 @@ When each user message is sent (before the agent touches any files), the workspa
154
161
  git --git-dir="<store>\git\.git" ls-tree -r --name-only snap-<messageID>
155
162
  ```
156
163
 
164
+ - The format and version-compatibility rules for every file in the store directory (index, recall chain, format marker, intent journal, etc.) live in [docs/format.md](docs/format.md).
165
+
157
166
  ## Local Development (without publishing)
158
167
 
159
168
  Point the profile's dependency for this package at your clone via `link:`; DSH loads the built `lib/` artifacts from the workspace (source lives in `src/`), so run `npm run build` after editing `src/`, then restart DSH — no copying or publishing needed:
@@ -172,11 +181,12 @@ Note: all source lives in `src/` (host in `src/host/`, browser side in `src/clie
172
181
 
173
182
  ### Tests
174
183
 
175
- - `npm test`: pure-logic unit tests (vitest, 34 files / 436 cases, no DSH dependency, runs identically in CI and locally) — config parsing, snapshot parsers, rescue orchestration, error classification, script-template same-name-export contract, shell execution dual-channel split and failure grading, settings bridge across both generations, client pure functions, published-package layout, snapshot index persistence, storage caps and retention, etc.;
184
+ - `npm test`: pure-logic unit tests (vitest, 37 files / 476 cases, no DSH dependency, runs identically in CI and locally) — config parsing, snapshot parsers, rescue orchestration, error classification, the disk-format guard, the recall intent journal, i18n dictionaries plus a missing-key scan, script-template same-name-export contract, shell execution dual-channel split and failure grading, settings bridge across both generations, client pure functions, published-package layout, snapshot index persistence, storage caps and retention, etc.;
185
+ - `npm run test:client`: client component tests (vitest + jsdom, 6 files / 82 cases, runs in CI) — the recall node main chain (preview→execute→fork→refill), snapshot-manager tree and delete flows, config/exclude card error paths, the logger switch matrix, and the zh/en rendering chains; assertions pin behaviour and structure only (className / aria / request payloads), never literal copy;
176
186
  - `npm run test:probe`: official-API field probes (requires a local dsh installation; **must run after any dsh upgrade**) — pins fields like `renderMessageImages`/`node`/`cwd`, `atSeq`/`increaseTitle` and the fork-cut anchors of `sessions.fork`, `listSessions` record shape, `AgentRegistry`, the shell execution seam (`execute`/`ShellExecution.result`), the settings surface (`SettingsForms`/profile entry id/volatile gate) plus `loader/volatile-update` and the `Fiber.entry` shape, and goes red on violation;
177
187
  - `npm run verify:host`: assembly gate (requires a local dsh installation) — boots the plugin with a real cordis context in two passes (legacy settings stub and a modern-face-only stub), asserting inject declarations, endpoint registration, Config schema, teardown cleanliness and settings-face dispatch, catching assembly regressions before release;
178
188
  - `npm run build`: full host+client build (mandatory after any `src/` change); `npm run check:dsh`: dsh version inspection (pre-release).
179
- - CI (GitHub Actions) runs `npm ci --legacy-peer-deps` + `npm run typecheck` + `npm test` + unified artifact-freshness check (`npm run build && git diff --exit-code lib/`; probes and the assembly gate only run on machines with dsh).
189
+ - CI (GitHub Actions) runs `npm ci --legacy-peer-deps` + `npm run typecheck` + `npm test` + `npm run test:client` + unified artifact-freshness check (`npm run build && git diff --exit-code lib/`; probes and the assembly gate only run on machines with dsh).
180
190
 
181
191
  ## License
182
192
 
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.1.7--rc.1-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.7-rc.1)
8
+ [![DSH](https://img.shields.io/badge/DSH-0.2.0--rc.1-blue)](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.0-rc.1)
9
9
  ---
10
10
  在任意一条你发过的消息下方点「↶ 撤回」——**工作区文件和对话历史一起回到那条消息发出之前的状态**。
11
11
  ---
@@ -69,27 +69,27 @@
69
69
 
70
70
  - git CLI:未安装时撤回按钮不出现(页面顶部会提示安装 git),不影响 DSH 运行。
71
71
  - shell:Windows 上 PowerShell 5.1 / 7 均可;Linux/macOS 需 bash。
72
- - DSH 版本:`0.1.2-alpha.1` 至 `0.1.7-rc.1`。peerDependencies 按 minor 版本线开窗(`>=0.1.2-alpha.1 <0.1.3 || >=0.1.3-alpha.1 <0.1.4 || >=0.1.5-alpha.1 <0.1.6 || >=0.1.6-alpha.1 <0.1.7 || >=0.1.7-alpha.1 <0.1.8`,与 `dsh.compatibility.dshReleases` 声明一致):每条线以首个核验版本为下限、上界开到下一 minor,同线内后续 prerelease/正式版自动放行、无需改 peer 声明;未核验的新 minor 线仍被拦截。
72
+ - DSH 版本:`0.1.2-alpha.1` 至 `0.2.0-rc.1`。peerDependencies 按 minor 版本线开窗(`>=0.1.2-alpha.1 <0.1.3 || >=0.1.3-alpha.1 <0.1.4 || >=0.1.5-alpha.1 <0.1.6 || >=0.1.6-alpha.1 <0.1.7 || >=0.1.7-alpha.1 <0.1.8 || >=0.2.0-rc.1 <0.2.1`,与 `dsh.compatibility.dshReleases` 声明一致):每条线以首个核验版本为下限、上界开到下一 minor,同线内后续 prerelease/正式版自动放行、无需改 peer 声明;未核验的新 minor 线仍被拦截(0.2.0 线必须显式开窗——npm 的 prerelease 门槛要求同 tuple 且带 prerelease 的比较器,既有 `<0.1.8` 段放行不了 `0.2.0-rc.1`)。
73
73
  - **0.1.7-alpha.1 是破坏性版本**(`ShellExecutor` 的 `run`/`start` 换成 `execute().result()`;settings 面换成 `SettingsForms`、按 profile 条目 id 寻址并要求字段标 `.volatile()`),插件采用**双分支共存适配**——同一次发布同时兼容 0.1.2–0.1.6 各线与 0.1.7,老版本 DSH 上行为不变。
74
74
  - `0.1.1-rc.2` 及更早不支持:那条线的客户端运行时没有 `sessions`/`workspaces`/`uiWorkspace` 服务,插件 UI 会静默不渲染。
75
75
 
76
76
  安装与验证:
77
77
 
78
- - DSH 官方插件命令:安装并自动挂载进 web profile
79
- ```powershell
78
+ - DSH 官方插件命令:安装并自动挂载进profile
79
+
80
+ Web UI
81
+ ```
80
82
  dsh plugin --profile web add dsh-recall-plugin
81
83
  ```
84
+ Desktop
85
+ ```
86
+ dsh plugin --profile desktop add dsh-recall-plugin
87
+ ```
82
88
  - 也可从 git 直接安装:
83
89
  ```powershell
84
90
  dsh plugin --profile web add github:limbo947/dsh-recall-plugin
85
91
  ```
86
92
 
87
- - 重启 DSH 进程(按你的启动方式择一)
88
- ```powershell
89
- dsh web # 前台运行
90
- pm2 restart <your-dsh-name> # 由 pm2 托管
91
- ```
92
-
93
93
  - 验证:重启后硬刷新页面(Ctrl+Shift+R),悬停任意一条插件启用后发送的用户消息——复制按钮旁出现「↶」即生效。没有按钮?九成是没重启 DSH 进程,或 git CLI 不在 PATH 里。
94
94
 
95
95
  - 卸载:同时移除依赖与挂载,快照数据保留在 home 下 `dsh-recall-snapshots/`,想彻底清除手动删掉该目录即可。
@@ -118,9 +118,16 @@ dsh plugin --profile web remove dsh-recall-plugin
118
118
  | `refillDraft` | true | 撤回后把被撤回的消息(文本与附件)回填到输入框 |
119
119
  | `snapshotEnabled` | true | 快照总开关(关闭只冻结新建,已有快照仍可撤回) |
120
120
  | `archiveOriginal` | true | 撤回后归档原会话(关闭后原会话保留在会话列表中) |
121
+ | `locale` | auto | 界面语言:`auto` 跟随系统(`navigator.language` 以 `zh` 开头用中文,否则英文)、`zh` / `en` 显式锁定。见下方「界面语言」 |
121
122
 
122
123
  设置卡片另提供「恢复默认」(一键重置全部字段)与「最近错误」查看/清空。
123
124
 
125
+ ### 界面语言
126
+
127
+ 插件自绘的界面(撤回按钮与确认面板、toast 提示、设置卡片三张卡、快照管理树、最近错误)全部双语,由 `locale` 控制,在配置卡片的「界面」分组里下拉切换、保存即生效(设置页立刻切换;聊天页的会话文案在下次刷新页面后跟进)。
128
+
129
+ 两处 v1 明示限制:① 宿主官方设置表单自己渲染的字段说明(即 `Schema.description()` 文本)仍是中文,插件无法本地化——插件自绘的配置卡片不受影响;② 错误面板与「最近错误」里,万一出现「宿主侧带动态细节的错误」(如回退失败的救援结果、具体校验失败原因、原始异常文本),会原样展示宿主返回的中文/英文原文而不再套本地化短句——宁可中英混排也不吞掉排障需要的细节。
130
+
124
131
  ## 快照维护与清理
125
132
 
126
133
  插件自动控制磁盘占用,无需手动管理:
@@ -153,6 +160,8 @@ dsh plugin --profile web remove dsh-recall-plugin
153
160
  git --git-dir="<store>\git\.git" ls-tree -r --name-only snap-<消息ID>
154
161
  ```
155
162
 
163
+ - 存储目录里每个文件(索引、撤回链、格式标记、意图记录等)的格式与版本兼容约定见 [docs/format.md](docs/format.md)。
164
+
156
165
  ## 本地开发(无需发布)
157
166
 
158
167
  把 profile 对本包的依赖改成 `link:` 指向克隆目录;DSH 加载的是工作区 `lib/` 构建产物(源码在 `src/`),改 `src/` 后先 `npm run build` 再重启 DSH 生效,无需复制或发布:
@@ -171,11 +180,12 @@ pnpm install
171
180
 
172
181
  ### 测试
173
182
 
174
- - `npm test`:纯逻辑单测(vitest,34 个文件 436 例,无 DSH 依赖,CI 与本地同跑)——配置解析、快照解析器、救援编排、错误分类、脚本模板同名导出契约、执行通道双分支与失败分级、settings 双代桥接、客户端纯函数、发布包内容布局、快照索引持久化、存储上限与保留天数等;
183
+ - `npm test`:纯逻辑单测(vitest,37 个文件 476 例,无 DSH 依赖,CI 与本地同跑)——配置解析、快照解析器、救援编排、错误分类、磁盘格式守卫、操作意图 journal、i18n 词典与 key 漏配扫描、脚本模板同名导出契约、执行通道双分支与失败分级、settings 双代桥接、客户端纯函数、发布包内容布局、快照索引持久化、存储上限与保留天数等;
184
+ - `npm run test:client`:client 组件测试(vitest + jsdom,6 个文件 82 例,CI 同跑)——撤回节点主链(preview→execute→fork→回填)、快照管理树与删除流、配置/排除卡片错误路径、logger 开关矩阵、zh/en 双语渲染链;断言只锁行为与结构(className / aria / 请求载荷),不锁文案字面量;
175
185
  - `npm run test:probe`:官方 API 字段探针(依赖本机 dsh 安装;dsh 升级后本地必跑)——钉住 `renderMessageImages`/`node`/`cwd`、`sessions.fork` 的 `atSeq`/`increaseTitle` 与切点锚点、`listSessions` 记录结构、`AgentRegistry`、shell 执行接缝(`execute`/`ShellExecution.result`)、settings 面(`SettingsForms`/profile 条目 id/volatile 门槛)与 `loader/volatile-update`/`Fiber.entry` 形状等字段,违反即红;
176
186
  - `npm run verify:host`:装配门禁(依赖本机 dsh 安装)——用真实 cordis 起插件跑两个 pass(旧面桩 + 只给新面的桩),断言 inject 声明、端点注册、Config schema、卸载清理与 settings 面分派,装配回归发版前即可拦截;
177
187
  - `npm run build`:host+client 全量打包(改任何 `src/` 后必跑);`npm run check:dsh`:dsh 版本巡检(发布前)。
178
- - CI(GitHub Actions)跑 `npm ci --legacy-peer-deps` + `npm run typecheck` + `npm test` + 产物新鲜度统一校验(`npm run build && git diff --exit-code lib/`;探针与装配门禁只在有 dsh 的机器跑)。
188
+ - CI(GitHub Actions)跑 `npm ci --legacy-peer-deps` + `npm run typecheck` + `npm test` + `npm run test:client` + 产物新鲜度统一校验(`npm run build && git diff --exit-code lib/`;探针与装配门禁只在有 dsh 的机器跑)。
179
189
 
180
190
  ## License
181
191