dsh-memoir 0.7.0 → 0.8.0

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.en.md CHANGED
@@ -12,11 +12,13 @@
12
12
  No embeddings, vector database, or cloud memory service. The npm package has zero bundled runtime dependencies; DSH peers are supplied by the host.
13
13
 
14
14
  > [!IMPORTANT]
15
- > `dsh-memoir@0.7.0` is a compatibility-only release for DSH **0.1.5-rc.1**, with no new features. It requires `>=0.1.5-rc.1 <0.1.6-0`; upgrade DSH first. Keep `dsh-memoir@0.6.2` on DSH 0.1.2, or `0.5.6` on DSH 0.1.1-rc.2.
15
+ > **0.8.0 requires DSH `>=0.1.7-rc.1 <0.1.8-0`**, tested against `0.1.7-rc.1` with Windows/WSL regressions and an isolated Windows host. Pin `dsh-memoir@0.7.1` on legacy DSH 0.1.5 instead of upgrading to latest. Real-browser interaction and paid-model end-to-end validation remain unverified.
16
+ >
17
+ > `dsh-memoir@0.7.1` fixes lost session snapshots after restart or RAM eviction (#10), supporting DSH **0.1.5-rc.1 / rc.2**. It requires `>=0.1.5-rc.1 <0.1.6-0`; check your host first. Keep `0.6.2` on DSH 0.1.2, or `0.5.6` on DSH 0.1.1-rc.2; those older lines do not include this fix.
16
18
 
17
19
  ```bash
18
- npm install --global @deepseek-ai/dsh@0.1.5-rc.1
19
- dsh plugin --profile web add dsh-memoir@latest
20
+ npm install --global @deepseek-ai/dsh@0.1.7-rc.1
21
+ dsh plugin --profile web add dsh-memoir@0.8.0
20
22
  ```
21
23
 
22
24
  Restart `dsh web`. Memory remains local and is not automatically deleted when the plugin is updated or removed.
@@ -57,7 +59,16 @@ Complete history and Hot Memory are separate layers:
57
59
 
58
60
  - **Full Memory** retains every record for the GUI, human review, Markdown projection, and ranked retrieval.
59
61
  - **Hot Memory** selects only budgeted actions, lessons, and recent state; the full `PROJECT_MEMORY.md` is never injected.
60
- - **Session Snapshot** freezes injected text within a session. New writes are immediately readable by tools and the GUI, while automatic injection refreshes in the next new session.
62
+ - **Session Snapshot** durably freezes injected text per session and restores it after restart or RAM eviction. New writes remain immediately readable by tools and the GUI; automatic injection refreshes in the next new session. Recovery failures explicitly report degraded persistence.
63
+
64
+ ### Snapshot recovery and cleanup (0.7.1)
65
+
66
+ - Default directory: `$DSH_HOME/dsh-memoir.json.snapshots/`, or `<storePath>.snapshots/` with a custom store. Records are partitioned by data-source/settings hash, language, and session hash. They load on demand without scanning the whole directory at startup.
67
+ - `sessionSnapshotMax` bounds RAM only. Disk records have no automatic TTL and survive shrinking, uninstalling, or clearing RAM. Include them in backups. To reclaim disk, stop the relevant DSH processes, back up first, and manually remove records you no longer need to resume. Access after removal establishes a new baseline.
68
+ - Languages use separate namespaces; switching back restores the original baseline for that language. Budget changes affect new baselines only. Forks/new session IDs do not inherit the parent's frozen record.
69
+ - Old sessions whose snapshots were lost before upgrading establish a baseline from current memory once. Memoir does not guess or slice original text out of historical system prompts. Corrupt, inaccessible, or locked records remain unchanged; fallback freezes only in the current process, with diagnostics and a log warning.
70
+ - Text is capped at 256 KiB and each record at 2 MiB; exceeding either reports the same degradation. New POSIX directories/files use 0700/0600; Windows access remains governed by directory ACLs. Snapshot files contain memory text and are user data.
71
+ - This fix prevents rebuilding recoverable snapshots; it cannot guarantee provider KV-cache retention or hit rates.
61
72
 
62
73
  ## Agent tools and memory lifecycle
63
74
 
@@ -79,7 +90,7 @@ Similar-memory governance starts with BM25 candidates, then combines title simil
79
90
 
80
91
  v0.6.2 diagnostics show the latest decision and process-local counters. Turns calling `memoir_record` or `memoir_update` skip reminders; submitting a reminder does not confirm persistence. Agent disposal clears gate state, with a fallback cap of 1024 recently active agents (eviction also forgets their turn watermark and cooldown). Manual recording remains available when automatic distillation is disabled.
81
92
 
82
- The validated baseline is DSH `0.1.5-rc.1` (npm has not published an unsuffixed 0.1.5). BM25 is lexical retrieval and does not guarantee cross-language semantic matches without shared terms. Distillation guidance does not replace fact checking.
93
+ 0.8.0 targets DSH `0.1.7-rc.1`; 0.7.1 was validated on `0.1.5-rc.1 / rc.2`. BM25 is lexical retrieval and does not guarantee cross-language semantic matches without shared terms. Distillation guidance does not replace fact checking.
83
94
 
84
95
  Automatic distillation is an observable agent turn-end reminder, not silent background scraping of every chat. The default `1 / 0 / 1` means every eligible worked turn, no extra cooldown, and at least one tool call.
85
96
 
@@ -117,6 +128,8 @@ Installing into a DSH-alpha `web` profile registers a native Memory Conversation
117
128
  <details>
118
129
  <summary>More GUI screenshots</summary>
119
130
 
131
+ ![v0.7.1 durable snapshot diagnostics on DSH rc.2](https://raw.githubusercontent.com/Qinling-Melon-Farmers/dsh-memoir/v0.7.1/picture/v0.7.1-snapshot-persistence-zh.png)
132
+
120
133
  ![v0.7.0 native memory settings on DSH 0.1.5-rc.1](https://raw.githubusercontent.com/Qinling-Melon-Farmers/dsh-memoir/v0.7.0/picture/v0.7.0-dsh015-settings-zh.png)
121
134
 
122
135
  ![v0.6.2 automatic distillation lifecycle diagnostics](https://raw.githubusercontent.com/Qinling-Melon-Farmers/dsh-memoir/v0.6.2/picture/v0.6.2-distill-diagnostics-zh.png)
@@ -139,20 +152,21 @@ Installing into a DSH-alpha `web` profile registers a native Memory Conversation
139
152
 
140
153
  | Channel | DSH baseline | Installation | Status |
141
154
  | --- | --- | --- | --- |
142
- | npm `latest` (`0.7.0`) | `>=0.1.5-rc.1 <0.1.6-0` | `dsh plugin --profile web add dsh-memoir@latest` | Compatibility release validated on 0.1.5-rc.1 |
155
+ | npm `latest` (`0.8.0`) | `>=0.1.7-rc.1 <0.1.8-0` | `dsh plugin --profile web add dsh-memoir@0.8.0` | DSH 0.1.7 line |
156
+ | pinned npm `0.7.1` | `>=0.1.5-rc.1 <0.1.6-0` | `dsh plugin --profile web add dsh-memoir@0.7.1` | Legacy 0.1.5 line |
143
157
  | pinned npm `0.6.2` | `>=0.1.2-alpha.2 <0.1.3` | `dsh plugin --profile web add dsh-memoir@0.6.2` | Legacy 0.1.2 line |
144
158
  | pinned npm `0.5.6` | `0.1.1-rc.2` | `dsh plugin --profile web add dsh-memoir@0.5.6` | rc2 compatibility line |
145
- | GitHub `main` (`0.7.0`) | `>=0.1.5-rc.1 <0.1.6-0` | source clone + `link:` | Development and debugging |
159
+ | Source `v0.8.0` | `>=0.1.7-rc.1 <0.1.8-0` | local build + `link:` | Development; not compatible with legacy 0.1.5 / 0.1.6 |
146
160
 
147
- Node.js `^22.19.0 || >=24.0.0` is required. 0.7.0 keeps the native `conversation.view` / `settings.section` slots and `snapshotEvents()`. DSH 0.1.5 uses Session log V3, independent of Memoir store v4 / settings v3. Back up DSH_HOME before upgrading DSH; migrated sessions are not guaranteed readable by older hosts. This Memoir release neither migrates nor resets memory and retains frozen session snapshots without enabling new dynamic-prompt behavior.
161
+ Node.js `^22.19.0 || >=24.0.0` is required. 0.7.1 keeps the native `conversation.view` / `settings.section` slots and `snapshotEvents()`. DSH 0.1.5 uses Session log V3, independent of Memoir store v4 / settings v3. Back up DSH_HOME before upgrading DSH; migrated sessions are not guaranteed readable by older hosts. This Memoir release neither migrates nor resets memory and retains frozen session snapshots without enabling new dynamic-prompt behavior.
148
162
 
149
163
  <details>
150
164
  <summary>Install from source</summary>
151
165
 
152
- 0.7.x source:
166
+ Published 0.7.1 source (legacy DSH 0.1.5):
153
167
 
154
168
  ```bash
155
- git clone https://github.com/Qinling-Melon-Farmers/dsh-memoir.git
169
+ git clone --branch v0.7.1 https://github.com/Qinling-Melon-Farmers/dsh-memoir.git
156
170
  cd dsh-memoir
157
171
  pnpm install --frozen-lockfile
158
172
  pnpm run build
@@ -162,6 +176,8 @@ dsh plugin --profile web add "link:/absolute/path/dsh-memoir"
162
176
 
163
177
  </details>
164
178
 
179
+ 0.8.0 uses native `uiWorkspace` navigation, `conversation.view` / `settings.section`, and a producer-owned Session V4 distillation source. Source links open the session and turn IDs remain copyable; global DOM turn scrolling is removed to avoid targeting another conversation. `snapshotEvents()` remains in use (deprecated but still available); migration to public asynchronous projections is planned. Memoir data formats are unchanged. Back up DSH_HOME before upgrading: DSH Session V4 migration is separate from plugin memory storage.
180
+
165
181
  ## Storage, privacy, and security boundaries
166
182
 
167
183
  ```text
@@ -196,7 +212,7 @@ Every field below can be set in the memoir `config` row in `cordis.patch.yml`. E
196
212
  | `hotMemoryMaxTokens` | `1200` | hard ceiling that no session exceeds |
197
213
  | `readDefaultLimit` | `8` | default `memoir_read` result count |
198
214
  | `readMaxLimit` | `30` | live upper bound per recall |
199
- | `sessionSnapshotMax` | `128` | frozen session-snapshot LRU capacity |
215
+ | `sessionSnapshotMax` | `128` | resident LRU capacity; durable records are not deleted |
200
216
  | `queryCacheSize` | `128` | BM25 query LRU capacity |
201
217
 
202
218
  Shrinking a cache evicts the oldest entries immediately. Existing frozen sessions are not rewritten after budget changes, preserving prompt-prefix stability. “Restore startup configuration” removes Web overrides and returns to profile startup values.
@@ -213,7 +229,9 @@ v0.5.6 benchmark (Node 24.19, 900/1200-token budget; full data in [`bench/report
213
229
 
214
230
  Numbers vary by machine and corpus. The important properties are that injection remains bounded and the cache-hit path is independent of total memory size.
215
231
 
216
- 0.7.0 passes 193 tests and type checking, retaining all existing regressions and adding an official DSH Session V3 object test for provenance, distillation, tool writes, and frozen injection. An isolated DSH 0.1.5-rc.1 profile passes Web Settings, bilingual diagnostics, and API checks. Historical alpha/rc validation is recorded in the changelog and does not imply 0.7.0 can be installed on older hosts.
232
+ 0.8.0: Windows passes 206 of 207 tests with one skip; WSL passes all 207. Typecheck/build pass on both. Three Cordis lifecycle cycles and client slot/style/language-observer cleanup pass, as do the isolated Windows page, official client combo and Memoir APIs. Real-browser interaction and paid-model end-to-end validation remain unverified; historical screenshots are not visual evidence for this version.
233
+
234
+ Historical 0.7.1 has 202 tests: 201 pass on Windows with one POSIX permission test skipped; all pass on Linux. Coverage includes actual process restart, same-session concurrent creation, LRU, empty baselines, forks, language isolation, corruption and permission errors, plus existing BM25/Hot Memory/tool regressions. DSH rc.1 and the newest rc.2 are validated. Prefix equality in tests is not a guarantee of actual billing savings.
217
235
 
218
236
  ## FAQ
219
237
 
@@ -242,6 +260,6 @@ pnpm test
242
260
  npm run bench
243
261
  ```
244
262
 
245
- Read [CONTRIBUTING.md](./CONTRIBUTING.md) before submitting changes. See [CHANGELOG.md](./CHANGELOG.md) for version history. Formal packages are published by the tag workflow through npm OIDC. The current npm release is [v0.7.0](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.7.0), and `main` is synchronized with it.
263
+ Read [CONTRIBUTING.md](./CONTRIBUTING.md) before submitting changes. See [CHANGELOG.md](./CHANGELOG.md) for version history. Formal packages are published by the tag workflow through npm OIDC. The current version is [v0.8.0](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.8.0), targeting DSH 0.1.7. Keep 0.7.1 on legacy DSH 0.1.5.
246
264
 
247
265
  Apache-2.0
package/README.md CHANGED
@@ -11,11 +11,13 @@
11
11
  无需 embedding、向量数据库或云端记忆服务;npm 包零捆绑运行时依赖,DSH peer 由宿主提供。
12
12
 
13
13
  > [!IMPORTANT]
14
- > `dsh-memoir@0.7.0` 是 DSH **0.1.5-rc.1** 的兼容性发布,不增加新功能。要求 `>=0.1.5-rc.1 <0.1.6-0`;请先升级 DSH。旧 DSH 0.1.2 用户固定使用 `dsh-memoir@0.6.2`,0.1.1-rc.2 用户固定使用 `0.5.6`。
14
+ > **0.8.0 要求 DSH `>=0.1.7-rc.1 <0.1.8-0`**,已针对 `0.1.7-rc.1` 完成 Windows/WSL 自动化回归与 Windows 隔离宿主验证。旧 DSH 0.1.5 请固定安装 `dsh-memoir@0.7.1`,不要直接升级 latest。真实浏览器交互与付费模型端到端验证尚未完成。
15
+ >
16
+ > `dsh-memoir@0.7.1` 修复重启和内存淘汰后旧会话快照丢失(#10),支持 DSH **0.1.5-rc.1 / rc.2**。要求 `>=0.1.5-rc.1 <0.1.6-0`;请先核对宿主版本。旧 DSH 0.1.2 用户固定使用 `0.6.2`,0.1.1-rc.2 用户固定使用 `0.5.6`;这些旧版未包含本次修复。
15
17
 
16
18
  ```bash
17
- npm install --global @deepseek-ai/dsh@0.1.5-rc.1
18
- dsh plugin --profile web add dsh-memoir@latest
19
+ npm install --global @deepseek-ai/dsh@0.1.7-rc.1
20
+ dsh plugin --profile web add dsh-memoir@0.8.0
19
21
  ```
20
22
 
21
23
  重启 `dsh web` 即可。记忆保存在本机,不会随插件升级或卸载自动删除。
@@ -56,7 +58,16 @@ memoir_record / memoir_update
56
58
 
57
59
  - **Full Memory** 保留全部记录,用于 GUI、人工审阅、Markdown 投影和排序检索。
58
60
  - **Hot Memory** 只选择预算内的 actions、lessons 与 recent state;不会把整个 `PROJECT_MEMORY.md` 塞进 prompt。
59
- - **Session Snapshot** 在会话内冻结注入文本。新写入立即可被工具和 GUI 读取,但自动注入从下一个新会话开始更新。
61
+ - **Session Snapshot** 按会话持久化冻结注入文本,重启恢复与内存淘汰后仍复用原文。新写入立即可被工具和 GUI 读取,但自动注入从下一个新会话开始更新;恢复失败会显式报告降级。
62
+
63
+ ### 快照恢复与清理(0.7.1)
64
+
65
+ - 默认目录:`$DSH_HOME/dsh-memoir.json.snapshots/`;自定义 storePath 时为 `<storePath>.snapshots/`。记录按数据源/设置文件的哈希、语言和会话哈希分开保存;按需读取,不在启动时加载全部文件。
66
+ - `sessionSnapshotMax` 只限制内存 LRU。磁盘记录无自动 TTL,不随缩容、卸载或清理内存删除;备份记忆时请一起备份该目录。需要回收磁盘时先停止相关 DSH 进程并备份,再人工删除确定不再恢复的记录。删除后再次访问会建立新基线。
67
+ - 语言切换使用独立快照空间;切回原语言会复用其旧基线。预算修改仅影响新基线;fork/新 session id 不借用父会话快照。
68
+ - 升级前已丢失快照的旧会话,首次使用新版只能按当前记忆建立一次新基线;不从历史 system prompt 猜测截取原文。读取损坏、权限或锁失败时保留原文件,回退到进程内冻结;诊断页和日志会提示重启稳定性降级。
69
+ - 单条文本上限 256 KiB,记录上限 2 MiB;超限走同样的可诊断降级。POSIX 新目录/记录使用 0700/0600,Windows 权限仍由目录 ACL 管理。记录含记忆文本,应视为用户数据。
70
+ - 本修复消除可恢复快照的重复重建,不能保证提供商仍保留 KV cache 或保证命中率。
60
71
 
61
72
  ## Agent 工具与记忆生命周期
62
73
 
@@ -78,7 +89,7 @@ memoir_record / memoir_update
78
89
 
79
90
  v0.6.2 的诊断页显示最近触发或跳过原因及本次进程计数。已经调用 `memoir_record` 或 `memoir_update` 的回合不再提醒;提交提醒不代表写入已完成。Agent 销毁会清理门控状态,最多保留 1024 个最近活动 Agent(淘汰后不再保留其回合水位和冷却)。关闭自动蒸馏后仍可手动记录。
80
91
 
81
- 当前验证基线为 DSH `0.1.5-rc.1`(npm 尚无不带后缀的 0.1.5)。BM25 是词项召回,不能保证无共同词项的跨语言语义匹配;提炼质量提示也不能替代事实核验。
92
+ 0.8.0面向 DSH `0.1.7-rc.1`;0.7.1 的历史验证范围为 `0.1.5-rc.1 / rc.2`。BM25 是词项召回,不能保证无共同词项的跨语言语义匹配;提炼质量提示也不能替代事实核验。
82
93
 
83
94
  自动蒸馏是可观察的 Agent 收尾提醒,不是后台静默抓取聊天内容。默认 `1 / 0 / 1` 表示:每个有效 worked turn、无额外冷却、至少一次工具调用即可提醒。
84
95
 
@@ -116,6 +127,8 @@ v0.6.2 的诊断页显示最近触发或跳过原因及本次进程计数。已
116
127
  <details>
117
128
  <summary>查看更多 GUI 截图</summary>
118
129
 
130
+ ![v0.7.1 在 DSH rc.2 中的快照持久化诊断](https://raw.githubusercontent.com/Qinling-Melon-Farmers/dsh-memoir/v0.7.1/picture/v0.7.1-snapshot-persistence-zh.png)
131
+
119
132
  ![v0.7.0 在 DSH 0.1.5-rc.1 中的原生记忆设置](https://raw.githubusercontent.com/Qinling-Melon-Farmers/dsh-memoir/v0.7.0/picture/v0.7.0-dsh015-settings-zh.png)
120
133
 
121
134
  ![v0.6.2 自动蒸馏生命周期诊断](https://raw.githubusercontent.com/Qinling-Melon-Farmers/dsh-memoir/v0.6.2/picture/v0.6.2-distill-diagnostics-zh.png)
@@ -138,20 +151,21 @@ v0.6.2 的诊断页显示最近触发或跳过原因及本次进程计数。已
138
151
 
139
152
  | 渠道 | DSH 基线 | 安装方式 | 状态 |
140
153
  | --- | --- | --- | --- |
141
- | npm `latest`(`0.7.0`) | `>=0.1.5-rc.1 <0.1.6-0` | `dsh plugin --profile web add dsh-memoir@latest` | 当前兼容性发布;验证基线 0.1.5-rc.1 |
154
+ | npm `latest`(`0.8.0`) | `>=0.1.7-rc.1 <0.1.8-0` | `dsh plugin --profile web add dsh-memoir@0.8.0` | 0.1.7 兼容线 |
155
+ | npm 固定版 `0.7.1` | `>=0.1.5-rc.1 <0.1.6-0` | `dsh plugin --profile web add dsh-memoir@0.7.1` | 旧 0.1.5 维护线 |
142
156
  | npm 固定版 `0.6.2` | `>=0.1.2-alpha.2 <0.1.3` | `dsh plugin --profile web add dsh-memoir@0.6.2` | 旧 0.1.2 兼容线 |
143
157
  | npm 固定版 `0.5.6` | `0.1.1-rc.2` | `dsh plugin --profile web add dsh-memoir@0.5.6` | rc2 兼容线 |
144
- | GitHub `main`(`0.7.0`) | `>=0.1.5-rc.1 <0.1.6-0` | 源码 clone + `link:` | 开发和调试使用 |
158
+ | 源码 `v0.8.0` | `>=0.1.7-rc.1 <0.1.8-0` | 本地构建 + `link:` | 开发调试,不兼容旧 0.1.5 / 0.1.6 |
145
159
 
146
- 需要 Node.js `^22.19.0 || >=24.0.0`。0.7.0 继续使用原生 `conversation.view` / `settings.section` 与 `snapshotEvents()`。DSH 0.1.5 的会话日志升级至 V3;其迁移与 Memoir 的 store v4 / settings v3 是独立格式。升级 DSH 前备份 DSH_HOME,迁移后的 DSH 会话不能承诺被旧宿主读取。Memoir 本次不迁移或清空记忆,也不启用新动态提示词行为;既有会话快照语义保持不变。
160
+ 需要 Node.js `^22.19.0 || >=24.0.0`。0.7.1 继续使用原生 `conversation.view` / `settings.section` 与 `snapshotEvents()`。DSH 0.1.5 的会话日志升级至 V3;其迁移与 Memoir 的 store v4 / settings v3 是独立格式。升级 DSH 前备份 DSH_HOME,迁移后的 DSH 会话不能承诺被旧宿主读取。Memoir 本次不迁移或清空记忆,也不启用新动态提示词行为;既有会话快照语义保持不变。
147
161
 
148
162
  <details>
149
163
  <summary>从源码安装</summary>
150
164
 
151
- 0.7.x 源码:
165
+ 已发布 0.7.1 源码(旧 DSH 0.1.5):
152
166
 
153
167
  ```bash
154
- git clone https://github.com/Qinling-Melon-Farmers/dsh-memoir.git
168
+ git clone --branch v0.7.1 https://github.com/Qinling-Melon-Farmers/dsh-memoir.git
155
169
  cd dsh-memoir
156
170
  pnpm install --frozen-lockfile
157
171
  pnpm run build
@@ -161,6 +175,8 @@ dsh plugin --profile web add "link:/absolute/path/dsh-memoir"
161
175
 
162
176
  </details>
163
177
 
178
+ 0.8.0使用原生 `uiWorkspace` 导航、`conversation.view` / `settings.section` 和 Session V4 专属蒸馏来源。来源链接打开会话,回合编号可复制;不再通过全局 DOM 自动滚到回合,避免多会话串扰。`snapshotEvents()` 仍在使用(宿主已标记弃用但尚未移除),公开异步 projection 迁移列入后续版本。Memoir 数据格式不变;升级 DSH 前备份 DSH_HOME,其 Session V4 迁移与插件记忆迁移是两回事。
179
+
164
180
  ## 存储、隐私与安全边界
165
181
 
166
182
  ```text
@@ -195,7 +211,7 @@ dsh plugin --profile web add "link:/absolute/path/dsh-memoir"
195
211
  | `hotMemoryMaxTokens` | `1200` | 任何会话都不能超过的硬上限 |
196
212
  | `readDefaultLimit` | `8` | `memoir_read` 默认结果数 |
197
213
  | `readMaxLimit` | `30` | 单次召回实时上限 |
198
- | `sessionSnapshotMax` | `128` | 会话冻结快照 LRU 容量 |
214
+ | `sessionSnapshotMax` | `128` | 内存快照 LRU 容量,不删除磁盘快照 |
199
215
  | `queryCacheSize` | `128` | BM25 查询 LRU 容量 |
200
216
 
201
217
  缩小缓存容量会立即淘汰最旧项;已冻结会话不会因预算修改而重写,以维持 prompt 前缀稳定。“恢复启动配置”会删除 Web 覆盖并回到 profile 的启动值。
@@ -212,7 +228,9 @@ v0.5.6 基准(Node 24.19,900/1200 token;完整数据见 [`bench/report.md`
212
228
 
213
229
  基准值取决于机器和语料;它证明的重点是注入预算保持有界、缓存命中路径与记忆总量解耦。
214
230
 
215
- 0.7.0 的 193 项测试与类型检查通过,保留全部既有回归,并新增使用官方 DSH Session V3 对象的来源、蒸馏、工具写入与冻结注入测试。隔离 DSH 0.1.5-rc.1 的 Web 设置、双语诊断与 API 回归通过。旧版 alpha/rc 的历史验证见各版本更新日志;不据此宣称 0.7.0 可安装在旧宿主。
231
+ 0.8.0:Windows 207 项测试中 206 通过、1 项跳过,WSL 207 项全部通过;双端类型检查/构建通过。三轮 Cordis 生命周期与客户端 slots/样式/语言 observer 清理测试通过,Windows 隔离宿主页、官方客户端组合资源及 Memoir API 通过。未完成真实浏览器交互和付费模型端到端验收;历史截图不是本版实机证据。
232
+
233
+ 历史 0.7.1 含 202 项测试:Windows 201 项通过、1 项 POSIX 权限测试跳过;Linux 全部通过。覆盖实际跨进程快照恢复、同会话并发写入、LRU、空基线、fork、语言隔离、损坏与权限失败,以及原有 BM25/Hot Memory/工具回归。已核验 DSH rc.1 和最新 rc.2;没有把测试前缀一致性等同于实际账单节省保证。
216
234
 
217
235
  ## 常见问题
218
236
 
@@ -241,6 +259,6 @@ pnpm test
241
259
  npm run bench
242
260
  ```
243
261
 
244
- 提交前请阅读 [CONTRIBUTING.md](./CONTRIBUTING.md)。版本变化见 [CHANGELOG.md](./CHANGELOG.md),正式包由 tag 工作流通过 npm OIDC 发布。当前 npm 正式版是 [v0.7.0](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.7.0),`main` 与该版本同步。
262
+ 提交前请阅读 [CONTRIBUTING.md](./CONTRIBUTING.md)。版本变化见 [CHANGELOG.md](./CHANGELOG.md),正式包由 tag 工作流通过 npm OIDC 发布。当前版本是 [v0.8.0](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.8.0),面向 DSH 0.1.7;旧 0.1.5 保留 0.7.1。
245
263
 
246
264
  Apache-2.0
package/cordis.patch.yml CHANGED
@@ -1,8 +1,7 @@
1
1
  # dsh-memoir bundle patch: inserts the host-half plugin row into the web
2
2
  # profile roster. Applied as a profile bundle layer (the `dsh.bundle.patch`
3
- # manifest field) over dsh-base. dsh-memoir v0.7.0 targets the published DSH
4
- # line >=0.1.5-rc.1 <0.1.6-0 and installs from npm with
5
- # dsh plugin --profile web add dsh-memoir@latest
3
+ # manifest field) over dsh-base. dsh-memoir v0.8.0 targets the DSH
4
+ # line >=0.1.7-rc.1 <0.1.8-0. Keep Memoir 0.7.1 on older DSH 0.1.5.
6
5
  #
7
6
  # The row is a bare plugin by package name: the node half (exports ".")
8
7
  # registers the memoir_record / memoir_read tools, the /api/dsh-memoir panel
@@ -11,6 +11,13 @@
11
11
  */
12
12
  import type { UserMessage } from '@deepseek-ai/dsh-llm';
13
13
  import type { MemoirLanguage } from './i18n.js';
14
+ declare module '@deepseek-ai/dsh-llm/message' {
15
+ interface MessageSourceMap {
16
+ 'dsh-memoir': {
17
+ kind: 'dsh-memoir';
18
+ };
19
+ }
20
+ }
14
21
  /** The steering prompt injected at the end of an active turn. */
15
22
  export declare function distillPrompt(language?: MemoirLanguage): string;
16
23
  /** Backwards-compatible Chinese prompt constant. */
@@ -155,7 +155,7 @@ export function installAutoDistill(wire, options) {
155
155
  try {
156
156
  agent.steer(createUserMessage({
157
157
  content: [{ type: 'text', text: distillPrompt(options.language?.()) }],
158
- source: { kind: 'plugin', plugin: AUTO_DISTILL_PLUGIN },
158
+ source: { kind: AUTO_DISTILL_PLUGIN },
159
159
  }));
160
160
  }
161
161
  catch (error) {
package/lib/client.js CHANGED
@@ -157,6 +157,18 @@ var MemoirApi = class {
157
157
  // src/client/i18n.ts
158
158
  var dictionaries = {
159
159
  zh: {
160
+ "snapshot.disk": "\u4F1A\u8BDD\u5FEB\u7167\u6301\u4E45\u5316",
161
+ "snapshot.restored": "\u4ECE\u78C1\u76D8\u6062\u590D",
162
+ "snapshot.created": "\u5EFA\u7ACB\u65B0\u57FA\u7EBF",
163
+ "snapshot.failures": "\u6301\u4E45\u5316\u5931\u8D25",
164
+ "snapshot.last": "\u6700\u8FD1\u78C1\u76D8\u64CD\u4F5C",
165
+ "snapshot.idle": "\u5C1A\u672A\u8BFB\u53D6",
166
+ "snapshot.volatile": "\u964D\u7EA7\u4E3A\u8FDB\u7A0B\u5185\u5FEB\u7167",
167
+ "snapshot.degraded": "\u65E0\u6CD5\u4FDD\u8BC1\u91CD\u542F\u540E\u524D\u7F00\u4E00\u81F4\uFF1B\u8BF7\u68C0\u67E5\u5FEB\u7167\u5B58\u50A8",
168
+ "snapshot.invalid-record": "\u683C\u5F0F\u6216\u6821\u9A8C\u4E0D\u7B26\uFF0C\u539F\u6587\u4EF6\u5DF2\u4FDD\u7559",
169
+ "snapshot.record-too-large": "\u8D85\u51FA\u5FEB\u7167\u5927\u5C0F\u4E0A\u9650",
170
+ "snapshot.unavailable": "\u8BFB\u5199\u4E0D\u53EF\u7528\u6216\u9501\u8D85\u65F6",
171
+ "snapshot.retention": "LRU \u4EC5\u9650\u5236\u5185\u5B58\uFF1B\u78C1\u76D8\u5FEB\u7167\u65E0\u81EA\u52A8\u8FC7\u671F\u3002\u65E7\u4F1A\u8BDD\u9996\u6B21\u5347\u7EA7\u53EF\u80FD\u5EFA\u7ACB\u4E00\u6B21\u65B0\u57FA\u7EBF\uFF1B\u4E0D\u4FDD\u8BC1\u670D\u52A1\u7AEF\u7F13\u5B58\u547D\u4E2D\u3002",
160
172
  "distill.title": "\u81EA\u52A8\u84B8\u998F\u751F\u547D\u5468\u671F",
161
173
  "distill.worked": "\u7D2F\u8BA1\u6709\u6548\u5DE5\u4F5C\u56DE\u5408",
162
174
  "distill.agents": "\u8DDF\u8E2A Agent",
@@ -229,7 +241,7 @@ var dictionaries = {
229
241
  "session": "\u4F1A\u8BDD",
230
242
  "source.label": "\u6765\u6E90",
231
243
  "source.turn": "\u56DE\u5408",
232
- "source.open": "\u6253\u5F00\u6765\u6E90\u4F1A\u8BDD\u5E76\u5B9A\u4F4D\u5230\u5BF9\u5E94\u56DE\u5408",
244
+ "source.open": "\u6253\u5F00\u6765\u6E90\u4F1A\u8BDD\uFF08\u56DE\u5408\u7F16\u53F7\u53EF\u590D\u5236\uFF09",
233
245
  "source.copy": "\u590D\u5236\u4F1A\u8BDD\u4E0E\u56DE\u5408\u6807\u8BC6",
234
246
  "source.copied": "\u5DF2\u590D\u5236",
235
247
  "sections.work": "\u5DE5\u4F5C\u8BB0\u5F55",
@@ -335,6 +347,18 @@ var dictionaries = {
335
347
  "settings.saveFailed": "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25"
336
348
  },
337
349
  en: {
350
+ "snapshot.disk": "Durable session snapshots",
351
+ "snapshot.restored": "Restored from disk",
352
+ "snapshot.created": "New baselines",
353
+ "snapshot.failures": "Persistence failures",
354
+ "snapshot.last": "Last disk operation",
355
+ "snapshot.idle": "Not read yet",
356
+ "snapshot.volatile": "Process-local fallback",
357
+ "snapshot.degraded": "Prefix stability across restart is not guaranteed; check snapshot storage",
358
+ "snapshot.invalid-record": "Invalid format or hash; original file preserved",
359
+ "snapshot.record-too-large": "Snapshot size limit exceeded",
360
+ "snapshot.unavailable": "Storage unavailable or lock timeout",
361
+ "snapshot.retention": "LRU bounds RAM only; disk snapshots do not auto-expire. Older sessions may establish a new baseline once after upgrade. Server-side cache hits are not guaranteed.",
338
362
  "distill.title": "Automatic distillation lifecycle",
339
363
  "distill.worked": "Eligible worked turns",
340
364
  "distill.agents": "Tracked agents",
@@ -407,7 +431,7 @@ var dictionaries = {
407
431
  "session": "session",
408
432
  "source.label": "Source",
409
433
  "source.turn": "turn",
410
- "source.open": "Open the source session and reveal its turn",
434
+ "source.open": "Open the source session (turn ID can be copied)",
411
435
  "source.copy": "Copy session and turn identifiers",
412
436
  "source.copied": "Copied",
413
437
  "sections.work": "Work Log",
@@ -1497,6 +1521,34 @@ ${projectSignature}`;
1497
1521
  "/",
1498
1522
  diag.snapshotMax
1499
1523
  ] }),
1524
+ diag.snapshotPersistence ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-dsh-part": "snapshot-persistence", children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { children: t("snapshot.disk") }),
1526
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1527
+ t("snapshot.restored"),
1528
+ ": ",
1529
+ diag.snapshotPersistence.restored,
1530
+ " \xB7 ",
1531
+ t("snapshot.created"),
1532
+ ": ",
1533
+ diag.snapshotPersistence.created,
1534
+ " \xB7 ",
1535
+ t("snapshot.failures"),
1536
+ ": ",
1537
+ diag.snapshotPersistence.failures
1538
+ ] }),
1539
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1540
+ t("snapshot.last"),
1541
+ ": ",
1542
+ t(`snapshot.${diag.snapshotPersistence.lastStatus}`)
1543
+ ] }),
1544
+ diag.snapshotPersistence.lastError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { role: "status", children: [
1545
+ t("snapshot.degraded"),
1546
+ " (",
1547
+ t(`snapshot.${diag.snapshotPersistence.lastError}`),
1548
+ ")"
1549
+ ] }) : null,
1550
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: t("snapshot.retention") })
1551
+ ] }) : null,
1500
1552
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1501
1553
  t("diag.cache"),
