dsh-engram 0.4.0 → 0.5.1

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/README.md CHANGED
@@ -23,6 +23,21 @@ with one goal: **save tokens**.
23
23
  integration**: a completed or blocked goal (`goal/change` session event)
24
24
  auto-sediments as a `handoff`/`error` memory (`tag:goal`), so the outcome of
25
25
  a goal survives its session (see the `GET /api/dsh-engram/goals` read surface).
26
+ - **User-correction / preference learning (`autoCapturePreferences`, default on,
27
+ zero LLM)** — the lesson that "when the AI makes a mistake and gets corrected,
28
+ that lesson should persist" ([correx](https://github.com/ozoz5/correx),
29
+ [PAHF](https://arxiv.org/abs/2602.16173), Mem0 preference extraction): a
30
+ `session/event` listener watches `user/message` and turns deterministic
31
+ correction/preference cues into a dedicated `preference` memory kind —
32
+ Chinese (`不要用X`, `改用X`, `以后统一用X`…) and English (`don't use X`,
33
+ `switch to X`, `prefer X`, `never use X`…) patterns, three directions
34
+ (`avoid` / `switch-to` / `prefer`), with interrogative/one-shot-instruction/
35
+ memory-tool-meta-talk filters to keep false positives out. `preference`
36
+ memories outrank every other kind in the `[ENGRAM]` block (`[R]` prefix) so a
37
+ standing user rule is the first line the model sees each session; per-session
38
+ cap + recent-window dedup mirror the auto-capture discipline. Zero LLM, zero
39
+ embeddings, deterministic and pure — extracted clauses are bounded, and every
40
+ write still goes through the secret redactor.
26
41
  - **Symbolic index + progressive disclosure** — a compact `[ENGRAM]` block (default
27
42
  budget 700 chars ≈ 175 tokens; one line per memory) is injected at prompt
28
43
  assembly and **frozen per session**, keeping the request prefix byte-stable for
@@ -51,6 +66,12 @@ with one goal: **save tokens**.
51
66
  Pure rule, zero LLM, pure read (never bumps `hits`); superseded stale truth does
52
67
  not occupy an injection slot (still fetchable via `engram_detail`); frozen per
53
68
  session together with `[ENGRAM]` for prefix stability.
69
+ - **Read-path activation (D)** — a hit whose one-line preview is truncated
70
+ (>140 chars) carries a `…` marker and the block's trailing guidance names that
71
+ concrete id: `#<id> truncated — engram_detail <id> (full) · engram_recall <query>`.
72
+ So the model knows the full record is one deterministic `engram_detail` call
73
+ away instead of assuming the preview is the whole memory (proven gap in real
74
+ use: 66 sessions, zero `engram_detail` calls — the detail path was invisible).
54
75
  - **Memory-to-memory semantics** — `engram_store` accepts optional
55
76
  `supersedes` / `contradicts` memory ids (validated against the same
56
77
  workspace). A superseded ("stale truth") memory is demoted to the tail of
@@ -243,8 +264,8 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
243
264
  <!-- dsh-market compatibility block — keep labels bilingual and in sync with
244
265
  docs/RELEASE-NOTES.TEMPLATE.md (market README convention) -->
245
266
 
246
- - **DSH 版本 / DSH versions**: `>=0.1.2-alpha.2 <0.2.0-0` (dsh-engram `0.3.7`; releases `0.2.0`–`0.3.6` support the old generation only — see matrix below)
247
- - **最后验证日期 / Last verified**: 2026-09-10 — full regression on `dsh-v0.1.5-alpha.1`; real-machine settings GET/PUT/conflict/reset/restart-persistence on `dsh 0.1.5-rc.1` (isolated profile)
267
+ - **DSH 版本 / DSH versions**: `>=0.1.2-alpha.2 <0.3.0-0` (本版 `0.5.1`; 已发布的 `0.3.7`、`0.4.0` 仍声明旧的 `0.1.x` 范围 `>=0.1.2-alpha.2 <0.2.0-0` — 见下方矩阵)
268
+ - **最后验证日期 / Last verified**: 2026-10-19 — 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)+ 数据导出/导入与替换/冲突可视化(issue #3),npm test 334/334 通过
248
269
  - **安装方式已验证 / Verified install**: `dsh plugin --profile web add dsh-engram`
249
270
 
250
271
  > **Short version.** Every **published** release (`0.2.0` … `0.3.6`) supports
@@ -257,7 +278,9 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
257
278
  | dsh-engram release | Status | DSH support | Declared via |
258
279
  | --- | --- | --- | --- |
259
280
  | `0.2.0` … `0.3.6` (npm, GitHub v0.3.3–v0.3.6) | published | **old DSH only** — `0.1.0-rc.7` / `0.1.0-rc.8` / `0.1.1-rc.1` / `0.1.1-rc.2` | peers `@deepseek-ai/dsh-*@^0.1.0-rc.7` |
260
- | `0.3.7` (npm, GitHub v0.3.7) | published | **new DSH only** — `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.2.0-0` |
281
+ | `0.3.7` / `0.4.0` (npm) | published | **new DSH, settings `installSection`** — `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.2.0-0` |
282
+ | `0.5.0` (npm) | published | **DSH `0.1.2-alpha.2` … `<0.3.0-0`** — 设置卡迁移到 entry-id volatile Config(`0.1.7+`/`0.2.x`),`≤0.1.5` 自动回退 `installSection` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.3.0-0` |
283
+ | `0.5.1` (本期 / this release) | published | 同 `0.5.0` — 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)、数据导出/导入 GUI、替换/冲突可视化(issue #3) | 同 `0.5.0` |
261
284
 
262
285
  ### Where the API family broke — `0.1.2-alpha.2`
263
286
 
@@ -282,36 +305,79 @@ HTTP route (`/api/dsh-engram/settings`, same-origin fetch like the memory
282
305
  viewer), so the card is generation-agnostic on both sides of the
283
306
  `0.1.2-alpha.2` boundary.
284
307
 
308
+ **One more settings break, at `0.1.7` / `0.2.0`** — and the reason `0.4.0` was
309
+ not actually loadable on the current DSH: `@deepseek-ai/dsh-settings` **removed
310
+ `settings.installSection` again** in `0.1.7-alpha.2` (and keeps it removed in
311
+ `0.2.x`). The new settings model is **entry-keyed**: editable fields are
312
+ `.volatile()` fields of the plugin's own `Config` (validated + hot-committed by
313
+ the loader), and the settings form is keyed by the profile entry id (`engram`),
314
+ so nothing needs "mounting". `0.5.0` feature-detects the two generations in
315
+ `installEngramSettings`:
316
+
317
+ - DSH `≥0.1.7` / `0.2.x` — the 27 GUI knobs are the volatile `Config` fields
318
+ (`lib/settings.js` `makeEngineConfigSchema`, exported as `Config` from
319
+ `lib/index.js`); the card reads/writes entry `engram` through the same
320
+ `/api/dsh-engram/settings` HTTP route, and the `service.settings` seam
321
+ re-syncs `resolved` on `loader/volatile-update` so card edits land live on
322
+ new sessions.
323
+ - DSH `≤0.1.5` — the legacy `settings.installSection` namespace (`dsh-engram`)
324
+ is registered as before; nothing changes.
325
+
326
+ The declared range `>=0.1.2-alpha.2 <0.3.0-0` covers both, and the HTTP wire
327
+ namespace stays `dsh-engram` on both generations (the seam's active namespace
328
+ drives mutations).
329
+
285
330
  So: keep DSH on `0.1.1-rc.2` or older while running a release ≤ `0.3.6`, and
286
331
  upgrade the plugin to `>=0.3.7` before moving DSH to `0.1.2-alpha.2`.
287
332
 
288
- ### Current release — `0.4.0`
333
+ ### Current release — `0.5.1`
334
+
335
+ Patch release on top of `0.5.0`: session scanning resolves the **versioned** log
336
+ name (`session.v3/v4.jsonl.zstd`) instead of the hardcoded
337
+ `session.jsonl.zstd` — on current DSH that mismatch made `/stats`, `/toolstats`,
338
+ BoundaryPrune's repayment history and the CJK session search all return zero
339
+ silently (issue #8). The memory viewer also gains data export/import and
340
+ supersede/contradict visualization (issue #3).
289
341
 
290
- This release declares support for DSH `>=0.1.2-alpha.2 <0.2.0-0` and was fully
291
- validated against the `dsh-v0.1.5-alpha.1` source and web runtime from the local
292
- `deepseek-harness` checkout.
342
+ The published `0.4.0` declared `>=0.1.2-alpha.2 <0.2.0-0` — which the semver
343
+ gate **rejects on every DSH `0.2.0-0` prerelease** (`<0.2.0-0` excludes
344
+ `0.2.0-rc.x`), and its host settings half still called the `installSection`
345
+ API that `dsh-settings 0.1.7+` removed. `0.5.0` fixes **both**: the range
346
+ opens to `<0.3.0-0` (verified against DSH's own `includePrerelease` check, so
347
+ `0.2.0-rc.2` and the whole `0.2.x` line pass), and the settings card is
348
+ migrated onto the entry-id **volatile Config** model described above (with a
349
+ feature-detected legacy fallback).
293
350
 
294
351
  | Component | Supported range |
295
352
  | --- | --- |
296
- | DSH runtime / host / client package family | `>=0.1.2-alpha.2 <0.2.0-0` |
353
+ | DSH runtime / host / client package family | `>=0.1.2-alpha.2 <0.3.0-0` |
297
354
  | `@deepseek-ai/cordis` | `^4.0.2` |
298
355
  | `@deepseek-ai/schemastery` | `^3.18.2` |
299
356
  | Node.js | `>=22.19.0` |
300
357
 
301
- `0.1.2-alpha.2` is the minimum version for this API family: host settings use
302
- `settings.installSection`, and the browser settings card reads and writes
303
- through the plugin's own HTTP route (`/api/dsh-engram/settings`, see Route B
304
- above). Host import and settings-registration smoke checks pass on
305
- `0.1.2-alpha.2` and `0.1.3-alpha.2`; the full regression suite is validated
306
- on `0.1.5-alpha.1` (real-machine GET/PUT/conflict/reset/persist-across-restart
307
- all verified on `dsh 0.1.5-rc.1` with an isolated profile). The older
358
+ `0.1.2-alpha.2` is the minimum version for this API family: host settings are
359
+ **feature-detected** — on DSH `≤0.1.5` they register the `dsh-engram`
360
+ namespace via `settings.installSection`; on `≥0.1.7` / `0.2.x` the form is the
361
+ plugin's `Config` with the 27 GUI knobs marked `.volatile()` (entry id
362
+ `engram`). The browser settings card reads and writes through the plugin's own
363
+ HTTP route (`/api/dsh-engram/settings`, see Route B above) on both. The full
364
+ regression suite (npm test 329/329, incl. the new settings-v2 coverage for the
365
+ volatile-Config model) passes against the DSH `0.2.0-rc.2` dependency set
366
+ (cordis 4.0.4 + schemastery 3.18.4 + cosmokit, matching the global DSH CLI).
367
+ The `.volatile()`/loader hot-commit path and the describe-side projection were
368
+ verified against the real `dsh-settings 0.2.0-rc.2` utilities, and a full
369
+ real-machine boot smoke on an isolated `dsh 0.2.0-rc.2` profile **passed**:
370
+ the plugin boots with no version-range denial, the settings card form is
371
+ served for entry `engram` (all 27 GUI knobs, incl. `trustedHosts`), and a
372
+ GET → PUT (`mutate` ops, revision 0→1) → unset round trip on
373
+ `/api/dsh-engram/settings` lands in the profile row and reads back. The older
308
374
  `0.1.0-rc.7` and `0.1.1` API graphs are outside this release's compatibility
309
375
  range.
310
376
 
311
377
  To install the DSH version used for validation:
312
378
 
313
379
  ```sh
314
- npm install --global @deepseek-ai/dsh@0.1.5-alpha.1
380
+ npm install --global @deepseek-ai/dsh@0.2.0-rc.2
315
381
  dsh --version
316
382
  ```
317
383
 
@@ -705,9 +771,59 @@ later request. The two switches are independent.
705
771
  simply never trigger (no boundary → no prune, zero side effects); the
706
772
  corpus-wide dry run activates on 38% of sessions. If the pruner service is
707
773
  absent the mechanism disables itself with one log line.
774
+ - **Boundary sources**: todo-progress completion and goal terminal events
775
+ (default), plus an optional **request-count cadence**
776
+ (`boundaryPruneIdleCadence`, default 0/off) — the "step idle" source that
777
+ covers the ~62% of sessions which never touch todo/goal (recon:
778
+ `eval/recon-idle-boundary.mjs`; +85% saved chars at cadence 50 in the
779
+ counterfactual `eval/simulate-idle-cadence.mjs`), still repayment-gated.
780
+ - **Threshold override**: `boundaryPruneThresholdChars` (default 0 → host's
781
+ 8192). Set e.g. 2048 with smaller retained edges
782
+ (`boundaryPruneHeadChars`/`boundaryPruneTailChars`) to prune earlier —
783
+ T2048+small-edges lands 2.4× the first-prune savings of the host default on
784
+ the real corpus (10.14M vs 4.23M chars). Lowering the threshold is safe with
785
+ `resultPack` on, which archives the removed middles.
708
786
  - Default **off**: `boundaryPrune: false`. When on, the log shows
709
787
  `engram boundary-prune (todo @N reqs): pruned …`.
710
788
 
789
+ ### ResultPack L2 (archive + `result_recall`, default OFF)
790
+
791
+ BoundaryPrune makes pruning lossless: the removed middle of every pruned
792
+ tool result is redacted (same rules as the memory write path) and archived to
793
+ `~/.dsh/engram/resultpack/<callId>.json`, and the pruned surface node gets a
794
+ tail handle — `[middle archived: result_recall(<callId>) 可取]` — so the model
795
+ knows the full result is one step away. `result_recall(callId, offset?)`
796
+ returns the archived middle in paged text blocks (progressive disclosure, the
797
+ same philosophy as `[ENGRAM]` → `engram_detail`, and the complete
798
+ ObservationPack pattern from SoL-Pi mechanism ③).
799
+
800
+ - **Lossless by construction**: L1 pruning shrinks the replay; L2 keeps the
801
+ original recoverable on demand — the "model rereads a pruned result" failure
802
+ mode goes to zero.
803
+ - **Scoped cost**: archives live outside the memory domain (big blobs never
804
+ bloat the domain JSON or the [ENGRAM] index); retention is a mtime sweep
805
+ (`resultPackRetentionDays`, default 7) riding the same GC cadence.
806
+ - **Self-disabling**: L2 without `boundaryPrune` has nothing to archive, so it
807
+ registers no tool and emits one warning.
808
+ - Default **off**: `resultPack: false` (must be on together with
809
+ `boundaryPrune: true`). When active the log shows
810
+ `engram result-pack enabled: result_recall(callId, offset) registered`.
811
+
812
+ ### Activation telemetry (SoL-Pi M15/C13)
813
+
814
+ Mechanisms must be provably activating, and dormant ones are off by default.
815
+ `/api/dsh-engram/stats` (and the GUI overview) now answer
816
+ `activation: { boundaryPrune, recall }`:
817
+
818
+ - `boundaryPrune` — cumulative, durable record of real BoundaryPrune fires
819
+ (fires / prunedResults / charsRemoved / byKind todo|goal|step / lastFire),
820
+ written to `~/.dsh/engram/boundary-prune.status.json` on every landed prune,
821
+ independent of `resultPack`.
822
+ - `recall` — the injection leg of the recall funnel: how many `[RECALL]`
823
+ blocks actually made it into sessions (`recall-injections.json`), closing
824
+ the loop already measured from session logs (engram_recall queries → hits →
825
+ engram_detail follows) so you can see injection→query→detail activation.
826
+
711
827
  ## Auto-capture policy
712
828
 
713
829
  Capture is deterministic and offline — it only sees tool *results*, never the
@@ -790,6 +906,15 @@ Defaults are token-conscious; override any key via the profile patch
790
906
  - id: engram
791
907
  config:
792
908
  autoCapture: true # zero-LLM tool-result capture
909
+ autoCapturePreferences: true # zero-LLM user-correction/preference capture (prefer/avoid/switch-to cues)
910
+ preferenceCapturePerSession: 8 # per-session preference memory cap
911
+ boundaryPrune: false # ResultPack L1: prune stale over-threshold tool results at todo/goal boundaries (default OFF)
912
+ boundaryPruneThresholdChars: 0 # threshold override (0 = host 8192; e.g. 2048 prunes earlier)
913
+ boundaryPruneHeadChars: 0 # retained head under an override (0 = host 4096)
914
+ boundaryPruneTailChars: 0 # retained tail under an override (0 = host 1024)
915
+ boundaryPruneIdleCadence: 0 # request-count boundary for todo/goal-less sessions (0 = event-only)
916
+ resultPack: false # ResultPack L2: archive pruned middles + result_recall paged recall (default OFF; needs L1 on)
917
+ resultPackRetentionDays: 7 # archive retention days (mtime sweep on the GC cadence)
793
918
  sessionSearch: true # engram_recall may also FTS past sessions
794
919
  recallScope: workspace # recall scope: "workspace" (strict isolation, default) | "global" (opt-in cross-workspace recall with [W:<ws>] origin markers)
795
920
  autoRecallOnStart: true # session-start auto recall (false = back to pure pull)
@@ -816,7 +941,7 @@ Defaults are token-conscious; override any key via the profile patch
816
941
  ## Development
817
942
 
818
943
  ```sh
819
- npm test # 279 tests: core + web API + GC + usage-observability + Context GC + ESR triggers + BoundaryPrune (node:test)
944
+ npm test # 291 tests: core + web API + GC + usage-observability + Context GC + ESR triggers + BoundaryPrune + preference learning (node:test)
820
945
  npm run eval # offline recall + structure benchmark (deterministic)
821
946
  npm run build:client
822
947
  ```
package/README.zh.md CHANGED
@@ -17,6 +17,15 @@
17
17
  + 失败行)自动捕获为 `tags:["error","test"]`、信号上调。DSH **goal 域打通**:completed/blocked 的
18
18
  goal(`goal/change` 会话事件)自动沉淀为 `handoff`/`error` 记忆(`tag:goal`),目标结局不随会话消散
19
19
  (读取面见 `GET /api/dsh-engram/goals`)。
20
+ - **用户纠正/偏好学习(`autoCapturePreferences`,默认开,零 LLM)** — 「AI 犯错被纠正后,教训要留存」
21
+ ([correx](https://github.com/ozoz5/correx)、[PAHF](https://arxiv.org/abs/2602.16173)、Mem0 偏好提取):
22
+ 一个 `session/event` 监听器盯 `user/message`,用确定性纠正/偏好句式把用户话语沉淀为独立的
23
+ `preference` 记忆 kind——中文(`不要用X`、`改用X`、`以后统一用X`…)与英文(`don't use X`、
24
+ `switch to X`、`prefer X`、`never use X`…)模式,三种方向(`avoid` / `switch-to` / `prefer`),
25
+ 并带疑问句 / 一次性操作指令 / 记忆工具元话语过滤器抑制误报。`preference` 记忆在 `[ENGRAM]`
26
+ 块里**压过其它所有 kind**(`[R]` 前缀),让用户的长期规则成为每会话模型看到的第一行;
27
+ 每会话上限 + 近期窗口去重与自动捕获同纪律。零 LLM、零向量、确定性纯函数——提取片段有界,
28
+ 每条写入依旧先过密钥脱敏器。
20
29
  - **符号索引 + 渐进披露** — 一个紧凑的 `[ENGRAM]` 块(默认预算 700 字符 ≈ 175 token;每条记忆一行)在
