dsh-recall-plugin 2.4.6 → 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 +28 -0
- package/README.en.md +12 -2
- package/README.md +12 -2
- package/lib/client.js +770 -199
- package/lib/config.js +11 -4
- package/lib/errors.js +3 -0
- package/lib/exclude-patterns.js +2 -1
- package/lib/index.js +31 -4
- package/lib/intent-journal.js +108 -0
- package/lib/maintenance.js +9 -4
- package/lib/routes-core.js +23 -6
- package/lib/routes-manage.js +24 -6
- package/lib/scripts.posix.js +4 -0
- package/lib/scripts.pwsh.js +4 -0
- package/lib/snapshots.js +74 -4
- package/lib/store.js +7 -5
- package/package.json +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
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
|
+
|
|
5
33
|
## [2.4.6] - 2026-09-29
|
|
6
34
|
|
|
7
35
|
### 变更
|
package/README.en.md
CHANGED
|
@@ -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,
|
|
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
|
@@ -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,
|
|
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
|
|