1502
1554
  ": ",
@@ -2686,15 +2738,15 @@ body[data-ds-dark-theme] [data-dsh-memoir-view] {
2686
2738
  }
2687
2739
  `;
2688
2740
 
2741
+ // src/client/native-navigation.ts
2742
+ function mainWorkspaceCwd(snapshot) {
2743
+ const main = Object.values(snapshot.byId).filter((session) => (session.retainedBy.mainView ?? 0) > 0);
2744
+ return main.length === 1 ? main[0].cwd ?? "" : "";
2745
+ }
2746
+
2689
2747
  // src/client/index.tsx
2690
2748
  var import_jsx_runtime2 = require("react/jsx-runtime");
2691
- var inject = ["sessions", "slots"];
2692
- function revealSourceTurn(turnId) {
2693
- if (turnId === void 0) return;
2694
- const reveal = () => document.querySelector(`[data-turn-tail="${turnId}"]`)?.scrollIntoView({ block: "center", behavior: "smooth" });
2695
- setTimeout(reveal, 0);
2696
- setTimeout(reveal, 250);
2697
- }
2749
+ var inject = ["uiWorkspace", "slots"];
2698
2750
  function ConversationMemoirView({
2699
2751
  api,
2700
2752
  ctx,
@@ -2702,7 +2754,8 @@ function ConversationMemoirView({
2702
2754
  sessionId,
2703
2755
  useSessions,
2704
2756
  viewRequest,
2705
- completeViewRequest
2757
+ completeViewRequest,
2758
+ openView
2706
2759
  }) {
2707
2760
  const cwd = useSessions((snapshot) => snapshot.byId[sessionId]?.cwd ?? "");
2708
2761
  (0, import_react2.useEffect)(() => {
@@ -2721,9 +2774,9 @@ function ConversationMemoirView({
2721
2774
  api,
2722
2775
  cwd,
2723
2776
  t,
2724
- openSource: (sourceSessionId, turnId) => {
2725
- ctx.sessions.open(sourceSessionId);
2726
- revealSourceTurn(turnId);
2777
+ openSource: (sourceSessionId) => {
2778
+ if (sourceSessionId === sessionId) openView("chat", "");
2779
+ ctx.uiWorkspace.openSession(sourceSessionId);
2727
2780
  }
2728
2781
  }
2729
2782
  )
@@ -2737,10 +2790,7 @@ function SettingsMemoirSection({
2737
2790
  close,
2738
2791
  useSessions
2739
2792
  }) {
2740
- const cwd = useSessions((snapshot) => {
2741
- const current = snapshot.current;
2742
- return current === void 0 ? "" : snapshot.byId[current]?.cwd ?? "";
2743
- });
2793
+ const cwd = useSessions(mainWorkspaceCwd);
2744
2794
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "memoir-settings-section", "data-dsh-plugin": "memoir", "data-dsh-part": "settings-section", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2745
2795
  MemoirPanel,
2746
2796
  {
@@ -2748,10 +2798,9 @@ function SettingsMemoirSection({
2748
2798
  cwd,
2749
2799
  t,
2750
2800
  onClose: close,
2751
- openSource: (sourceSessionId, turnId) => {
2801
+ openSource: (sourceSessionId) => {
2752
2802
  close();
2753
- ctx.sessions.open(sourceSessionId);
2754
- revealSourceTurn(turnId);
2803
+ ctx.uiWorkspace.openSession(sourceSessionId);
2755
2804
  }
2756
2805
  }
2757
2806
  ) });