21
30
  组装提示词时注入,并**按会话冻结**,让请求前缀字节稳定以复用 KV 缓存。模型需要细节时用
22
31
  `engram_recall` / `engram_detail` 下钻,而不是把命中的原文灌进上下文。召回对内存池做
@@ -35,6 +44,10 @@
35
44
  agentmemory 候选①)——相关记忆不依赖模型想起来就已在上下文里。纯规则、零 LLM、
36
45
  纯读不 bump 命中;superseded 过期真相不占注入槽(仍可 `engram_detail` 重取);
37
46
  与 `[ENGRAM]` 一起按会话冻结保持前缀稳定。
47
+ - **读路径激活(D)** — 命中条目的单行预览被截断(>140 字符)时带 `…` 标记,且块尾
48
+ 引导行点名该具体 id:`#<id> truncated — engram_detail <id> (full) · engram_recall <query>`。
49
+ 模型由此知道完整记录就在一次确定性 `engram_detail` 之后,而不是以为预览就是全文
50
+ (实证盲区:66 个会话 0 次 `engram_detail`——下钻路径此前对模型不可见)。
38
51
  - **记忆间语义(supersede/contradict)** — `engram_store` 可选收 `supersedes` / `contradicts`
39
52
  记忆 id(同工作区校验)。被 supersede 的「过期真相」在召回里**降级到尾部**、并从 `[ENGRAM]`
40
53
  块中**剔除**(新陈述占行);被 contradicts 的记忆保留排序但标注 `· contradicted by <id>`。
@@ -153,8 +166,8 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
153
166
 
154
167
  <!-- dsh-market 兼容信息块 —— 与 docs/RELEASE-NOTES.TEMPLATE.md 的市场 README 公约保持一致 -->
155
168
 
156
- - **DSH 版本**:`>=0.1.2-alpha.2 <0.2.0-0`(dsh-engram `0.3.7`;`0.2.0`–`0.3.6` 仅旧代际 —— 见下文矩阵)
157
- - **最后验证日期**:2026-09-10 —— `dsh-v0.1.5-alpha.1` 完整回归;`dsh 0.1.5-rc.1` 隔离 profile 真机验证设置 GET/PUT/冲突/复位/跨重启持久化
169
+ - **DSH 版本**:`>=0.1.2-alpha.2 <0.3.0-0`(本版 `0.5.1`;已发布的 `0.3.7`、`0.4.0` 仍声明旧的 `0.1.x` 范围 `>=0.1.2-alpha.2 <0.2.0-0` —— 见下文矩阵)
170
+ - **最后验证日期**:2026-10-19 —— 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)+ 数据导出/导入与替换/冲突可视化(issue #3),npm test 334/334 通过
158
171
  - **安装方式已验证**:`dsh plugin --profile web add dsh-engram`
159
172
 
160
173
  > **一句话结论**:所有**已真正发布**的版本(`0.2.0` … `0.3.6`)**只支持旧版
@@ -165,7 +178,9 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
165
178
  | dsh-engram 发布版本 | 状态 | 支持的 DSH | 声明方式 |
166
179
  | --- | --- | --- | --- |
167
180
  | `0.2.0` … `0.3.6`(npm 全量 + GitHub v0.3.3–v0.3.6) | 已发布 | **仅旧版** —— `0.1.0-rc.7` / `0.1.0-rc.8` / `0.1.1-rc.1` / `0.1.1-rc.2` | peers `@deepseek-ai/dsh-*@^0.1.0-rc.7` |
168
- | `0.3.7`(npm + GitHub v0.3.7) | 已发布 | **仅新版** —— `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers 同范围 |
181
+ | `0.3.7` / `0.4.0`(npm) | 已发布 | **新版 DSH,settings `installSection`** —— `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers 同范围 |
182
+ | `0.5.0`(npm) | 已发布 | **DSH `0.1.2-alpha.2` … `<0.3.0-0`** —— 设置卡迁移到 entry-id volatile Config(`0.1.7+`/`0.2.x`),`≤0.1.5` 自动回退 `installSection` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.3.0-0` |
183
+ | `0.5.1`(本期 / this release) | 已发布 | 同 `0.5.0` —— 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)、数据导出/导入 GUI、替换/冲突可视化(issue #3) | 同 `0.5.0` |
169
184
 
170
185
  ### API 族断裂点:`0.1.2-alpha.2`
171
186
 
@@ -185,33 +200,65 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
185
200
  HTTP 路由(`/api/dsh-engram/settings`,与记忆 viewer 同样的同源 fetch)读写
186
201
  `dsh-engram` settings namespace,因此在 `0.1.2-alpha.2` 分界线两侧都是代际无关的。
187
202
 
203
+ **`0.1.7` / `0.2.0` 还有一次 settings 断裂**——`0.4.0` 在最新 DSH 上启动被拒的
204
+ 真正原因:`@deepseek-ai/dsh-settings` 在 `0.1.7-alpha.2` **再次移除了
205
+ `settings.installSection`**(`0.2.x` 一路保留)。新设置模型是**条目式**的:可编辑字段
206
+ 是插件自身 `Config` 的 `.volatile()` 字段(由 loader 校验并热提交),设置表单以
207
+ profile 条目 id(`engram`)为命名空间,不再需要任何“挂载”调用。`0.5.0`
208
+ 在 `installEngramSettings` 里对两代际做特性检测:
209
+
210
+ - DSH `≥0.1.7` / `0.2.x` —— 27 个 GUI 旋钮即 `Config` 的 volatile 字段
211
+ (`lib/settings.js` `makeEngineConfigSchema`,由 `lib/index.js` 导出为
212
+ `Config`);卡片仍走同一 `/api/dsh-engram/settings` HTTP 路由读写条目 `engram`,
213
+ `service.settings` 接缝在 `loader/volatile-update` 事件上把 `resolved`
214
+ 重新同步,卡片编辑对新会话即时生效。
215
+ - DSH `≤0.1.5` —— 照旧注册旧 `settings.installSection` 命名空间(`dsh-engram`)。
216
+
217
+ 声明范围 `>=0.1.2-alpha.2 <0.3.0-0` 同时覆盖两代际;HTTP wire 命名空间在两代际都保持
218
+ `dsh-engram`(由接缝的活动命名空间驱动写入)。
219
+
188
220
  所以:跑 ≤ `0.3.6` 的已发布版本时请把 DSH 保持在 `0.1.1-rc.2` 或更旧;升级
189
221
  DSH 到 `0.1.2-alpha.2` 之前,先升级插件到 `>=0.3.7`。
190
222
 
191
- ### 当前发布版本 —— `0.4.0`
223
+ ### 当前发布版本 —— `0.5.1`
192
224
 
193
- 当前发布版本声明适配 DSH `>=0.1.2-alpha.2 <0.2.0-0`,并以本地
194
- `deepseek-harness` 的 `dsh-v0.1.5-alpha.1` 代码和 Web 运行时完成完整验证。
225
+ 在 `0.5.0` 之上的补丁版:会话扫描改为解析**版本化**日志名
226
+ (`session.v3/v4.jsonl.zstd`)而不硬编码 `session.jsonl.zstd`——在当前 DSH 上
227
+ 这个错位让 `/stats`、`/toolstats`、BoundaryPrune 的偿还历史、CJK 会话搜索全部
228
+ **静默返回 0**(issue #8)。记忆查看器同时补上数据导出/导入与替换/冲突可视化
229
+ (issue #3)。
230
+
231
+ 已发布的 `0.4.0` 声明 `>=0.1.2-alpha.2 <0.2.0-0`——这个范围被 semver 门槛在
232
+ **所有 DSH `0.2.0-0` 预发布**上拒绝(`<0.2.0-0` 排除 `0.2.0-rc.x`),且其宿主设置端
233
+ 仍在调用 `dsh-settings 0.1.7+` 已移除的 `installSection` API。`0.5.0`
234
+ **两处都修复**:范围放宽到 `<0.3.0-0`(用 DSH 自身的 `includePrerelease` 检查验证过,
235
+ `0.2.0-rc.2` 及整个 `0.2.x` 线都放行);设置卡迁移到上面描述的 entry-id **volatile
236
+ Config** 模型(并带特性检测的旧代际回退)。
195
237
 
196
238
  | 组件 | 适配版本 |
197
239
  | --- | --- |
198
- | DSH runtime / host / client 包族 | `>=0.1.2-alpha.2 <0.2.0-0` |
240
+ | DSH runtime / host / client 包族 | `>=0.1.2-alpha.2 <0.3.0-0` |
199
241
  | `@deepseek-ai/cordis` | `^4.0.2` |
200
242
  | `@deepseek-ai/schemastery` | `^3.18.2` |
201
243
  | Node.js | `>=22.19.0` |
202
244
 
203
- `0.1.2-alpha.2` 是这组 API 的最低版本:宿主设置注册使用
204
- `settings.installSection`,浏览器端设置卡通过插件自有 HTTP 路由
205
- (`/api/dsh-engram/settings`,见上文 Route B)读写。
206
- `0.1.2-alpha.2` 和 `0.1.3-alpha.2` 已完成宿主导入与设置注册 smoke 验证;
207
- `0.1.5-alpha.1` 已完成完整回归(并在 `dsh 0.1.5-rc.1` 隔离 profile 上真机验证
208
- 设置 GET/PUT/冲突/复位/跨重启持久化全通)。旧的 `0.1.0-rc.7` 与 `0.1.1` API 图不在
245
+ `0.1.2-alpha.2` 是这组 API 的最低版本:宿主设置注册按代际**特性检测**——DSH `≤0.1.5`
246
+ 走 `settings.installSection` 注册 `dsh-engram` 命名空间;`≥0.1.7` / `0.2.x` 走插件
247
+ `Config`(27 个 GUI 旋钮标记为 `.volatile()`,条目 id `engram`)。浏览器设置卡在两代际
248
+ 都经插件自有 HTTP 路由(`/api/dsh-engram/settings`,见上文 Route B)读写。完整回归
249
+ (npm test 329/329,含新增的 volatile-Config 模型 settings-v2 覆盖)在 DSH
250
+ `0.2.0-rc.2` 依赖集(cordis 4.0.4 + schemastery 3.18.4 + cosmokit,与全局 DSH CLI 同版本)
251
+ 上通过;`.volatile()`/loader 热提交路径与 describe 侧投影已针对真实 `dsh-settings
252
+ 0.2.0-rc.2` 工具函数验证;并在隔离的 `dsh 0.2.0-rc.2` profile 上完成了**真机启动烟测并
253
+ 通过**:插件启动无版本范围拒绝、设置卡表单以条目 `engram` 对外提供(全部 27 个 GUI
254
+ 旋钮,含 `trustedHosts`)、`/api/dsh-engram/settings` 的 GET → PUT(mutate ops,
255
+ revision 0→1)→ unset 往返落盘并读回。旧的 `0.1.0-rc.7` 与 `0.1.1` API 图不在
209
256
  本发布版本的兼容范围内。
210
257
 
211
258
  如需按验证版本安装 DSH:
212
259
 
213
260
  ```sh
214
- npm install --global @deepseek-ai/dsh@0.1.5-alpha.1
261
+ npm install --global @deepseek-ai/dsh@0.2.0-rc.2
215
262
  dsh --version
216
263
  ```
217
264
 
@@ -333,7 +380,7 @@ npm run build:client
333
380
  ## 测试与评测
334
381
 
335
382
  ```sh
336
- npm test # 279 项单元测试(含 usage 滚动 / /stats 路由 / Context GC / ESR 触发 / BoundaryPrune)
383
+ npm test # 311 项单元测试(含 usage 滚动 / /stats 路由 / Context GC / ESR 触发 / BoundaryPrune)
337
384
  npm run eval # 离线召回 + 结构基准(确定性语料,跑真实 store/recall 路径)
338
385
  ```
339
386
 
@@ -518,9 +565,48 @@ Context GC 管「压缩的摘要内容」,BoundaryPrune 管「确定性剪枝
518
565
  workspace 历史先验 + 超过后 ×2 外推(有界乐观),长会话不会被挡死。
519
566
  - **诚实激活面**:goal=0 且无 todo 推进的会话天然不触发(无边界不剪,零副作用);
520
567
  全量干跑 77 会话激活 38%。pruner 服务缺席即自动禁用并打一条日志。
568
+ - **边界源**:默认 todo 完成推进 / goal 终态事件;可选**空闲步数边界**
569
+ (`boundaryPruneIdleCadence`,默认 0=关)——覆盖约 62% 从不动 todo/goal 的
570
+ 会话(侦察:`eval/recon-idle-boundary.mjs`;反事实:`eval/simulate-idle-cadence.mjs`
571
+ 显示 cadence 50 时节省 +85%),仍受偿还门约束。
572
+ - **阈值覆盖**:`boundaryPruneThresholdChars`(默认 0=宿主 8192)。设为 2048
573
+ 并配更小保留边缘(`boundaryPruneHeadChars`/`boundaryPruneTailChars`)可更早
574
+ 开剪——T2048+小边缘在真实语料上首剪节省是宿主默认的 2.4 倍(10.14M vs 4.23M
575
+ 字符)。降阈值在 `resultPack` 开启时安全(被剪中段有归档兜底)。
521
576
  - 默认**关**:`boundaryPrune: false`。开启后启动日志出现
522
577
  `engram boundary-prune (todo @N reqs): pruned …`。
523
578
 
579
+ ### ResultPack L2(结果归档 + `result_recall`,默认关)
580
+
581
+ 让 BoundaryPrune 剪枝**无损**:每个被剪工具结果的被移除中段,按与记忆写入
582
+ 相同的规则脱敏后归档到 `~/.dsh/engram/resultpack/<callId>.json`,并在剪枝
583
+ 节点尾部追加 handle——`[middle archived: result_recall(<callId>) 可取]`——
584
+ 模型知道完整结果就在一步之遥。`result_recall(callId, offset?)` 按页返回
585
+ 归档中段(渐进披露,与 `[ENGRAM]` → `engram_detail` 同一哲学,也是 SoL-Pi
586
+ 机制③ ObservationPack 的完整形态)。
587
+
588
+ - **构造性无损**:L1 剪掉的是重放、L2 保留的是可随时取回的原文——「模型回头
589
+ 找被剪结果」的失败模式归零。
590
+ - **成本受控**:归档在记忆域之外(大块内容永远不会撑大 domain JSON 或
591
+ [ENGRAM] 索引);保留期 `resultPackRetentionDays`(默认 7 天)按 mtime 清理,
592
+ 搭 GC 同一节奏。
593
+ - **自我禁用**:只开 L2 不开 L1 将无内容可归档,故不注册工具并打一条警告。
594
+ - 默认**关**:`resultPack: false`(须与 `boundaryPrune: true` 同开)。开启后
595
+ 启动日志出现 `engram result-pack enabled: result_recall(callId, offset) registered`。
596
+
597
+ ### 激活遥测(SoL-Pi M15/C13)
598
+
599
+ 机制必须可证明在激活,休眠机制默认关。`/api/dsh-engram/stats`(以及 GUI
600
+ overview)现在返回 `activation: { boundaryPrune, recall }`:
601
+
602
+ - `boundaryPrune`——BoundaryPrune 真实 fire 的累计持久记录(fires /
603
+ prunedResults / charsRemoved / byKind todo|goal|step / lastFire),每次剪枝
604
+ 落盘到 `~/.dsh/engram/boundary-prune.status.json`,与 `resultPack` 无关。
605
+ - `recall`——召回漏斗的**注入腿**:实际进过会话的 `[RECALL]` 块数量
606
+ (`recall-injections.json`),与从 session log 已统计的另一端
607
+ (engram_recall 查询 → 命中 → engram_detail 下钻)闭合,可看到
608
+ 注入→查询→下钻的完整激活。
609
+
524
610
  ## 自动捕获策略
525
611
 
526
612
  捕获是确定性、离线的——只看到工具*结果*,从不看对话本身。什么会被记录成一条记忆:
@@ -591,6 +677,15 @@ promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-pr
591
677
  - id: engram
592
678
  config:
593
679
  autoCapture: true # 零 LLM 工具结果捕获
680
+ autoCapturePreferences: true # 零 LLM 用户纠正/偏好捕获(prefer/avoid/switch-to 句式)
681
+ preferenceCapturePerSession: 8 # 每会话偏好记忆条数上限
682
+ boundaryPrune: false # ResultPack L1:todo/goal 边界剪掉超阈值旧工具结果(默认关)
683
+ boundaryPruneThresholdChars: 0 # 阈值覆盖(0=宿主 8192;如 2048 可更早开剪)
684
+ boundaryPruneHeadChars: 0 # 覆盖时的保留头部(0=宿主 4096)
685
+ boundaryPruneTailChars: 0 # 覆盖时的保留尾部(0=宿主 1024)
686
+ boundaryPruneIdleCadence: 0 # 空闲步数边界(0=仅事件边界;覆盖无 todo/goal 会话)
687
+ resultPack: false # ResultPack L2:剪掉的中段脱敏归档 + result_recall 按页召回(默认关,须与 L1 同开)
688
+ resultPackRetentionDays: 7 # 归档保留天数(mtime 清理,走 GC 节奏)
594
689
  sessionSearch: true # engram_recall 也可对历史会话 FTS
595
690
  recallScope: workspace # 召回范围:workspace(严格工作区隔离,默认)| global(可选跨工作区召回,带 [W:<ws>] 来源标记)
596
691
  autoRecallOnStart: true # 会话启动自动召回:按首条消息注入 [RECALL] 块(false=回到纯拉取)
@@ -3,7 +3,7 @@
3
3
  > 依据:[SoL-Pi](https://nvlabs.github.io/SoL-Pi/) 四幸存机制之二(ObservationPack:归档载荷、上下文留 handle+摘录、按页召回)
4
4
  > 与提案池 C11「Reduce tool output before its first prompt insertion」/ C23「Build observation packs before paying for full bodies」。
5
5
  > 数据:[Oracle Analysis](./ORACLE-ANALYSIS.zh.md)(97 真实会话、13.9K 请求、15.0K 工具调用)。
6
- > **状态:L1 已实现(`lib/boundary-prune.js`,默认关;L0 见 §6,全量干跑见 §6.1)。L2 归档召回未实施。**
6
+ > **状态:L1 已实现(`lib/boundary-prune.js`,默认关;L0 见 §6,全量干跑见 §6.1)。L2 已实现(`lib/result-pack.js`,默认关)。**
7
7
 
8
8
  ---
9
9
 
@@ -56,6 +56,8 @@ L1 剪掉的中段今天直接丢弃(仅 session 日志保留原文,模型取不
56
56
  - 注册 `result_recall(callId, offset?)` 工具,按页返回归档中段 —— 与 [ENGRAM] 渐进披露同一哲学:索引行→详情下钻;
57
57
  - 剪枝结果尾部追加一行 handle:`[middle pruned: result_recall(callId) 可取]`,模型可自主决定是否下钻(M3 遥测将能看到激活率)。
58
58
 
59
+ > **已实现(2026-09-23,§8)**:实现细节与设计中两处修正——①归档落 `~/.dsh/engram/resultpack/<callId>.json`(独立文件而非域表,大块内容不撑 domain JSON);②handle 走宿主 surface-replace 协议(`session.append("tool/result", …, surfaceOp replace)`),replay-safe 零 LLM;脱敏先于持久化。17 项单测 + L1→L2 钩子 3 项 + 进程内端到端(归档→分页→缺档)验证通过。
60
+
59
61
  ### L3 · 上游提案(严格 C11)
60
62
 
61
63
  向 DSH 提 PR:ToolRuntime 开放 per-tool 可扩展 finalizeContent 链(或"结果归档 transport")。附上 oracle 数据(96% 集中在三个工具、top-10 占 46%)作为动机。
@@ -128,8 +130,10 @@ L1 剪掉的中段今天直接丢弃(仅 session 日志保留原文,模型取不
128
130
 
129
131
  ## 8. 后续
130
132
 
131
- - **L2(未实施)**:pruned 中段归档 + `result_recall` 按页召回 + handle 行;T2048 阈值与 L2 绑定。
132
- - **激活遥测**:fire 事件已可从 session log 的 `compaction/prune` 序列读出;建议 /stats 加一个「boundary-prune vs overflow-prune 来源区分」计数(用 fire 前后日志行归属判定)。
133
+ - ~~**L2**~~ **已实现**(2026-09-23, `lib/result-pack.js`):pruned 中段归档 + `result_recall` 按页召回 + handle 行;默认关(`resultPack`,须与 `boundaryPrune` 同开),保留 7 天走 GC;T2048 阈值与 L2 绑定,**现已满足开低阈值的归档前提**。
134
+ - ~~**激活遥测**~~ **已实现**(2026-09-23):`/stats` 与 GUI overview 返回 `activation: { boundaryPrune, recall }` —— BoundaryPrune fire 每次落盘 `~/.dsh/engram/boundary-prune.status.json`(fires/prunedResults/charsRemoved/byKind,与 resultPack 无关),[RECALL] 注入计数落盘 `recall-injections.json`,与 session-log 已统计的 recall 漏斗(查询→命中→detail 下钻)闭合(SoL-Pi M15/C13)。
135
+ - ~~**step 空转边界**~~ **已实现**(2026-09-23):`boundaryPruneIdleCadence`(默认 0=关)——请求计数边界,覆盖无 todo/goal 会话。侦察 105 会话:65 个无边界、含 5.23M over-threshold 暴露(38%);反事实仿真(cadence 50):激活面 36→64 会话、首剪节省 4.23M→7.84M(+85%),仍受偿还门约束。
136
+ - **T2048 落地**(2026-09-23):`boundaryPruneThresholdChars`/`HeadChars`/`TailChars`(默认 0=宿主)——插件以 `Object.create(pruner)` 原型克隆宿主 pruner 实例、仅覆盖 config,宿主不变。真实语料干跑:T2048 H1024/T256 首剪 10.14M vs 宿主默认 4.23M(+2.4x)。
133
137
  - **上游提案(L3)**:ToolRuntime 开放 finalizeContent 链,附 oracle 数据。
134
138
 
135
139
 
@@ -0,0 +1,58 @@
1
+ ## dsh-engram v0.5.0
2
+
3
+ DSH 0.2.x 兼容修复版:`0.1.7` 起 `@deepseek-ai/dsh-settings` 移除了
4
+ `settings.installSection`,设置卡改为**条目式 volatile Config** 模型;本版本把设置
5
+ 绑定迁移到新模型(entry-id `engram`),并把声明范围修到能覆盖 DSH `0.2.x` 全系,
6
+ 使插件在最新 DSH 上恢复加载与设置卡读写。
7
+
8
+ ### 功能
9
+
10
+ - **DSH ≥0.1.7 / 0.2.x 设置模型支持** — 插件 `Config`(`lib/index.js` 导出的
11
+ `Config`)声明全部 48 个 DEFAULTS 键;27 个 GUI 旋钮为 `.volatile()` 字段,
12
+ 设置表单由 DSH 以 profile 条目 id `engram` 对外提供,卡片编辑经
13
+ `loader/volatile-update` **热提交**到运行配置(新会话即时生效,已冻结块保持
14
+ 冻结——插件既有契约)。其余键普通声明,profile patch 里的内部旋钮不再被校验丢弃。
15
+ - **双代际特性检测** — `installEngramSettings` 自动识别宿主:DSH `≤0.1.5`
16
+ 回退旧的 `settings.installSection` 命名空间(`dsh-engram`),行为与 0.4.0 完全
17
+ 一致;`≥0.1.7` / `0.2.x` 走新模型(无挂载调用)。HTTP wire 命名空间两代际都保持
18
+ `dsh-engram`,**客户端 bundle 零改动**。
19
+
20
+ ### 修复
21
+
22
+ - **版本范围**:`engines.dsh` + 5 个 `@deepseek-ai/dsh-*` peer 从
23
+ `>=0.1.2-alpha.2 <0.2.0-0` 修正为 `>=0.1.2-alpha.2 <0.3.0-0`——原范围把
24
+ DSH `0.2.0-rc.x` 全部排除(semver 预发布窗口),插件在最新 DSH 上被兼容门槛拒绝。
25
+ 新范围已用 DSH 自身的 `includePrerelease` 检查实测:`0.2.0-rc.2` 与整个 `0.2.x`
26
+ 线放行,`0.3.0-0` 拒绝。
27
+ - **宿主设置崩溃**:移除对已不存在的 `settings.installSection` 的无条件调用
28
+ (其在 `dsh-settings 0.1.7-alpha.2` 起被删除),改为特性检测。
29
+ - **`boundaryPruneThresholdChars` 默认 `null` → `0`**:与设置卡长期使用的
30
+ “0=用宿主 8192” 语义一致(`0` 与 `null` 在剪枝判定中等价),并让 describe 的
31
+ 表单值完整包含该键。
32
+
33
+ ### 其他
34
+
35
+ - 测试 329/329 通过(新增 `test/settings-v2.test.mjs` 6 个用例,覆盖 volatile
36
+ Config 校验/解包/热同步与两代际安装路径;`test/api.test.mjs` 随 ns 语义更新)。
37
+ - settings 投影与写路径针对真实 `dsh-settings 0.2.0-rc.2` 工具函数
38
+ (`volatileForm`/`projectForm`/`isVolatilePath`/`plainConfig`)验证;
39
+ 真实 cordis 4.0.4 集成验证 Config 校验与热提交。
40
+ - **真机烟测通过**(隔离 profile,`dsh 0.2.0-rc.2`,`--port 3099`):插件启动无
41
+ 版本拒绝、`/api/dsh-engram/settings` 提供 27 键表单、
42
+ GET → PUT(mutate,revision 0→1)→ unset 往返落盘并读回。
43
+ - `scripts/prepublish-check.mjs` 随新导出面与 peer 范围更新,`prepublishOnly`
44
+ 在 0.2.x peer 集上通过。
45
+
46
+ ## DSH 版本支持
47
+
48
+ - **支持的 DSH 范围**:>=0.1.2-alpha.2 <0.3.0-0
49
+ - **peer 声明**:
50
+ - `@deepseek-ai/dsh-compaction`: `>=0.1.2-alpha.2 <0.3.0-0`
51
+ - `@deepseek-ai/dsh-compaction-basic`: `>=0.1.2-alpha.2 <0.3.0-0`
52
+ - `@deepseek-ai/dsh-llm`: `>=0.1.2-alpha.2 <0.3.0-0`
53
+ - `@deepseek-ai/dsh-settings`: `>=0.1.2-alpha.2 <0.3.0-0`
54
+ - `@deepseek-ai/dsh-storage-domain`: `>=0.1.2-alpha.2 <0.3.0-0`
55
+ - **验证版本**:`dsh-v0.2.0-rc.2`(全局 DSH CLI,并针对真实
56
+ `dsh-settings` / `cordis` / `schemastery` 依赖集)
57
+ - **最后验证日期**:2026-10-15
58
+ - **安装方式已验证**:`dsh plugin --profile web add dsh-engram`
@@ -0,0 +1,54 @@
1
+ ## dsh-engram v0.5.1
2
+
3
+ 补丁版:修复会话日志扫描对**版本化文件名**的硬编码(issue #8,所有会话扫描
4
+ 功能静默返回 0),并补齐记忆查看器的**数据导出/导入**与**替换/冲突可视化**
5
+ (issue #3 收尾)。
6
+
7
+ ### 修复
8
+
9
+ - **会话扫描识别 `session.v<N>.jsonl.zstd`**(#8)—— `lib/usage.js`、
10
+ `lib/boundary-prune.js`、`lib/session-cjk.js` 曾硬编码 `session.jsonl.zstd`;
11
+ 当前 DSH 把会话格式版本写进文件名(`0.1.x` → `session.v3.jsonl.zstd`,
12
+ `0.2.x` → `session.v4.jsonl.zstd`),`statSync` 抛 `ENOENT` 后被循环 `continue`
13
+ 静默吞掉,导致:
14
+
15
+ - 设置页 → 遥测恒为「样本不足(0 次)」,`GET /stats`、`GET /toolstats` 全 0;
16
+ - BoundaryPrune 的 `workspaceHistory()`(L0 偿还输入)看不到任何会话;
17
+ - CJK 跨会话搜索恒返回 `[]`。
18
+
19
+ 新增 `sessionLogFile(dir)`:正则匹配 `session[.v<N>].jsonl.zstd`,取目录内
20
+ **mtime 最新**的一个(迁移会话可能 v3/v4/legacy 并存,最新 = 宿主正在追加的
21
+ 文件),无匹配时回退历史名、调用方保留原有 ENOENT 处理。四处调用点统一改用它。
22
+ 本机实测(14 天窗口):`files 0 → 17`、`events 0 → 643`。
23
+
24
+ ### 功能
25
+
26
+ - **数据备份面板**(#3)—— 设置页 → Engram 记忆 →「数据备份」:一键导出当前
27
+ 工作区(或全部)为 `dsh-engram/export` v1 JSON 并下载;导入支持 **dry-run 预览**
28
+ → `merge`(只补缺失,幂等)/ `replace`(需勾选确认,宿主要求
29
+ `confirm:"restore"`)。宿主端的 `GET /export`、`POST /import` 早已存在,本版只补
30
+ GUI 入口,GUI 与 API 走同一格式。
31
+ - **替换 / 冲突可视化**(#3)—— 记忆表给被替代的旧行挂「已替换」徽标、给新行挂
32
+ 「替代 #id」跳转;记忆详情面板新增「替换 / 冲突」双向链(出边来自记录的
33
+ `supersedes`/`contradicts`,入边由已加载列表反查),元数据露出
34
+ `source`/`conditions`/`filePath`。
35
+
36
+ ### 测试
37
+
38
+ - 334/334(新增 5 个 #8 用例:版本化/纯 legacy/v3+v4+legacy 混合/陈旧 legacy
39
+ 不遮蔽 v4/无关文件名不被误选,以及 CJK 版本化读取与 `workspaceHistory` 计数)。
40
+ - `npm run typecheck`、`npm run prepublish-check` 通过;`lib/client.js` 重新构建。
41
+
42
+ ## DSH 版本支持
43
+
44
+ - **支持的 DSH 范围**:>=0.1.2-alpha.2 <0.3.0-0
45
+ - **peer 声明**:
46
+ - `@deepseek-ai/dsh-compaction`: `>=0.1.2-alpha.2 <0.3.0-0`
47
+ - `@deepseek-ai/dsh-compaction-basic`: `>=0.1.2-alpha.2 <0.3.0-0`
48
+ - `@deepseek-ai/dsh-llm`: `>=0.1.2-alpha.2 <0.3.0-0`
49
+ - `@deepseek-ai/dsh-settings`: `>=0.1.2-alpha.2 <0.3.0-0`
50
+ - `@deepseek-ai/dsh-storage-domain`: `>=0.1.2-alpha.2 <0.3.0-0`
51
+ - **验证版本**:`dsh-v0.2.0-rc.2`(与 0.5.0 相同的宿主依赖集;本版只改会话日志
52
+ 解析与纯客户端界面,未触碰设置/注入路径)
53
+ - **最后验证日期**:2026-10-19
54
+ - **安装方式已验证**:`dsh plugin --profile web add dsh-engram`