memory-eternal 0.16.0 → 0.17.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +138 -656
- package/README.zh-CN.md +138 -0
- package/bin/dsh-memory.mjs +33 -3
- package/cursor-extension/package.json +1 -1
- package/dsh-tui-plugin/dsh-plugin.json +1 -1
- package/dsh-tui-plugin/package.json +1 -1
- package/index.js +26 -3
- package/lib/api.js +9 -0
- package/lib/capture-run.js +35 -8
- package/lib/client.js +17 -17
- package/lib/db.js +19 -0
- package/lib/ledger.js +107 -0
- package/lib/mcp.js +8 -2
- package/orca-plugin/orca-plugin.json +1 -1
- package/package.json +145 -145
- package/scripts/release.mjs +5 -4
- package/web/app.js +15 -15
- package/README.en.md +0 -623
package/README.md
CHANGED
|
@@ -1,656 +1,138 @@
|
|
|
1
|
-
# 🧠 memory-eternal —
|
|
2
|
-
|
|
3
|
-
[![
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<p align="center">
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
|
59
|
-
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
dsh-memory
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
|
125
|
-
|
|
126
|
-
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
- 终端不渲染图片 / 二维码 / Canvas 星图;星图使用连接簇和邻接表。
|
|
140
|
-
- 长文、颜色和边框依赖终端字体;Windows 建议 Windows Terminal / VS Code 集成终端。
|
|
141
|
-
- 默认只读;真实批驳必须显式加 `--write`;示例数据用 `--demo`。
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## ✨ 它到底解决什么问题
|
|
146
|
-
|
|
147
|
-
| 你遇到的 | 以前的做法 | 用了之后 |
|
|
148
|
-
| --- | --- | --- |
|
|
149
|
-
| 聊了 3 天的项目,新会话全忘 | 手动翻历史 / 重新解释一遍 | 每轮结束自动沉淀成卡,开工前 `memory_recall` 一句就回来 |
|
|
150
|
-
| 记忆塞进 prompt,token 爆掉 | 全量粘贴、越聊越贵 | 只召回相关的一小块(可配上限),省 token 也少噪音 |
|
|
151
|
-
| AI 写的东西不敢直接信 | 无法追溯、无法回滚 | 卡片带来源 / 时间 / 状态,先审后入,删除进回收站 |
|
|
152
|
-
|
|
153
|
-
- **看得见**:知识卡 / 图谱 / 健康概览 / 沉淀日志,四层可视化;
|
|
154
|
-
- **控得住**:审核模式(全部人工 / 仅高风险)、去重与矛盾扫描、批量合并、每日备份与一键恢复;
|
|
155
|
-
- **带得走**:知识库是本地 Markdown + SQLite,可 git 管理、可导出,不锁在插件里。
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## 🚀 五分钟上手
|
|
160
|
-
|
|
161
|
-
### 🟦 DeepSeek Harness(DSH)—— 重点
|
|
162
|
-
|
|
163
|
-
**装**(官方桌面版走插件市场,`dsh web` 用一条 CLI):
|
|
164
|
-
|
|
165
|
-
```bash
|
|
166
|
-
# ① 官方 DeepSeek Harness 桌面版(推荐):设置 → 插件 → 搜 memory-eternal 安装
|
|
167
|
-
# 等价 CLI(该壳的 profile 名就是 desktop):
|
|
168
|
-
dsh plugin --profile desktop add memory-eternal
|
|
169
|
-
|
|
170
|
-
# ② dsh web(浏览器端):
|
|
171
|
-
dsh plugin --profile web add memory-eternal
|
|
172
|
-
|
|
173
|
-
# 或在 profile(pnpm workspace)里直接更新。注意用 pnpm,npm install 会报 EUNSUPPORTEDPROTOCOL
|
|
174
|
-
cd ~/.dsh/profiles/web && pnpm add memory-eternal@latest
|
|
175
|
-
|
|
176
|
-
# 刚发新版时 @latest 可能被 pnpm 的最小发布年龄门槛/元数据缓存挡住(静默停在旧版):
|
|
177
|
-
# 直接指定版本号最快,pnpm 会自动把它加进 workspace 的 minimumReleaseAgeExclude
|
|
178
|
-
cd ~/.dsh/profiles/web && pnpm add memory-eternal@0.15.0
|
|
179
|
-
|
|
180
|
-
# 验版本(应等于上面装的号),然后重启对应宿主生效
|
|
181
|
-
node -e "console.log(require('memory-eternal/package.json').version)"
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
#### 🖥️ 桌面版支持策略(先看这条)
|
|
185
|
-
|
|
186
|
-
- **优先、且只保证支持官方 DeepSeek Harness 桌面版**(官方 Electron 发行版,profile 名 `desktop`)。v0.9.4 → v0.9.6 三轮修复全部围绕它:0.1.7 设置服务换血、`schemastery 3.18.4` 的 volatile 活引用、以及窗口标题栏避让(`data-windows-titlebar`,避免全屏浮层压住最小化/最大化/关闭)。
|
|
187
|
-
- **作者自研的 `dsh-desktop`(`dsh-desktop-shell` / `dsh-ui-*` / `dsh-plugin-marketplace` 那一套)已停止维护**,不再作为支持目标 —— 官方桌面版已覆盖同样能力,继续维护两条壳只会分裂兼容性。注意它的 profile 依赖里还钉着已从 npm 撤下的 `dsh-desktop-shell@0.3.0`,会让 `pnpm install` 直接失败;**建议迁到官方桌面版,并从 profile 里删掉这套依赖**。
|
|
188
|
-
- 兼容矩阵(均已实测):官方桌面版 `desktop` profile(schemastery **3.18.4**)+ `dsh web` 的 `web` profile(schemastery **3.18.1**),两条路径共用同一份代码。
|
|
189
|
-
|
|
190
|
-
**重启宿主**后,三样东西立即生效:
|
|
191
|
-
|
|
192
|
-
| 效果 | 在哪看 |
|
|
193
|
-
|---|---|
|
|
194
|
-
| 自动沉淀知识卡 | 每轮对话结束自动发生,无需操作 |
|
|
195
|
-
| `memory_recall` 工具 | Agent 需要历史时自动调用 |
|
|
196
|
-
| 图形化界面 | 侧边栏底部「记忆」按钮 / 设置 → 记忆 |
|
|
197
|
-
|
|
198
|
-
**入口**:侧边栏底部「记忆」按钮 → 记忆库(左栏含 知识卡 / 知识图谱 / 用量 / **审核中心** / 回收中心 / **记忆配置**);「DSH 设置 → 记忆」= 纯配置页。
|
|
199
|
-
|
|
200
|
-
**界面语言**:全套 UI 中英双语,跟随 **DSH 设置 → 语言** 实时切换(含知识卡 / 知识图谱 / 审核中心 / 新建卡模板);浏览器直接访问 web 时跟随浏览器语言。
|
|
201
|
-
|
|
202
|
-
**改配置**:记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)→ DSH 记忆配置 / 成本控制 / 自动审核配置 / 服务自管理,点「保存配置」即写入。`autoWebMode`/`watchdogAutoSpawn` 的改动需重启 DSH 生效;注意**已常驻的 watchdog 不会被配置改动自动停掉**,要停止或换新版请执行 `dsh-memory stop` / `dsh-memory restart`(见「服务自管理」)。
|
|
203
|
-
|
|
204
|
-
### 🟨 Claude Code
|
|
205
|
-
|
|
206
|
-
```bash
|
|
207
|
-
npm i -g memory-eternal # 装 CLI + MCP(写 ~/.claude.json mcpServers.memory)
|
|
208
|
-
dsh-memory connect claude # 写 ~/.claude/settings.json 的 SessionEnd hook → 会话结束自动沉淀
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
装完即用:会话里说「recall 一下数据库选型」→ 自动检索记忆;会话结束 → 自动沉淀进统一 `~/.dsh/memory-vault`(新卡 `pending` 待审核)。
|
|
212
|
-
|
|
213
|
-
### 🟧 Codex CLI / Cursor
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
npm i -g memory-eternal # 装完自动写 Codex config.toml / Cursor mcp.json 的 mcpServers.memory
|
|
217
|
-
dsh-memory connect codex # 写用户级 ~/.codex/hooks.json 的 Stop hook → 会话结束自动沉淀(含 Codex Desktop)
|
|
218
|
-
dsh-memory connect cursor # 写 ~/.cursor/hooks.json 的 stop/sessionEnd hook → 自动沉淀
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
重启工具 → MCP 已在列表,会话里直接:`用 memory_recall 查一下项目历史决策`;会话结束自动沉淀进统一 `~/.dsh/memory-vault`(新卡 `pending` 待审核)。
|
|
222
|
-
|
|
223
|
-
> **三种 agent 同一套库**:全部写入 `~/.dsh/memory-vault`,每卡 `submittedBy` 区分作者(DeepSeek Harness / claude-code / codex / cursor),主库只显已审核、审核中心管新卡。
|
|
224
|
-
|
|
225
|
-
> **原生插件(可选,平台 Marketplace)**:仓库含 `.claude-plugin` / `.codex-plugin` / `.cursor-plugin` 清单,可 `claude /plugin marketplace add EternalNight996/memory-eternal` + `/plugin install`、`codex plugin marketplace add EternalNight996/memory-eternal` + `codex plugin add`、Cursor Settings→Plugins。`connect` 走用户级 hooks.json(更稳,不依赖 Marketplace 审核;Codex Desktop 也走这条)。
|
|
226
|
-
|
|
227
|
-
> **只保留本插件(卸载其它记忆插件,如 agentmemory)**:`dsh-memory` 只写自己的键(`mcpServers.memory` / 含 `capture.mjs` 的 hooks),不依赖、也不冲突任何其它记忆插件。卸载其它插件需在其自身配置层删除对应条目(如 Codex 的 `[marketplaces.*]` 与 `hooks.state.*`、其 `hooks.json` 事件、`~/.agentmemory` 数据目录)。
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
### 🟩 浏览器(不依赖任何 Agent)
|
|
232
|
-
|
|
233
|
-
```bash
|
|
234
|
-
dsh-memory open # 起 web + 开浏览器(默认 http://127.0.0.1:7999)
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
统计 / 搜索 / 知识卡(增删改合并导入导出)/ 知识图谱,全在此。与 DSH 内嵌页同一份 UI,数据同步。
|
|
238
|
-
|
|
239
|
-
> **zcode(智谱)**:暂无原生 MCP,经社区 [zcode-open-bridge](https://github.com/tizerluo/zcode-open-bridge) 转 MCP 或用 CLI。
|
|
240
|
-
|
|
241
|
-
---
|
|
242
|
-
|
|
243
|
-
## 📖 命令速查
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
dsh-memory recall "数据库选型" # 检索
|
|
247
|
-
dsh-memory capture "重要结论..." # 手动沉淀(- 读 stdin)
|
|
248
|
-
dsh-memory sweep ~/.claude/projects # 挖掘已有会话记录
|
|
249
|
-
dsh-memory setup [--dry-run] # 重跑/预览自动挂载(幂等)
|
|
250
|
-
dsh-memory connect <claude|codex|cursor> # 写会话结束自动沉淀 hook(用户级 hooks.json,含 Codex Desktop)
|
|
251
|
-
dsh-memory mcp # MCP stdio(挂任意 MCP 客户端)
|
|
252
|
-
dsh-memory serve [--port 7999] # 前台跑 web
|
|
253
|
-
dsh-memory open # ensure web 存活 + 开浏览器
|
|
254
|
-
dsh-memory tui # 完整终端客户端(只读真实队列;--write 才批驳)
|
|
255
|
-
dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
|
|
256
|
-
dsh-memory status [--json] # 看常驻 watchdog:pid / 端口 / 版本 / 是否存活
|
|
257
|
-
# 以及**端口上真正在服务的版本**(锁里的版本号只是 watchdog 自述)
|
|
258
|
-
dsh-memory stop [--port 7799] # 停止常驻 watchdog(配置改动不会自动停;连它拉起的 web 一起停)
|
|
259
|
-
# 停完会再问一次端口占用者,仍被占着就明确告警
|
|
260
|
-
dsh-memory restart [--port 7799] # 停止并重起 watchdog(升级后换新版)
|
|
261
|
-
# 按端口占用者兜底收掉锁里没登记的旧 web,起完自检端口上服务的版本
|
|
262
|
-
dsh-memory audit list [--status pending|rejected|approved|deleted|all] [--limit N] [--json]
|
|
263
|
-
# 列出卡片;总数不受 --limit 影响,--limit 只限制显示条数(默认全部)
|
|
264
|
-
# all = 审核队列(pending + rejected),不含 approved / deleted
|
|
265
|
-
dsh-memory audit approve <卡片路径...> # 人工批量批准
|
|
266
|
-
dsh-memory audit reject <卡片路径...> --reason "..." # 人工批量驳回
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
单独装(不发 DSH)时 `dsh-memory` 命令来自 `npm i -g`。
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## ⚙️ 服务自管理(白话)
|
|
274
|
-
|
|
275
|
-
**三个概念**,别搞混:
|
|
276
|
-
|
|
277
|
-
- **web server 怎么保活**(`autoWebMode`)→ `init`=DSH 启动时拉一次(默认);`interval`=DSH 进程内定时探活自动拉起(0 额外内存);`manual`=全手动只从 `dsh-memory open` 起。
|
|
278
|
-
- **看门狗进程**(`watchdogAutoSpawn`,默认开)→ 一个**独立** node 进程,DSH 退出了它也能拉起 web(约 +47 MB 内存)。只在要 7×24 保活时开。
|
|
279
|
-
- **版本漂移自愈**(`autoRestartOnDrift`,默认开)→ 常驻 web **比 DSH 活得久**:升级只换磁盘文件,端口上那个进程仍跑着启动时加载的旧代码(「启动 dsh 后运行中还是旧版本」就是这么来的)。开着它,DSH 激活时会**问端口上真正服务的版本**,与磁盘不一致就用新代码重启常驻实例;关掉则只告警,需要时在「插件信息」点**重启常驻实例**。
|
|
280
|
-
- **自动挂载 MCP**(`autoMcpSetup`,默认关)→ 是否自动把 MCP 写进 Claude Code/Codex/Cursor 配置。关 = 不碰你本机配置文件,需要时手动 `dsh-memory setup`。
|
|
281
|
-
|
|
282
|
-
**改这些**:记忆库左栏「记忆配置」(或 DSH 设置 → 记忆)→ 表格里改,点「保存配置」;`autoWebMode`/`watchdogAutoSpawn` 需重启 DSH 生效。
|
|
283
|
-
|
|
284
|
-
> ⚠️ **同端口不会因配置改动而自动替换(#19)**:看门狗是**独立进程且故意不杀**(多会话共用一个),所以
|
|
285
|
-
> 关闭 `watchdogAutoSpawn` **不会**停掉已经在跑的那个,改 `webCheckIntervalMs`/`webMaxRestart` 也不会生效。
|
|
286
|
-
> 要停止 / 换新版请显式执行 `dsh-memory stop [--port N]` 与 `dsh-memory restart [--port N]`(`stop` 会**连它拉起的 web 一起停**,不留占端口的孤儿);
|
|
287
|
-
> `dsh-memory status` 会显示常驻实例的 pid、端口、启动时间与**版本是否落后于本机安装的包**,并单独报出**端口上真正服务的版本**
|
|
288
|
-
> (锁文件里的版本号只是 watchdog 自述,端口上跑旧代码时它不会变)。
|
|
289
|
-
>
|
|
290
|
-
> ⚠️ **升级后请以「端口实际服务版本」为准(#23)**:`restart` 现在不只信锁里登记的 web 子进程,
|
|
291
|
-
> 还会按**端口占用者**兜底清理(锁里没登记的旧 web 也会被识别并收掉,非本插件的进程只告警不动手),
|
|
292
|
-
> 并在起完之后自检「端口上服务的版本 = 本机版本」——对不上就以非 0 退出码报失败,不再打印「已启动」就完事。
|
|
293
|
-
>
|
|
294
|
-
> ✅ **「更新了却还是旧版本」现在有两条出路(#19/#23)**:① 默认开的 `autoRestartOnDrift` 在 DSH 启动时
|
|
295
|
-
> **自动**替换旧常驻实例;② 面板「插件信息」里发现版本漂移时给一个**🔄 重启常驻实例**按钮(服务端即
|
|
296
|
-
> `dsh-memory restart --port N` 的同一实现:收旧实例 → 抢锁 → 拉起新 web → 自检端口版本)。
|
|
297
|
-
> 注意「运行中」那一格指的是**服务本页的进程**:独立 Web 页里就是常驻 web,DSH 设置页里是宿主 ——
|
|
298
|
-
> 面板会同时列出**常驻实例**的版本,两者不同时一眼能看出该重启谁。
|
|
299
|
-
|
|
300
|
-
**MCP 是协议不是常驻服务**:agent 开会话才 spawn,用完即退,没有「开机自启」一说。
|
|
301
|
-
|
|
302
|
-
### 三种部署强度
|
|
303
|
-
|
|
304
|
-
| 场景 | 配置 | 内存 |
|
|
305
|
-
|---|---|---|
|
|
306
|
-
| 个人开发(推荐) | `autoWebMode=init` + **手动**把 `watchdogAutoSpawn` 关掉(默认是**开**,见下方配置表) | web 47 MB |
|
|
307
|
-
| 常驻 7×24 | `watchdogAutoSpawn=on` | web + watchdog 47+47 MB |
|
|
308
|
-
| 真正开机自启(无 DSH) | Windows 计划任务跑 `dsh-memory watchdog --port 7799 --interval 5000 --max-restart 10` | 同上 |
|
|
309
|
-
|
|
310
|
-
---
|
|
311
|
-
|
|
312
|
-
## ⚙️ 记忆配置(大白话)
|
|
313
|
-
|
|
314
|
-
> 入口:**记忆库左栏「记忆配置」** 或 **DSH 设置 → 记忆**;改完点右下角「保存配置」。**大多数项立即生效**,只有 `autoWebMode` / `watchdogAutoSpawn` 改动需要重启 DSH(见「服务自管理」)。
|
|
315
|
-
>
|
|
316
|
-
> 下图是配置页全貌(多库隔离 / 成本控制 / 自动审核 / 服务自管理)。**第一次用?直接看下面的「一键推荐」。**
|
|
317
|
-
|
|
318
|
-
<p align="center">
|
|
319
|
-
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/config.png" width="880" alt="记忆配置页:多库(按项目隔离)、成本控制、自动审核配置与服务自管理" />
|
|
320
|
-
</p>
|
|
321
|
-
|
|
322
|
-
### 🎯 先点这里 · 一键推荐(多数人到这一步就够了)
|
|
323
|
-
|
|
324
|
-
配置页顶部有 **🟢 A 轻量省心 / 💰 B 极致省钱 / ⭐ C 高质量** 三个按钮,点一下自动填好,再点保存即可:
|
|
325
|
-
|
|
326
|
-
| 方案 | 场景 | 保活 | 看门狗 | 蒸馏 | 蒸馏上限 | 召回阈值 | 内存 | LLM 成本 |
|
|
327
|
-
|---|---|---|---|---|---|---|---|---|
|
|
328
|
-
| 🟢 **A 轻量省心** | 个人开发(推荐)| init | 关 | 开 | 4000 | 2 | ~47 MB | 正常 |
|
|
329
|
-
| 💰 **B 极致省钱** | 预算敏感/多 Agent | init | 关 | **关** | 500 | 3 | ~47 MB | **近 0** |
|
|
330
|
-
| ⭐ **C 高质量** | 长项目/团队 | interval | **开** | 开 | 1200 | 1 | ~94 MB | 高 |
|
|
331
|
-
|
|
332
|
-
> 拿不准就选 **A**(就是默认值)。选完即可用,下面的表只有想细调时才看。
|
|
333
|
-
|
|
334
|
-
### ① 最常用(三件事)
|
|
335
|
-
|
|
336
|
-
| 配置项 | 默认 | 大白话说明 |
|
|
337
|
-
|---|---|---|
|
|
338
|
-
| 自动沉淀 | 开 | 每轮聊完自动把有用的内容存成知识卡 |
|
|
339
|
-
| 自动召回 | 开 | AI 需要历史时自动帮你查记忆 |
|
|
340
|
-
| 记忆库目录 | `~/.dsh/memory-vault` | 记忆存哪,纯 Markdown 可 git 管理 |
|
|
341
|
-
|
|
342
|
-
#### 多库(按项目隔离记忆)
|
|
343
|
-
|
|
344
|
-
配置里还有两个字段(写在 `memory-eternal-config.json`,或设置面板同步过来的共享配置):
|
|
345
|
-
|
|
346
|
-
```json
|
|
347
|
-
{
|
|
348
|
-
"vaultProfiles": [{ "name": "work", "path": "D:/vaults/work" }],
|
|
349
|
-
"activeVault": "work"
|
|
350
|
-
}
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
解析顺序:`MEMORY_VAULT_DIR` 环境变量 → `activeVault` 命中 `vaultProfiles[].name` 的 `path` → `vaultDir` → 默认 `~/.dsh/memory-vault`。
|
|
354
|
-
|
|
355
|
-
**v0.9.8 起,CLI / MCP / hooks / 独立 web / sweep 与 DSH 宿主共用同一套顺序**:以前只有宿主认 `activeVault`,切库后终端与 hooks 仍往默认库写,记忆会被劈成两份。
|
|
356
|
-
|
|
357
|
-
> 说明:`vaultProfiles` / `activeVault` 可在配置页「多库(按项目隔离)」里可视化维护(添加库 / 激活库 / 默认库);按 workspace 自动选库仍待做。
|
|
358
|
-
|
|
359
|
-
### ② 省钱(重要,可选)
|
|
360
|
-
|
|
361
|
-
| 配置项 | 默认 | 大白话说明 |
|
|
362
|
-
|---|---|---|
|
|
363
|
-
| **蒸馏知识卡** | 开 | 把对话**压缩**成精炼知识卡(要调 AI,花钱)。**关掉 = 存原文**,一分钱不花 |
|
|
364
|
-
| **语义去重喂 AI** | 开 | 判断新内容是不是重复(要调 AI)。**关掉 = 用简单去重**,省一次 AI 调用 |
|
|
365
|
-
| 蒸馏输出上限 | 4000(硬顶) | 压缩一次最多写多少字,越大越准越费钱;调小更省 |
|
|
366
|
-
| 召回相关性阈值 | 2 | 检索要「多像」才返回,越大越准但漏得越多(越省) |
|
|
367
|
-
| 捕获最小长度 | 200 | 对话太短不存,避免闲聊浪费 |
|
|
368
|
-
| 日配额 | 60 | 一天最多存几张,防 AI 烧钱 |
|
|
369
|
-
|
|
370
|
-
### ③ 服务怎么跑(一般不用动)
|
|
371
|
-
|
|
372
|
-
| 配置项 | 默认 | 大白话说明 |
|
|
373
|
-
|---|---|---|
|
|
374
|
-
| 保活模式 `autoWebMode` | init | `init`=DSH 启动时开一次 web;`interval`=定时检查挂了自动重启;`manual`=全靠手动 |
|
|
375
|
-
| 看门狗 `watchdogAutoSpawn` | 开 | 后台一个**独立进程**保证 web 不死(+47 MB 内存)。个人用可关——但关闭后**既有实例不会自动停**,需 `dsh-memory stop`(#19) |
|
|
376
|
-
| 漂移自愈 `autoRestartOnDrift` | 开 | DSH 激活时发现**端口上服务的版本 ≠ 磁盘版本**(升级后常驻 web 还是旧代码)就用新代码重启常驻实例。关掉只告警,仍可在「插件信息」点**🔄 重启常驻实例**(#19/#23) |
|
|
377
|
-
| 自动挂载 MCP `autoMcpSetup` | 关 | **让 Claude Code / Codex / Cursor 也能用你的记忆库**。开=自动配好它们;关=不碰你电脑配置,手动跑 `dsh-memory setup` |
|
|
378
|
-
| 凭证提示 `secretHint` | 空 | 填一句自己的约定(例如「需要 API key/token 时先查记忆里的密钥目录」),会**注入每个会话的 systemPrompt**。记忆库里只存**目录**(名字/位置/取用方式),密钥的**值**不入库 —— 见下方「密钥怎么放」 |
|
|
379
|
-
|
|
380
|
-
> 🔑 **密钥怎么放(`secretHint` 的用法)**:把**值**留在本机凭据里(环境变量 / 密钥文件,权限收紧),
|
|
381
|
-
> 记忆库里只留一张**目录卡**(有哪些键、放在哪、干什么用、轮换状态),再用 `secretHint` 让 agent
|
|
382
|
-
> 每次会话都想到去查这张卡。为什么值不能入库:卡片 `summary` 就是正文开头,**召回默认就带 130 字**
|
|
383
|
-
> (`recallSummaryLen`),值一入库就随每次相关召回进入模型上下文;`/export`、备份、多机同步也会带上。
|
|
384
|
-
> `dsh-memory setup` 会在 Claude Code 里注册一个 `PreToolUse` 守卫:直接读
|
|
385
|
-
> `.credentials.yaml` / `.env` / `*-token` 会被拒绝并指路到目录卡。
|
|
386
|
-
|
|
387
|
-
---
|
|
388
|
-
|
|
389
|
-
## 🔔 自动沉淀日志 & 异常提示
|
|
390
|
-
|
|
391
|
-
自动沉淀是后台静默管线:一旦坏了,以前只能表现为「卡不再增加」,看不出原因。v0.9.0 起它会自己说话。
|
|
392
|
-
|
|
393
|
-
- **自动沉淀日志**:设置 → 记忆 → 用量/今日,最近 100 条运行轨迹:`🚀 启动` / `👂 会话接入(用的事件接口 + 事件数)` / `✅ 新建卡` / `➕ 追加更新` / `⏭ 跳过(原因)` / `❌ 失败(原因)`。
|
|
394
|
-
- **异常主动提示**:出现失败即亮红——记忆库页面顶部红条,同时把「自动沉淀异常 + 原因」注入 Agent 上下文,Agent 会在回复第一句提醒你,不需要你自己去翻日志。
|
|
395
|
-
- **停滞兜底**:**本次运行至今一次收尾事件都没收到**、且最近 20 分钟内持续有轮次开始(例如 DSH 改了事件名/作用域),5 分钟巡检记一条 `warn` —— 不再冒充红色「自动沉淀异常」,正在进行的**长回合也不会被误判**(一轮对话中途 `inbox/claimed` 会多次触发,收尾要等回合真的结束)。
|
|
396
|
-
- **接口自适应**:自动识别 DSH 会话事件接口(`ownEvents()` → `snapshotEvents()` → `events`),DSH 升级换 API 不会再静默断掉。
|
|
397
|
-
|
|
398
|
-
---
|
|
399
|
-
|
|
400
|
-
## 🛡️ 审核中心 & 回收中心
|
|
401
|
-
|
|
402
|
-
新卡默认进**审核中心**(`pending`),由你确认后才入主库;驳回的进「已驳回」,可恢复或删除进回收站。命中免审条件(审核模式=全部免审 / 免审智能体 / 免审类型)的新卡直接入库;回收站软删卡 30 天内可恢复,超期自动永久删除。
|
|
403
|
-
|
|
404
|
-
> **v0.3.0 起:审核守卫下沉到数据库层。** 所有写入路径(自动沉淀 / MCP / UI 新建 / 导入)强制经过 `enforceAudit()`,调用方无法绕过。`setCardStatus()` 是唯一审核操作入口,每次变更写入 `audit_log` 审计日志(who/what/when/why),不可篡改。
|
|
405
|
-
|
|
406
|
-
> **v0.10.0 起:升级为「两套存储」——未审核内容物理隔离。**
|
|
407
|
-
> `cards`(**主库 / 正常区**)物理上**只存 approved**;`quarantine`(**隔离区 / 异常区**)存 `pending` / `rejected` / `deleted`。
|
|
408
|
-
> 意义:召回、检索、知识图谱、去重池、导出这些读路径**查主库即安全**——不再依赖「每处 SQL 都记得写 `WHERE status='approved'`」。
|
|
409
|
-
> (v0.9 审计实测漏过三处:去重池会把新知识追加进待审卡、`readCard` 可按 path 直读未审核正文、`/card` 接口无状态校验。)
|
|
410
|
-
> 审核流转 = **跨表搬家**:批准 → 搬进主库,驳回/软删 → 搬进隔离区;搬家在一个事务里完成,更新记录与审计日志跟着走。
|
|
411
|
-
> 升级说明:首次启动会自动把存量非 approved 卡搬进隔离区(**不删除任何内容**,都在同一个 `.db` 文件里)。
|
|
412
|
-
> 若回滚到旧版本,审核队列会显示为空(旧代码看不到隔离区表),升级回来即恢复。
|
|
413
|
-
|
|
414
|
-
<p align="center">
|
|
415
|
-
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/audit.png" width="880" alt="审核中心:待审队列 + 右侧判词(现象 / 根因 / 解决方案)" />
|
|
416
|
-
</p>
|
|
417
|
-
|
|
418
|
-
- **待审核 / 已驳回** 双页签,按类型 / 日期 / 智能体筛选,全选后一键「批准 / 驳回 / 删除进回收站」。
|
|
419
|
-
- 审核规则在「记忆配置 → 自动审核配置」:`审核模式`(全部要审 / 全部免审)+ `免审智能体` + `免审类型` + `回收保留天数`。
|
|
420
|
-
- **Agent 不能代你审批(v0.9.1 起)**:只要审核生效,插件就往 Agent 的上下文里注入红线——新卡只能停在「待审核」,并明确禁止调用 `approve`/`reject` 接口或任何等价方式绕过;判断权始终在你手里。
|
|
421
|
-
|
|
422
|
-
### 🥇 为什么审核系统更靠谱(对比其它记忆产品)
|
|
423
|
-
|
|
424
|
-
多数记忆产品(mem0 / Zep / agentmemory…)对话一结束就**自动全量入库**,好坏不论——噪音、错误事实、敏感内容一起进库,之后又被召回,**污染上下文、放大幻觉**。
|
|
425
|
-
|
|
426
|
-
memory-eternal 走**人机协同审核**,只让可信内容进主库:
|
|
427
|
-
|
|
428
|
-
| 维度 | 其它记忆产品 | memory-eternal 审核系统 |
|
|
429
|
-
|---|---|---|
|
|
430
|
-
| 入库 | 自动全量,无把关 | 新卡先进**审核中心(pending)**,人工批准才入主库 |
|
|
431
|
-
| 质量 | 未过滤噪音/错误 | 只保留你确认过的卡 → 召回更准、噪声更低 |
|
|
432
|
-
| 可信 | 无来源/审核追溯 | 每卡带 `submittedBy` 作者 + `pending/approved/rejected` 审核状态 |
|
|
433
|
-
| 免打扰 | — | 免审智能体 / 免审类型命中 → 可信卡直接入库,零等待 |
|
|
434
|
-
| 容错 | 删了就没 | 回收站软删,30 天内可恢复 |
|
|
435
|
-
|
|
436
|
-
---
|
|
437
|
-
|
|
438
|
-
## 🧬 为什么全自研
|
|
439
|
-
|
|
440
|
-
市面上记忆方案多,但大多依赖第三方框架 / 动不动起 MCP 服务 / 记忆锁私有库。本插件把骨架自己搭,**零第三方运行时依赖**,逻辑逐行可读:
|
|
441
|
-
|
|
442
|
-
| 模块 | 自研实现 | 替代什么 |
|
|
443
|
-
|---|---|---|
|
|
444
|
-
| 去重 | 词法 Jaccard bigram(0.62)+ 语义去重 | 防重复卡 |
|
|
445
|
-
| 检索 | CJK 感知:中文整词 + 字符 bigram | 无需全文搜索引擎 |
|
|
446
|
-
| 图谱 | 力导向 + `[[wikilink]]`/共享标签连边 | 知识关联一眼看清 |
|
|
447
|
-
| 存储 | **SQLite**(`node:sqlite` 内置,零依赖)| 不锁库、分表存储、SQL 查询 |
|
|
448
|
-
| 审核 | **数据库层审核守卫** + 审计日志 | 杜绝越权,所有写入强制走审核 |
|
|
449
|
-
|
|
450
|
-
> 与热门项目同向(总结→存储→按需召回),但定位不同:**本地、自研、零依赖、可读可控**。如果你已在用 mem0/Zep 等,也能把它当「本地持久记忆底座」叠加使用。
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
|
|
454
|
-
## 🛠 开发 / 测试
|
|
455
|
-
|
|
456
|
-
```bash
|
|
457
|
-
npm i
|
|
458
|
-
npm test # 单元测试:vault 去重/检索/图谱 + capture 管线 + API 形状
|
|
459
|
-
npm run build # 构建 lib/client.js(DSH 内嵌)+ web/app.js(独立 web bundle)
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
> 📄 **README 是中英两份独立文件**:`README.md`(简体中文,**默认**,npm / 插件市场展示这一份)
|
|
463
|
-
> 与 `README.en.md`(English)。两份标题层级一一对应,**改一处要同步另一处**(新增一节两边都加)。
|
|
464
|
-
|
|
465
|
-
---
|
|
466
|
-
|
|
467
|
-
## 🐞 反馈问题
|
|
468
|
-
|
|
469
|
-
插件左栏有 **🐞 反馈异常** 入口:填完问题描述后可以
|
|
470
|
-
|
|
471
|
-
- **在 GitHub 提交(已预填)** —— 标题 / 正文 / 诊断信息 / `bug` 标签都填好,再点一次 **Submit** 即提交;
|
|
472
|
-
- **复制给 AI 的提示词** —— 粘进 AI 对话框,让它用 `gh` 查重后建 issue 并回报链接。
|
|
473
|
-
|
|
474
|
-
> **为什么不能「点一下自动提交」**:GitHub 不允许匿名创建 issue;插件里内置 token 会被反编译提取;OAuth / 自建中转需要服务器。所以实现为「预填 + 一次点击」。
|
|
475
|
-
> 诊断信息会**自动脱敏**(home 目录 → `~`,`sk-` / `ghp_` / `github_pat_` / `api_key:` 等 → `***`),但公开提交前仍建议自己扫一眼。
|
|
476
|
-
|
|
477
|
-
如果你拿不到插件界面,也可以直接把下面这段贴给 AI(把「我的问题描述」换成你的问题即可):
|
|
478
|
-
|
|
479
|
-
```text
|
|
480
|
-
我在使用 memory-eternal(DeepSeek Harness 的记忆插件)时遇到了问题,请你帮我把这个问题提交到 GitHub。
|
|
481
|
-
|
|
482
|
-
仓库:https://github.com/EternalNight996/memory-eternal
|
|
483
|
-
|
|
484
|
-
请按这个流程做:
|
|
485
|
-
|
|
486
|
-
1. 先跑 `gh issue list --repo EternalNight996/memory-eternal --state open --limit 50`,看是否已有相同问题:
|
|
487
|
-
- 已有 → 用 `gh issue comment <编号> --body-file <文件>` 补充现象,不要重复开新 issue;
|
|
488
|
-
- 没有 → 继续第 2 步。
|
|
489
|
-
2. 把正文写成文件,然后 `gh issue create --repo EternalNight996/memory-eternal --title "[Bug] <一句话概括>" --body-file <文件>`;正文用这个结构:
|
|
490
|
-
现象 / 复现步骤 / 期望行为 / 实际行为 / 环境(插件版本 / DSH 版本 / 操作系统 / Node)/ 诊断信息。
|
|
491
|
-
3. 如果 `gh` 没登录或不可用,不要尝试提交,改为输出一条「标题与正文都已预填」的 GitHub 新建 issue 链接给我,我自己点提交。
|
|
492
|
-
4. 提交成功后,把 issue 链接发给我。
|
|
493
|
-
|
|
494
|
-
注意:我的问题描述请原样照录,不要替我美化或省略细节;诊断信息原样放进「诊断信息」一节。
|
|
495
|
-
|
|
496
|
-
我的问题描述:
|
|
497
|
-
<在这里写你的问题>
|
|
498
|
-
|
|
499
|
-
诊断信息:
|
|
500
|
-
<把「反馈异常」弹窗里复制的诊断信息粘到这里>
|
|
501
|
-
```
|
|
502
|
-
|
|
503
|
-
### 环境自检(贴问题时附上更快定位)
|
|
504
|
-
|
|
505
|
-
```bash
|
|
506
|
-
dsh plugin list # 插件版本
|
|
507
|
-
node -v # Node 版本
|
|
508
|
-
curl http://127.0.0.1:7999/memory-eternal/api/web-info # 独立 web 是否活着
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
---
|
|
512
|
-
|
|
513
|
-
## 📋 更新日志
|
|
514
|
-
|
|
515
|
-
> **发布规则(2026-09-30 起)**:正式版一律**三端同步发布** —— `npm publish` + **GitHub Release** + **Gitee Release**,并保证 `vX.Y.Z` tag 在三端一致。桌面版 profile 依赖 `github:EternalNight996/memory-eternal`、`dsh web` 走 npm 版本号,少任何一端都会出现「npm 上是新版、桌面版还是旧的」。统一入口:`npm run release:dry`(预演)→ `npm run release`(正式)。详见 [PUBLISH.md](./PUBLISH.md)。
|
|
516
|
-
>
|
|
517
|
-
> **版本撤回说明**:**v0.9.16 – v0.9.22 已全部撤回**(问题期间版本:git tag 已删除、npm 已标记 deprecated,请使用 **v0.9.23**)。**v0.9.14 已 deprecated**(客户端渲染改动降低手感,已回退;其服务器侧算法优化并入 **v0.9.15**,渲染与 v0.9.13 完全一致)。**v0.9.0 – v0.9.5 均已在 npm 标记 deprecated;v0.9.1 / v0.9.2 / v0.9.3 / v0.9.4 的 release tag 已从 git 移除**(v0.9.5 只是被取代、tag 保留)—— v0.9.1/v0.9.2 带记忆页白屏缺陷,v0.9.0 沉淀告警误报刷屏,v0.9.3 不支持 DSH v0.1.7-rc.2(升级后插件整体挂不上:设置服务换血 + 客户端 `settingsScope` 消失),v0.9.4 在官方桌面版(schemastery 3.18.4 的 volatile 活引用)下抛 `cfg.vaultDir.trim is not a function`、插件整体挂不上,**v0.9.5 的全屏浮层会盖住桌面版的窗口控制面板(右上角「×」压在窗口「关闭」上,点一下会退出整个桌面壳)**。请一律使用 **v0.9.6+**(`npm i memory-eternal@latest`)。
|
|
518
|
-
> **DSH 兼容性**:`>=0.1.5-alpha.2 <0.3.0-0`(**v0.10.1 起放宽**:旧的上界 `<0.2.0` 在 semver 上会把 DSH `0.2.0-rc.x` 排除在外 —— DSH 的插件守卫用 `includePrerelease:true` 判定,实际仍能装上,但声明与事实不一致;现在明确覆盖整个 0.1/0.2 线,且 `<0.3.0-0` 仍然挡住 0.3.0 及其预发布。**已在官方桌面版 DSH 0.2.0-rc.2 上实测运行**:插件挂载、记忆页、自动沉淀、审核中心、导入导出均正常)。**v0.9.4 起适配 DSH v0.1.7-rc.2**(该版本把设置服务换成纯表单 API,并移除了 `@deepseek-ai/dsh-client-runtime` 与 `settingsScope`);**v0.9.5 起兼容 schemastery ≥3.18.4 的 volatile 活引用**(官方桌面版 profile 即此形态,web profile 仍是 3.18.1,两者都支持);**v0.9.6 起全屏浮层自动让开桌面壳的窗口标题栏**(不再压住最小化/最大化/关闭)。0.1.5 系列仍走旧的 `settings.register` 路径,两条路径都保留。
|
|
519
|
-
|
|
520
|
-
| 版本 | 日期 | 关键改动 |
|
|
521
|
-
|---|---|---|
|
|
522
|
-
| **v0.16.0** | 2026-10-24 | **配置页与 MCP 兼容性大更新**:①「记忆配置」专业化重做——统一分区卡片(图标 / 标题 / 说明)、Switch 开关、粘性保存栏 +「未保存」状态、数值单位提示;「一键推荐」改为方案卡并加点击反馈(选中态 / Toast / 脏状态联动)。②「Agent MCP 挂载状态」改为自动识别:Claude Code / Codex / Cursor 可一键挂载,Gemini CLI / Windsurf / Cline / VS Code 只读识别;自动列出每个客户端已配置的 MCP 并汇总,支持「重新检测」。③ Web 首屏加载提示 + 知识星图分帧预热,消除打开记忆页 / 星图的白屏与卡顿。④ 修复 issue #51:LLM 蒸馏请求带 sessionId,opencode-go 等网关不再 MissingSessionID 400、不再每轮白烧调用。⑤「Web 版」入口只允许本机地址(端口取 watchdog 实际端口)。⑥ README 展示重做(新截图、平台表 ✅/⏳、配置章 UX 重排)。新增 9 个测试文件,npm test 全绿。 |
|
|
523
|
-
| **v0.14.3** | 2026-10-23 | **终端实时数据全页打通**:`--live` 现在同时加载真实记忆库(全量分页)、知识星图节点/连线、运行状态总数/备份/版本、回收站;不再在记忆库/星图/状态页显示 999 节点、0.12.0、示例日志等假数据;待审仍为真实队列,读卡按卡状态请求正文。 |
|
|
524
|
-
|
|
525
|
-
<details>
|
|
526
|
-
<summary>更早的更新日志(v0.10.52 及以前 · 共 79 版,点开查看)</summary>
|
|
527
|
-
|
|
528
|
-
| 版本 | 日期 | 关键改动 |
|
|
529
|
-
|---|---|---|
|
|
530
|
-
| **v0.15.0** | 2026-10-23 | **TUI 默认实时**:`dsh-memory tui` 不再需要 `--live`,默认加载真实记忆库、待审、星图、状态、回收站;`--demo` 才使用示例数据;`--write` 才允许批驳;同时修复空队列误回退示例、记忆库/星图/状态假数据问题。 |
|
|
531
|
-
| **v0.14.2** | 2026-10-23 | **实时空队列修复**:`dsh-memory tui` 在真实待审为 0 时不再回退示例数据,显示 LIVE 空队列;同时保留 `npx -p memory-eternal dsh-memory tui` 的免路径入口与 0.14.1 的旧全局 CLI 升级提示。 |
|
|
532
|
-
| **v0.14.1** | 2026-10-23 | **终端入口免绝对路径**:新增 `dsh-memory tui`,任意用户安装插件后可直接运行完整终端客户端,无需 `cd` 到仓库或填写本机绝对路径;Web 终端页同步改为只读/写入两条通用命令。 |
|
|
533
|
-
| **v0.14.0** | 2026-10-23 | **Web 终端入口 + 新功能截图**:记忆库左栏新增「终端版」,内置运行命令、dsh-tui `/audit` 引导与终端预览,解决 Web 无法展示 TUI 的问题;README 新增终端审核、长文读卡器和 Web 终端页真实截图;dsh-tui 原生 `/audit` 正式接入。 |
|
|
534
|
-
| **v0.13.0** | 2026-10-23 | **完整 TUI + dsh-tui 原生审核**:终端客户端补齐记忆库、待审、星图、运行状态、回收站、设置、长文读卡器与搜索;待审支持全选/批量批驳;`--live` 直连本地 API(默认只读,`--write` 才写);新增 dsh-tui 原生 `/audit` 场景,不再只是打开浏览器。 |
|
|
535
|
-
| **v0.12.0** | 2026-10-22 | **跨宿主入口**:Orca 右侧栏「记忆」启动器、dsh-tui `/memory` 命令、Codex / Claude `memory-open` skill、Cursor / VS Code Webview 扩展;README 新增平台支持表;坚持一套 Web UI 唯一真源,能内嵌的用 Webview,不能内嵌的只做启动器;不新增 agent 权限。 |
|
|
536
|
-
| **v0.11.0** | 2026-10-22 | **Memory Atlas 正式版**:V5 UI 全量落地 —— 记忆库 4 列知识卡 + 今日回看 + 固定/连接微缩图;待审顶部类型条 + 队列 + 右侧判词栏(无底部栏、无弹窗);知识星图 V5 页头/连接簇图例 + DPR/网格/边渲染优化;运行状态四张状态卡;读卡器右侧目录/相关记忆;金乌品牌图标;二维码点击放大;首次引导先 Star/Fork;GitHub/Gitee/npm 三端同步。 |
|
|
537
|
-
| **v0.11.0-rc.1** | 2026-10-21 | **V5 预览 RC**:Memory Atlas 深色余烬视觉、228px 侧栏与顶栏;记忆库 V5 卡片网格 + 页头;审核顶部类型条 + 队列 + 判词预览 + 全选批量批准/驳回;星图默认缩小;首次引导(重点 👍)、读卡器目录/相关记忆与左栏交流群/赞助/旧程序/异常反馈;公开 Demo 同步 V5。 |
|
|
538
|
-
| **v0.10.54** | 2026-10-20 | **本地大模型蒸馏更稳(#48)+ README 焕新与最新截图**。① **#48**:本地 30B 模型冷启动要 40–60 秒,而蒸馏超时**硬编码 45 秒** → 每次冷启动必报 ABORTED;现在超时可配 **captureTimeoutMs**(默认仍 45s,本地推理建议 120000+),且**超时后自动用更长超时重试同一候选一次**(不直接换模型)。② **#48 的第二种形态**:模型不给 max-tokens finish 时,截断只表现为「JSON 未闭合」→ 新增独立错误码 **TRUNCATED_OUTPUT**,与 MAX_TOKENS 一起走「加倍输出预算重试同一候选」的阶梯。③ 配置页新增「蒸馏超时(毫秒)」输入项。④ **README 焕新**:全部换成真实运行截图(知识卡 / 知识图谱 897 节点 4257 连线 / 健康概览 / 审核中心 / 配置页)+ 新增「它到底解决什么问题」对照表 + 更清晰的卖点与 Star 引导(中英同步)。npm test 共 **440 项** |
|
|
539
|
-
| **v0.10.53** | 2026-10-20 | **健康面板(可靠性线):把看不见的状态集中一处**。在「用量/今日」顶部新增「🩺 健康概览」:① **知识库**(journal 模式 / 可写 / 最后一次体检时间,异常时给 doctor 指引);② **备份**(数量 + 最近一次多久前,>7 天标黄、0 备份标红);③ **自动沉淀**(最近一次成功/失败与原因);④ **版本**(磁盘 / 常驻 / 宿主;未检查时给「开始检查」按钮,因为 align 要联网比 npm 不能自动跑)。数据来自既有接口:/overview 新增 **backups 摘要**(count/newestAt/最近 5 个文件名),加上 /capture-log 与 /align。**顺带修一个白屏 bug**:健康卡最初误用了 ConfigPanel 作用域的 tf() → 点开「用量/今日」整页白屏;同时给 panel-check 补了两道闸 —— **页面存活(textLen<20 判空白页)** 与 **Runtime.exceptionThrown 捕获**,因为「白屏没有横向溢出」原本会让检查通过。新增 tests/health-panel.test.mjs(4 项)。npm test 共 **437 项** |
|
|
540
|
-
| **v0.10.52** | 2026-10-19 | **诊断包脱敏补漏(隐私修复,建议尽快升级)**。0.10.51 的「一键诊断包」有个漏点:doctor/align --json 与 meta.json 里的家目录是 **JSON 转义形态**(C:\\Users\\x),而我只替换了原样/正斜杠两种写法 → 包里仍能 grep 到家目录明文。现在三种写法(原样、正斜杠、JSON 转义)都替换成 <home>,并在测试里加了一条「JSON 转义形态不能漏」的断言;本机重新生成诊断包实测:**无家目录明文、无 sk- 密钥**。**如果你已经用 0.10.51 生成过诊断包并分享出去,建议重新生成一次再分享。** npm test 共 **433 项** |
|
|
541
|
-
| **v0.10.51** | 2026-10-19 | **一键诊断包 dsh-memory support**(可靠性线;#47 里用户贴的现场被 GitHub 截断,我拿不到完整信息,这功能就是为这类沟通做的)。一条命令生成 **zip**:meta(版本矩阵/平台/node/vault)/ doctor / align / status / 看门狗锁 / 宿主心跳 / 共享配置 / 沉淀日志尾巴(默认 200 行)+ README;写入前**统一脱敏**(家目录→<home>,sk-/ghp_/github_pat/Bearer/配置里 key 类字段→***)。实现为**零依赖**:自写 store/deflate ZIP 写入器(lib/zip.js,Windows 资源管理器可直接打开,实测 Expand-Archive 解压正常)。受限环境可加 --no-spawn 跳过子进程;--out 指定输出、--tail N 控制日志条数。新增 tests/support-bundle.test.mjs(4 项:脱敏、zip 结构、包内不含明文密钥、CLI 接线)。npm test 共 **433 项** |
|
|
542
|
-
| **v0.10.50** | 2026-10-19 | **「模型不守 JSON」不再每轮白烧调用(#47 衍生改进)**。① 把「模型回了散文、输出里根本没有 JSON 对象」从笼统的 UNPARSEABLE_OUTPUT 里拆出来,给独立错误码 **MODEL_NOT_JSON**,文案直接说清「该模型可能不遵守结构化输出」。② 新增 `lib/model-strikes.js`:记下「哪个 provider/model 最近不守格式」(24h 窗口、最多 50 条),宿主选路时把它们**排到候选末尾**(不删除;全部命中过则按原顺序兜底,绝不没候选),并留一条「蒸馏候选已调整」日志。③ 效果:像 stepfun/step-api 这种会把蒸馏当聊天回的模型,第二轮起就不再优先尝试,省掉每次的无效调用。新增 `tests/model-strikes.test.mjs`(4 项:散文识别 / 排序稳定性 / 记录窗口与裁剪 / 接线守卫)。npm test 共 **429 项** |
|
|
543
|
-
| **v0.10.49** | 2026-10-19 | **桌面版里「无法选择蒸馏模型」修复(#47):独立页把 /llm-routes 与 /llm-test 代理给宿主 + 文案补一个状态**。根因:桌面版的「记忆」弹窗是 iframe 里的**独立页**,它没有 DSH 的 llm 服务 → /llm-routes 恒失败 → provider/model 只能手填。现在独立页读宿主心跳(<config>.host.json 里的端口,仅 127.0.0.1、心跳 120s 内有效),把这两个路由**转发给宿主**执行并标记 proxied;宿主不在线时给明确原因(面板直接显示,不再静默回退成手填)。② 版本卡状态矩阵补 restartRepair:**版本已对但没人保活**时提示「重新托管并保活」,不再出现「换成 v0.10.48(当前 v0.10.48)」这种自相矛盾的文案(截图实测)。npm test 共 **425 项** |
|
|
544
|
-
| **v0.10.48** | 2026-10-19 | **界面验收手段 + 状态穷举(方案一落地)**。① **新增 `npm run panel-check`**(scripts/panel-check.mjs,零依赖):用本机 Chrome headless + CDP 打开面板,逐屏断言**没有横向溢出**并截图(列表 / 阅读器 / 配置 / 反馈弹窗)。它第一次跑就抓到两个真 bug:卡片标题里的无断点长串(`(begin/chunk/probe/complete/abort)`)溢出 45px;**反馈弹窗 textarea 溢出 22px**(width:100% + padding 在 content-box 下)—— 正是用户报的「排版不对 / 滚动条异常」。② 修复:`.mc-cardrow h4` 断词、`.me-overlay input/textarea/select` 统一 border-box。③ **版本卡判定抽成纯函数** `src/client/version-state.js`:`versionCardState(...)` 返回 `{showButton, emphasis, warn, hint}`,组件只做 key→文案;新增表驱动测试**穷举 324 种组合**,并守住不变式「只要拿到版本信息,按钮永远显示」—— 0.10.45「第三种状态导致按钮消失」从结构上不可能再发生。npm test 共 **420 项** |
|
|
545
|
-
| **v0.10.47** | 2026-10-19 | **面板版本探针自动复查 + 「常驻未运行」文案修正(用户反馈:重启后面板一直显示「常驻实例 未运行」)**。① 现场其实**是活的**(status 显示 7999 在听、服务 v0.10.46),但版本探针只在面板挂载时跑一次 —— 恰好撞上重启窗口就会被缓存成「未运行」并一直显示。现在**每 60 秒静默复查**(不点亮「检查更新」按钮)+ **切回页面立刻复查**(visibilitychange),重启后 6 秒本来就会复查一次。② 文案:常驻实例真没跑时,原来那句会拼出「当前 v」(空值);现在改走 vcRestartStart:**「点击会用当前磁盘版本 v{disk} 把常驻实例拉起来(不用重启 DSH)」**。npm test 共 **408 项** |
|
|
546
|
-
| **v0.10.46** | 2026-10-19 | **「重启常驻实例」按钮改为**永远显示** + 修正「谁旧」的文案(用户第二次反馈按钮消失)**。上一版把按钮放进「常驻实例需要重启」的分支,而实际状态是**常驻实例已是新版、服务本页的进程(DSH 宿主)还是旧版** → 落进宿主提示分支、按钮又没了;更糟的是警告文案写着「点重启常驻实例即可」,而重启常驻根本不会改变宿主提供给你的本页版本。现在:① 按钮**只依赖 vc 存在**,任何状态都在;② 三种状态各给准确说明 —— 常驻旧(点了就换新版,不用重启 DSH)/ 宿主旧(重启常驻**不影响本页**,请重启 DSH)/ 全部对齐(卡住时可强制重启);③ 警告文案拆出 vcHostProcessStale,明确「服务本页的是宿主进程」。npm test 共 **406 项** |
|
|
547
|
-
| **v0.10.45** | 2026-10-19 | **修复「重启常驻实例」按钮消失 + 对话框排版/滚动条异常(用户截图反馈)**。① **按钮常驻**:它原来被包在「有异常才渲染」的警告块里 —— 版本一对齐整块消失,用户再也找不到(#28 也抱怨过)。现在操作行**独立渲染**:异常时是修复入口,一切正常时也保留按钮并说明「通常无需重启,卡住时可强制重启」。② **滚动条异常**:阅读器最多 8 个头部按钮,而 0.10.42 给按钮组设的是 nowrap → 在 max-width:760px 的对话框里**横向溢出**;现在按钮自身仍不缩不换行、但**按钮组整体允许换行**(掉到第二行也不变形)。③ **排版**:.me-dialog / .me-dialog-body 改 border-box 并隐藏正文横向滚动(阅读器的 width:100% + padding 不再撑破容器);诊断/反馈的 <pre> 长路径加 word-break(远程 agent 的路径没有断点,以前必横向滚);大标题 modal 的 h2 加省略号。新增 tests/ui-overflow.test.mjs(4 项守卫)。npm test 共 **406 项** |
|
|
548
|
-
| **v0.10.44** | 2026-10-19 | **界面体验(第二批):标签点击即筛选 + 搜索命中片段**。① **标签筛选**:列表/阅读器里的标签芯片现在可点,点了立即按该标签筛(与搜索 / 类型 / 状态**可叠加**);工具栏出现可清除的「🏷 标签 ✕」。服务端 /cards?tag= 走 SQL 精确匹配 —— 按「JSON 数组里带引号的完整标签」用 instr 匹配,**不会把 ab 命中成 a**;countCards 与 sort=title 两条分支同步生效。② **搜索命中片段**:命中正文时,列表多显示一段**关键词附近的片段并高亮**(折叠空白、两端省略),一眼看出「为什么命中」。③ 新增 src/client/snippet.js(纯函数,Node 可单测)+ tests/tag-filter.test.mjs(3 项)。npm test 共 **402 项** |
|
|
549
|
-
| **v0.10.43** | 2026-10-19 | **界面体验(第一批 · 读卡)**。① **复制**:阅读器新增「📋 复制正文」「🔗 复制路径」,复用带兜底的剪贴板工具,1.5s 内显示「已复制」(不再点了没反应);② **阅读进度**:头部下沿 2px 细条随正文滚动推进,短卡直接 100%;③ **j/k 翻页**:阅读器内 j 下一张、k 上一张(基于当前排序后的列表),**方向键仍留给正文滚动**,输入框内不触发;④ **阅读宽度**:窄/中/宽三档(720/900/1180px)一键切换并记住偏好;⑤ 延续 0.10.42 的头部不挤压修复。新增 tests/card-reader-ux.test.mjs(4 项守卫)。npm test 共 **399 项** |
|
|
550
|
-
| **v0.10.42** | 2026-10-19 | **卡片头部不再被长标题挤压(用户截图反馈)**。旧布局是 space-between,而 h3 没有 min-width:0(长标题压不动),按钮组也没有 flex:0 0 auto / nowrap → 按钮被挤成竖排(「收/起」「关/闭」)。现在:h3 可收缩并**单行省略**(悬停显示完整标题)、按钮组 flex:0 0 auto + nowrap(永不被压扁)、头部允许换行(极窄时整组按钮掉到第二行,仍逐个可点);阅读器与导出图谱两个头部统一走 .me-head-actions。新增 tests/card-head-actions.test.mjs(3 项守卫)。npm test 共 **395 项** |
|
|
551
|
-
| **v0.10.41** | 2026-10-19 | **知识图谱初始视图缩小两个度**。图谱是 canvas + sim.zoom,滚轮每格 ×1.1;按「两个度」= ×(1/1.1)² ≈ **0.826** 处理 —— 初始 fit()(首屏、布局稳定后、以及「重置视图」)现在都在「刚好铺满」的基础上再外扩两格,四周留出拖拽/框选余量;滚轮与初始视图**共用同一个 ZOOM_STEP 常量**,以后调步长不会两边漂;原有限幅(0.35–1.8)保留,小图不会被缩到看不见。新增 tests/graph-initial-zoom.test.mjs(3 项源码守卫,含系数数值)。npm test 共 **392 项** |
|
|
552
|
-
| **v0.10.40** | 2026-10-19 | **#45 后续(阻塞级):枚举超时不再被当成「端口空闲」+ macOS 红绿灯遮挡(#46)**。① 报告者实测:Get-NetTCPConnection 在该机要 **4.1–61.4 秒**(TCP 表大),而 takeover 只给 2.5s → 超时 → 空输出 → null → 「端口已空出」→ 新看门狗绑不上 7999、漂到 8001,**旧 web 永远活着、0.10.38 的修复永远轮不到执行**。修法:Windows 改**先走 netstat -ano**(实测 1.8s 且直接给 pid,本机复测 **403ms** 拿到 pid+命令行),PowerShell 只作兜底;portFree 升级成**三态**(free / occupied / **unknown**),枚举超时=未知 → **中止接管**并写明确日志(不再漂移);status 把「响应超时」与「没有服务」分开报;探测路径不再漏 socket。② **#46**:macOS 桌面版的窗口按钮是 Electron **原生 traffic light**(titleBarStyle:hiddenInset),任何 z-index/portal 都盖不住 —— 插件改为**跟随宿主安全区** --dsh-frame-overlay-top(macOS 68px,原生全屏宿主降到 20px),浮层顶边恒 68px、遮罩仍铺满全屏,不再被「关闭」按钮压住。npm test 共 **389 项** |
|
|
553
|
-
| **v0.10.39** | 2026-10-17 | **接线自愈:codex 的 TOML 按段解析 + 单引号/转义兼容 + 启动期只读体检(#44 第②④条)**。① 修了一个真 bug:setup / align 解析 ~/.codex/config.toml 时**没有限定 [mcp_servers.memory] 段**,会抓到别的 server 的第一个 command =(本机实测读成了 MCP_DOCKER 的 docker.exe);而且只认双引号,而 codex 自己写出来是单引号 → **既读不到也改不掉,却照样打印「已更新」**。现在读写共用纯函数(按段 → 单/双引号 → 反转义),**改完校验真的变了**,幂等(已是目标形态不算改动),改不了就如实报 section unparseable。② 新增启动期**只读**体检 staleWiringCheck():MCP/hooks 里若写着**已不存在**的 node 路径、或条目与当前版本不一致,就在自动沉淀日志里留一条 warn(dsh-memory setup 可修)—— 没人会天天跑 align,静默失效要变成可见。③ 本机验证:align 三端接线全部 ✓(修前 codex 一直报「node 路径与当前不一致」)。npm test 共 **379 项** |
|
|
554
|
-
| **v0.10.38** | 2026-10-17 | **接管成功即释放 claim(修 0.10.35 的副作用)+ #45 真机 A/B 验证数据**。0.10.35 的 claim 在「助手变成常驻看门狗」这条路径上永远不会被释放(那个进程不退出),于是接管完成后 60 秒内的下一次 restart 会被自己挡成 in-flight(本机实测踩到)。现在 bootWatchdog() 之后立刻 releaseReplaceClaim() —— 互斥窗口严格等于「接管进行中」。**#45 真机验证(同一台机、同一组 20 次 /version-check)**:0.10.36 → 9 成功 / **11 拒连**,TIME_WAIT 24→**1976**(+1952 ≈ 每请求 100 条连接 = 递归深度),首帧 2389ms;0.10.37 → **20/20 成功**,TIME_WAIT 1969→**1971**(+2),18–34ms。npm test 共 **376 项** |
|
|
555
|
-
| **v0.10.37** | 2026-10-17 | **修 #45(我 0.10.36 引入的严重回归:/version-check 自我探测递归被重试放大)+ #44(node 路径硬编码 / 僵尸看门狗误判)+ #43(跟随会话的官方字段)**。① **#45**:selfPort() 就是本进程自己的端口,而 /version-check 会用 probeServedVersion **再向它发一条 /version-check** → 处理器 → 自己 → 处理器…… 深度只受超时约束、每层各开一条连接 → listen backlog 打满 → **间歇 ECONNREFUSED(17–37ms)**、TIME_WAIT 静置 40 秒涨 4402、served 反成空串;0.10.36 的「重试 3 次」把它**乘以 3**。现在自探一律**直接作答**(probedSelf:true,不发 HTTP),/restart-self 同一道闸门;/version-check 也支持 ?port= 问别的端口。② **#44**:nodeBinary() 缓存命中前**验存在性**,execPath 失效(brew 升级删掉旧目录)时回退 PATH —— 长命 watchdog 从此自愈;inspectResident 新增 portServing,decideResidentAction 遇「登记说 watchdog 活着、端口却没人应答」判 restart(watchdog-port-dead)而**不再 delegate**(那正是「沉淀静默停摆、日志只写 delegated」的现场);status 打印实际用的 node 与存在性。③ **#43**:sessionRouteFrom() 改为 async,按官方字段优先取 session.requestHeader().config → agent.options → 旧嗅探兜底。npm test 共 **375 项** |
|
|
556
|
-
| **v0.10.36** | 2026-10-16 | **把两种新状态接到所有入口(align / doctor)+ 修一处测试卫生**。① align 新增一条结论:**常驻实例由宿主托管**(锁不可写 → 看门狗路径被绕过,改由宿主停旧起新并保活)—— **warn 级**,明确写「实例是有人管的,不是孤儿;锁恢复可写后自动交还看门狗」(align.ok 不受影响,warn 不算失败);② dsh-memory doctor 增印一行 **常驻实例托管:**(看门狗 / 宿主托管 / 不可替换(环境限制)),配合已有的 journal 模式、写探针、锁可写性、备份清单,一次看清这台机器处于哪种状态;③ **测试卫生**:/restart-self 的 alreadyCurrent 用例依赖**开发者本机真实的锁文件**(我本机锁里只剩一条 endedAt 记录,于是它不再判 alreadyCurrent、用例翻红)—— 把「读锁」做成可注入(deps.readWatchdogLock),用例自己注入一条活登记,从此与开发机状态无关。npm test 共 **371 项** |
|
|
557
|
-
| **v0.10.35** | 2026-10-16 | **接管助手单飞互斥(#30 第六轮实测:宿主 tick 与 CLI 各拉一个 --replace 互相踩)**。报告者观察到失败的 restart 期间同端口上**同时存在两个互不相识的接管助手**(父 = DSH 宿主 / 父 = CLI),谁都不写锁、还互相收对方的 web —— 现场更难复盘。修法:新增 **claim 文件**(memory-eternal-replace.claim,与看门狗锁同目录)—— ① **助手开工前独占抢 claim**,抢不到就**直接退出**(把接管让给已经在跑的那个),并在退出时释放;② **restartResident 发现接管进行中**(持有者活着且 <60s)就**不再拉第二个助手**,返回 stage「in-flight」并说明是谁在跑、多久前启动;③ **失败开放**:受限环境写不了 claim 时**照常接管**(不能让权限问题把接管彻底堵死),但把原因写进返回值 / 日志。测试覆盖:先到先得 / 活持有者让位 / 过期与死 pid 可接管 / 只清自己的 claim / restartResident 的 in-flight 分支。npm test 共 **370 项** |
|
|
558
|
-
| **v0.10.34** | 2026-10-16 | **治本:锁不可写时由「宿主」自己托管常驻实例(采纳 #30 报告者第四点建议)**。他的诊断:那台机器上「登记」恰好落在**唯一写不了锁的那个进程**上 → 自动替换被永久拒绝。修法:① 宿主的 `autoWebMode=interval` 保活循环**不再只探存活**,还探**端口上服务的版本** —— 落后且锁可写 → 仍交给看门狗 / 漂移路径(宿主不抢);落后且**锁不可写** → **宿主自己接管**(`ensureWebServer({forceTakeover:true})`:只收掉**自家** web、在**同端口**起新实例、由宿主保活);② 新增可测试的 `takeoverOwnWeb()`(安全闸门:只对 `looksLikeOurWeb` 为真的占用者下手,别人的进程一个都不动);③ 「宿主托管」记成状态(`recordHostManaged`)→ `/version-check` 报 `resident.managedBy='host'`,面板显示「**宿主托管**」而不是「未托管 / 孤儿」;看门狗重新登记后自动清除。`npm test` 共 **366 项** |
|
|
559
|
-
| **v0.10.33** | 2026-10-15 | **蒸馏供应商:动态下拉 + 跟随当前会话(尽力)+ 仅试首选 + 测试按钮(issue #42 第一刀)**。① **动态下拉**:新增只读接口 `/llm-routes`(宿主侧的 listProviders/listModels),配置页的「蒸馏 provider / model」从**手填编号**变成**下拉选择**(取不到时自动回退成文本输入);② **跟随当前会话**(他的最高优先级):新增配置 `captureFollowSession`,开启后宿主蒸馏**先尝试**用这一轮会话的 provider/model(`sessionRouteFrom()` 按 `agent / session / session.route / session.options` 逐个嗅探),**读不到就回退显式配置并记一条日志**(DSH 未保证会话对象暴露 route —— 这条日志就是以后补适配的依据,不猜);后台 / 无会话任务(CLI、watchdog、hooks)天然只能回退配置;③ **仅试首选 + 测试按钮**:`captureFirstOnly` 让选路只取第一个候选、失败即停(避免「先打官方 → 白烧一次调用 → 再轮换」);`POST /llm-test` 用真实调用路径打一次小请求,回显 **provider / model / 耗时 / 候选数 / 错误码**,也是 ①② 的验证手段。④ 顺带修一处**探测抖动**:`probeServedVersion()` 现在退避重试 3 次(报告者实测「同一时刻 version-check 200、2 秒后 status 说没有服务」),单发探测不再被重启窗口/瞬时拒绝骗到。`npm test` 共 **363 项** |
|
|
560
|
-
| **v0.10.32** | 2026-10-15 | **「常驻实例不可替换」变成长期状态,而不是每次开机重试(#30 第五轮复测:A: 盘的稳态)**。报告者实测 5 天 44 次「版本漂移」,机制是**每一步都按设计工作、合起来永远换不掉**:升级 → 端口上仍是上一代 → 宿主探测漂移 → 拉起 `--replace` 助手 → 助手前置探到 `lock.writable=false` → **正确地拒绝**(0.10.29 的设计,零破坏)→ 旧实例继续存活 → 下次开机再报一次。修法:① 新增 `lib/resident-state.js`:把「不可替换(环境限制)」**记成状态**(原因 / 锁路径 / 端口 / 版本 / 次数,7 天有效);② 宿主在重启前**先探锁可写性**,不可写就**不再重试注定失败的替换**,只记一条 —— **首次 `warn`**、之后每次开机降级为 `boot` 级「已知状态」(报告者原话:不想每次开机都看到一条 warn 永久持续);③ 措辞与「漂移」显式分开:**「常驻实例不可替换(环境限制)」** + 出路(修权限后 restart,或 `dsh-memory watchdog --port N` 前台观察);④ 面板 / `/version-check` / `align` 都能看到这个状态(`resident.replaceBlocked`;`align` 里是一条 `warn` 级结论,环境修好后自动清除)。`npm test` 共 **359 项** |
|
|
561
|
-
| **v0.10.31** | 2026-10-15 | **`restart` 在「端口枚举很慢」的机器上 100% 失败的根因修复(#30 第三/四轮复测,报告者定位到行)**。机制:`--replace` 助手的顺序是「清场 → 端口空了才写锁 → 启动看门狗」,而清场里 `Get-NetTCPConnection` 实测要 **4–13.6 秒**且不受 8 秒预算约束 → 助手**永远走不到写锁那一步** → 锁里什么都没有 → `waitForTakeover`(要求「锁里出现新的活看门狗」)**必然超时** → CLI 与面板按钮在这类机器上 100% 报「(无响应)」,**与版本号无关**。修法:① **清场失败也必须留下登记**(`acquireWatchdogLock({force:true})`),现场不再出现「端口有 web、锁里空白」;② **`acquireWatchdogLock` 支持 `force`**(`--replace` 的语义就是替换同端口那个,不许让位 —— 否则助手在清场没跑完时先退出);③ 0.10.30 已给枚举执行器加超时(`defaultExec` 真掐断子进程、`findPortListener({timeoutMs})` 逐层传递、清场给 ≤2.5s 有界值);④ CLI 的 `restart` 结果**真的写进自动沉淀日志**(提示里一直这么说,但 CLI 路径过去不写)。他还实测到「两个接管助手互相赛跑」(宿主 tick + CLI 各拉一个)—— 登记 + force 之后这种赛跑的危害大幅降低(后到者接管登记,而不是双双留空)。`npm test` 共 **355 项** |
|
|
562
|
-
| **v0.10.30** | 2026-10-15 | **两处「让整页不可用」的修复:畸形 tags 黑屏(#41)+ 接管链路的两个真病灶(#30 第三/四轮复测)**。① **#41**:`/write` 传字符串 `tags`(如 `"[\"a\",\"b\"]"`)被 `writeCard` 直接 `JSON.stringify` → **双重编码**落库 → 读出来仍是字符串 → 前端 `card.tags.slice(...).map` 抛错 → **React 根组件挂载失败、整页黑屏**(重启 DSH / 插件 / 常驻实例都无效,因为坏数据一直在库里)。现在 `normalizeTags()` 是**唯一入口**:数组 / JSON 字符串 / 逗号串 / **双重编码的旧数据**都收敛成数组,写路径与**所有读路径**都走它(既有坏数据不必用户手工修);渲染层再加兜底(坏 tags 最多少显示几个标签,不许把整棵树带走)。② **#30**:报告者在我 0.10.29 里又抓出两处 —— `restoreWatchdogLock` 的「活登记」判定**少了 `!w.endedAt`**(PID 复用/正在退出时,写回会被放弃);**Windows 端口枚举没有超时**(实测 `Get-NetTCPConnection` 无条件要 4–7.5 秒、一次 13.6 秒,而接管清场的 8 秒预算根本管不住它 → 决策基于过期数据)。现在 `defaultExec` 支持超时并真的掐断子进程、`findPortListener({timeoutMs})` 逐层传下去、接管清场给有界值(≤2.5s)。新增 `tests/tags-normalize.test.mjs`(4 项)+ `watchdog-restart` 2 项回归。`npm test` 共 **354 项** |
|
|
563
|
-
| **v0.10.29** | 2026-10-14 | **失败的接管不再破坏现场(issue #30 复测的关键发现)**。报告者在 0.10.26 上实测:`restart` 失败后**锁文件被删除**、端口上还是同一个 web —— 现场从此看起来像「从来没有过常驻实例」,只剩一个孤儿。四处修复:① **释放锁不再删文件**(`releaseWatchdogLock` 留一条带 `endedAt` 的「已结束」记录,`status` 会显示「已退出(陈旧记录)」;这种槽**永不阻挡**新实例,pid 复用也不怕);② **失败的 restart 把替换前的登记写回去**(`restoreWatchdogLock`,只在本端口没有活着的登记时才写);③ **枚举不到占用者时不再误判「端口已空」**——`clearResidentForTakeover` 用 **TCP 复核**(受限环境里进程查询被拒会返回空,旧实现据此认为端口空出 → 新 web 绑不上 → 自检「(无响应)」+ 锁被清空),还在监听就中止接管且**一个登记都不动**;④ **锁写不进去时前置拒绝接管**(写不进锁 → 新实例无法登记 → 只会留孤儿;直接报错并给前台观察的出路)。另外 `restart` 失败信息补上「**端口上现在是谁**」+「锁有没有写回」,`status` 把「端口探测」与「锁登记」**分段标注**(报告者指出旧输出两者并列会误导)。`npm test` 共 **348 项** |
|
|
564
|
-
| **v0.10.28** | 2026-10-14 | **新增「版本对齐检查」:把「插件迭代后旧接口 / 旧接线还在」一次查清**。插件升级只换**磁盘文件**,两类「旧」不会自己消失:① **运行时** —— DSH 宿主进程内存里注册的事件订阅(`agent/turn-stopping`、`agent/inbox/claimed`…)来自它**启动时加载的那份代码**,不重启 DSH 就会一直用旧接口(那条「轮次收尾事件疑似失效」多半就是它:本机实测宿主 **v0.10.8** / 磁盘 v0.10.27,而且该告警文案里的「累计 135/75」本身就证明跑的是 ≥0.10.20 之前的旧判据);② **持久接线** —— 插件写进 `~/.claude.json`、`~/.claude/settings.json`、`~/.codex/config.toml`、`~/.cursor/mcp.json` 的 MCP 与 hooks 条目含**旧安装路径 / 旧 node 路径 / 旧参数**,升级后不会自动改写。新增 `lib/align.js` + `GET /align` + `dsh-memory align [--json] [--fix]` + 面板「用量/今日 → 🔧 版本对齐检查」:逐条给结论与**可执行动作**(`完全退出并重开 DSH` / `dsh-memory restart --port N` / `dsh-memory setup` 重写接线 / 更新插件),`--fix` 只重写插件自己的键(不动数据与配置)。新增 `tests/align.test.mjs`(6 项,含「同一 node 但条目是旧写法」的措辞准确性守卫)。`npm test` 共 **345 项** |
|
|
565
|
-
| **v0.10.27** | 2026-10-13 | **「损坏时从备份重建」从引导升级成可执行(issue #31 的现实出路)**。WAL 头一旦损坏,代码修不了**数据** —— 上一版只能告诉你「有哪些备份、按什么顺序替换」,现在多了 `dsh-memory doctor --restore latest\|备份名`:**不带 `--yes` 只演练**(一个文件都不动,先告诉你备份里有几张卡、会不会覆盖可写库);执行时四道闸门:① **校验备份**(只读打开 + `PRAGMA quick_check` + 数卡片);② 当前库**此刻**完好时要求显式 `--force`(用**当场探测**判,不信缓存的体检结论);③ 检测到常驻看门狗还活着就**中止**(避免边写边换,除非 `--force`);④ 恢复后**复核**(重新打开 + 写探针 + 卡片数)。原现场**一律改名 `*.corrupt-<时间戳>` 留证,绝不删除**(库 + `-wal` + `-shm` 一起)。新增 `tests/db-restore.test.mjs`(4 项:坏库重建 / 完好库需 force 且演练不动任何文件 / 拒绝危险来源 / 体检结论刷新)。`npm test` 共 **339 项** |
|
|
566
|
-
| **v0.10.26** | 2026-10-13 | **撞锁改用「应用层异步重试」,而不是 `busy_timeout`(issue #31/#33 实测对症)**。本机对照实验:外部进程持写事务时我方立刻拿到 `database is locked`,而且**对方被强杀之后仍然锁着** —— 老实现没有忙等,一撞锁就永久失败(在 A: 盘那类受限 / 被外部占用的现场,表现出来就是「整个记忆库坏了」)。评审已否决 `PRAGMA busy_timeout`(`node:sqlite` 的 `DatabaseSync` 是**同步 API**,忙等会阻塞宿主事件循环、把整个 DSH 卡住),所以改成新增 `withDbRetry()`:撞锁时**让出事件循环睡一小会儿再试**(线性退避,最多 3 次),只用**写路由**;「响应已发出」时显式禁止重试(`e.noRetry`,避免重复写),非锁类错误(数据 / 语法)只试一次并原样抛。测试覆盖:重试到成功 / 非忙错误只试一次 / `noRetry` 生效 / 试满仍失败 / 源码守卫(不许偷偷加 `busy_timeout`)。`npm test` 共 **335 项** |
|
|
567
|
-
| **v0.10.25** | 2026-10-13 | **「判不出来」不再冒充「未托管」+ 库不可写时的统一 UX(issue #33 第③层收口)**。① **托管状态变三态**:`/version-check` 增报 `resident.managedKnown`、`lock:{path,writable,error}` 与 `pid` —— 受限环境(沙箱禁写 / ACL / 非系统盘)写不进看门狗锁文件时,面板不再显示「未托管」(那会让用户以为实例坏了、反复重启),改为「**托管状态未知**:锁文件写不进去 → 无法判断;保活通常仍然有效」,并同时显示真实原因与路径;`writeWatchdogLock` 的失败也不再全静默(每个进程留一条可查的痕)。② **库不可写时统一 UX**:写路由(delete / merge / card / refine-apply / optimize-execute / …)在入口**短路径失败**(503 + `DB_UNAVAILABLE` + 恢复指引),没枚举到的写路径由最外层兜底把 SQLite 原文翻译成同一套答复;面板**跨标签顶部**明示「记忆库当前不可写 —— 卡片读写会失败,但**配置仍可保存**」。③ `dsh-memory doctor` 增报看门狗锁可写性(受限环境一眼可见)。`npm test` 共 **334 项** |
|
|
568
|
-
| **v0.10.24** | 2026-10-13 | **「同目标、不同做法」也能归到一起了(issue #36 第三块,闭环)**。前两版解决了「冗余合并」与「矛盾判别」,但最后一块 —— 同一目的、不同工作流/代码 —— 按正文相似度**根本抓不到**(「用 7z 压缩」「用 tar.gz 分发」「npm 包瘦身」字面差很远、目标却一样)。做法:**先让模型给每张卡抽一句「这张卡解决什么问题」,再按目标聚类** —— 目标抽取结果缓存在 `refine-goals.json`(同一张卡只抽一次,第二次扫描 0 次调用),聚类用并查集;命中后按「同一目标 N 张卡」成组给出,动作二选一:**都保留**(记两两决策,下次不再提示)或**合并这一组**(走现有 /merge,新卡进审核队列)。*全程只读,唯一写入是目标缓存*。入口:面板「整理 → 🎯 同目标不同做法」+ API `/refine-goals`、`/refine-goals-keep`;CLI `dsh-memory refine --goals --keep --path A --path B`(聚类本身需要模型,在面板跑)。`npm test` 共 **332 项** |
|
|
569
|
-
| **v0.10.23** | 2026-10-13 | **存量「注入垃圾卡」可一键清理(issue #36 第二刀)**。上一版修掉了**新生成**的注入垃圾卡(技能目录 / `<system-reminder>` 被当 user 消息 → 剥标签后仍被当知识沉淀),但**存量还在**:在真实库里一跑就找出 **32 张**「和对话毫无关系」的卡(`- \`code-review\`: Review the changes…` 这种目录行、`用户system-reminder-you-are-teammate-c…` 这种碎片)。新增 `findInjectedCards()`(只**提议**、不动数据;判据:含注入标签 / 技能目录条目占比 ≥30% / 剥离注入后只剩 <200 字 / 标题是碎片)与 `purgeInjected()`(**走回收站、可还原**,逐个报告失败)。入口:面板「整理 → 🧹 注入垃圾卡」(扫描存量 / 单张回收 / 全部回收)+ CLI `dsh-memory refine --junk [--purge --path P] [--json]`;API `/refine-junk`、`/refine-junk-purge`。`npm test` 共 **331 项** |
|
|
570
|
-
| **v0.10.22** | 2026-10-13 | **「矛盾扫描」:相似可以合并,矛盾必须人判断(issue #36 第一刀)**。现状是 `/optimize` 只按相似度(≥0.55)**盲合并** —— 而两张语义相近、结论互斥的卡相似度同样很高(实测 0.93),盲合并会把冲突悄悄「和解」掉,且 `mergeCards` 会**永久删除**源卡。新增 `lib/refine.js` + 客户端「整理 → 🧭 矛盾扫描」:① **宽召回**(≥0.30,上限放到 1.0,特意把近重复那一段纳入 —— 那正是盲合并最危险的区间);② 模型只判「重复 / 补充 / 矛盾 / 无关」并给一句冲突点 + 建议保留谁;③ **人拍板**:保留 A / 保留 B / 都保留 / 合并;**扫描绝不写库**,`keepA|keepB` 只把让位那张送**回收站**(可还原);④ 决策记进 `refine-decisions.json`,同一对不再重复打扰;⑤ **安全联锁**:被判为矛盾的对**绝不参与一键优化**的盲合并。另修一处冗余来源:技能目录(`- \`skill-name\`: …`)这类注入文本不再被抓成垃圾卡(实测库里已有这种卡)。API:`/refine`、`/refine-scan`、`/refine-apply`;CLI:`dsh-memory refine`(列候选不调模型,`--apply` 落裁决)。`npm test` 共 **329 项** |
|
|
571
|
-
| **v0.10.21** | 2026-10-13 | **「库不可用」从「静默运行」变成「看得清」(issue #33,按评审裁剪范围)**。① **打开即体检**:老实现执行完 `PRAGMA journal_mode=WAL` 就当成功(这条 PRAGMA 失败时**不抛错**,只把真实模式当一行返回),文件只读 / 目录不可写时插件会一路静默跑下去;现在打开后**读回真实 journal 模式 + 真写一次探针**(建表→删表,写主库而非 temp 库),结论记进 `dbStatus()`,不可写时立即把原因与排查方向打到日志。② **`/overview` 降级返回**:库/目录不可用时不再吐 500(500 会被探针、面板、脚本读成「这个端口坏了 / 不是我们的服务」,历史上正是重启风暴与端口漂移的入口),改成 200 + `degraded:true` + `dbError` + `db:{journalMode,writable,error}`,并保住 `vaultDir` / `version` 身份字段 —— 「这是我们、但库有问题」从此可以被分开表达。③ **新增 `dsh-memory doctor`**:库健康自检(真实 journal 模式 / 写探针 / 备份清单 / 恢复引导),带 `--json` 可脚本化;不可写或疑似损坏时给出**只引导、不动数据**的步骤(先停写者 → 复制现场留证 → 查备份 → 替换 → 复核)。**刻意不做**(评审结论,已写成测试守卫防止顺手加回):`busy_timeout`(`DatabaseSync` 是同步 API,撞锁会阻塞宿主事件循环)与 WAL→DELETE 自动降级(会**永久**改变库模式,且 rollback journal 并发更差、可能反过来加剧锁竞争)。`npm test` 共 **322 项** |
|
|
572
|
-
| **v0.10.20** | 2026-10-12 | **「库不可用 → 看门狗自杀自拉 → 端口漂到 8006」三层现场一起修(issue #31 / #33,AkiMei0721 实测)**。① **`/overview` 非 200 ≠ 端口死了**:`/overview` 会碰数据库,库只读 / WAL 损坏时它返回 500,而看门狗据此判「离线」→ 走 `spawnWeb()`(开头先 SIGTERM 掉自己的上一个 child)→ **自杀自拉、端口从 7999 一路漂到 8006(6 个实例)**;新增 `decideTickAction()` 纯决策:只要 **TCP 还能连**就视为存活(`api-degraded-but-listening`,并重置重启计数),新实例的就绪判定同样接受「已监听但 `/overview` 非 200」。② **版本探测改走 `/version-check`**(不碰库;拿不到才回落 `/overview`)—— 病根是「端口上明明活着、版本却拿不到」,于是 verify 报「(无响应)」、`status` 甚至误判成「v0.10.4 之前的旧版」。③ **受限沙箱里不再崩**:默认 `stdio:'pipe'` 会在 `spawn()` 时**同步抛 EPERM**(老实现只在 `'error'` 事件里兜底,根本没机会执行)→ `dsh-memory watchdog --reap` 直接崩、进程枚举永远空名单;现在 `try/catch` 包住 spawn,并**用临时文件接 stdout**、完全不碰命名管道。④ 顺带给 stall 判据加「最短运行时长(30 分钟)」:刚落进程必然 0 收尾,不该据此报「收尾事件失效」(实测重启 1.5 分钟后就误报过)。新增 `tests/issue-fixes-8.test.mjs`(4 项)+ `tests/stall.test.mjs` 的 too-early 用例。`npm test` 共 **318 项** |
|
|
573
|
-
| **v0.10.19** | 2026-10-12 | **`UNSUPPORTED_REASONING_EFFORT` 不再让整条蒸馏熔断(issue #34)**。现场(zafir-zhong 等):很多**网关**在能力自述里说支持 reasoning effort,真把 `reasoningEffort: 'off'` 发出去才回 `UNSUPPORTED_REASONING_EFFORT`(实测:workbuddy/hy3、FallbacksChain/Auto、new-api-resp/gpt-6-luna),于是整个候选被判失败、换下一个 provider —— 用户的自动沉淀直接停摆(#37 / #38 / #40 的诊断信息里都能看到这条 FAIL)。既有设计只覆盖「provider **自述**不支持 → 不传该字段」(#15),缺了运行时这一半。修法:新增进程内记忆 `markEffortUnsupported()\/isEffortUnsupported()` —— 首次被拒时**不带该字段重试一次**(同一候选、同一 provider),并记住这个 `provider/model`,此后同一进程内它第一次请求就不再带 effort;记忆**按 provider/model 分开**,不误伤别的 provider。回归守卫加在 `tests/capture-route.test.mjs`(第一次带 → 被拒 → 第二次不带并拿到卡 → 后续只发一次 → 别的 provider 不受影响)。`npm test` 共 **313 项** |
|
|
574
|
-
| **v0.10.18** | 2026-10-12 | **「保存配置」被无条件拒绝的修复(issue #35)**。现场(Dzifxazy):全新安装、什么都不动、直接点保存 → **HTTP 400「字段不能为空:secretHint」**,整单被拒,用户改的数字与开关**一个都存不进去**;文案还会把排查方向误导到「某个数字格子没填」。根因:宿主侧的前置校验把**任何空字符串**都当成「数字没填」,只豁免一份硬编码名单(vaultDir / captureProvider / captureModel / recallEmbedding / activeVault)—— 而 `secretHint` 的默认值**就是**空串(`z.string().default('')`)又不在名单里;面板提交的是整张表单,于是每次保存必然命中。修法:新增 `emptyNumericFields(patch, Config.dict)`,**按 schema 的 `type === 'number'` 判定**「哪些键被清空」,彻底删掉硬编码名单(以后新增可空字符串字段不需要谁记得去补)。新增 `tests/config-validate.test.mjs`(2 项:空串只对 number 字段报「没填」——里面放了一个「不在任何名单里」的新字符串字段做回归钉子;源码守卫不许退回名单)。`npm test` 共 **312 项** |
|
|
575
|
-
| **v0.10.17** | 2026-10-12 | **「常驻实例在服务、却没人托管」这类状态自相矛盾的根治(issue #28)**。现场:用户升级到 0.10.16 后常驻实例没起来,面板**既没有重启按钮也没有任何提示**(横幅只在 `vc.stale` 时渲染,而宿主与端口版本号一致时 `stale` 为假),只看到一条「自动沉淀异常」;本机复现出另一半:`stop` 之后端口上**还活着**、锁里却空了、`status` 说「没有登记的看门狗」—— 因为端口上那个 web 是宿主 `ensureWeb()` 直接拉起的(锁里没有 `webPid`),`stop` 只杀了看门狗。五处一起修:① **`/version-check` 报出 `resident.managed`**(锁里有活着的看门狗才算被托管)—— 「在服务」≠「被托管」;② **面板**:常驻实例**没在跑**或**没被托管**时都显示重启按钮与专门文案(「未运行」/「未托管」),不再只认「版本落后」这一种;③ **`/restart-self`**:`alreadyCurrent` 必须同时满足「版本一致 **且** 被托管」,否则照常替换(孤儿 web 版本再新也得重新托管);④ **显式 `dsh-memory stop` 把端口腾出来**(`forcePortOccupant: true`,闸门不变:只终止 `looksLikeOurWeb` 认得出是自家的进程);⑤ **自检改为「新实例接管」**:`restartResident` 不再只看「端口服着期望版本」(升级前后版本号常常相同、旧实例本就是这个版本 → 自检会被**旧实例**骗过,实测打印「自检通过(等待 0s)」后紧接着 `status` 说没有登记的看门狗),新增 `waitForTakeover()`:必须「版本一致 **且** 锁里出现一个不在替换前 pid 集合里的活看门狗」才算成功,失败原因也据此分离成「版本不对」与「没人接管」。本机同一现场实测:替换前 `41672/17432` → 替换后 `28136/31976`,`restart` 等待 13s 后自检通过,**紧接着的 `status` 立刻显示新看门狗已登记**(不再矛盾)。`npm test` 共 **310 项** |
|
|
576
|
-
| **v0.10.16** | 2026-10-09 | **`--reap` 的输出不再「明明收了却说没收」**(alario-tang 反馈,macOS + 0.10.13 实测)。现场:`kill -9` 掉 watchdog 造出 `PPID=1` 的孤儿 web 后跑 `dsh-memory watchdog --reap --port 7999`,端口确实被释放、web 确实被收掉,屏幕上却写着「**扫描到 1 个 watchdog 进程,清理孤儿 0 个**」—— 因为 0.10.13 让 `scanned` 含了 web、而 `webKilled` 一个字都没打印。修法:新增纯函数 `formatReapSummary()` 统一口径(扫描数拆成 `watchdog A / web B`、清理项按类分别列出 pid),`reapStaleWatchdogs` 返回值补 `scannedWatchdogs` / `scannedWebs`(`scanned` 仍是两者之和,兼容旧调用方),CLI 改用它。测试补 2 项(收了 web 时不许再说「清理孤儿 0 个」/ 扫描计数必须拆开),并在 `tests/watchdog-orphan-web.test.mjs` 头部记下复现要点:**这类孤儿只能用 `kill -9` 造 —— SIGTERM 会走 watchdog 的优雅退出、把它拉起的 web 一起停掉**(与 #19 里「Windows 上 SIGTERM 即无条件终止」是同一枚硬币的两面)。`npm test` 共 **308 项** |
|
|
577
|
-
| **v0.10.15** | 2026-10-09 | **三处「升级留下的尾巴」**。① **推荐方案 A 不再把刚省掉的调用装回来**:v0.10.12 把「蒸馏输出上限」默认提到 4000(省掉「先撞 2000、再以 4000 重跑同一候选」那次白烧),但面板里「一键推荐配置 A(轻量)」仍写死 `captureMaxTokens: 2000` —— 用户点一下就又把重试装回来;现已与默认对齐,并加源码守卫(`tests/csrf-origin.test.mjs` 断言 planA 的上限 === 默认值)。② **README 的「停滞兜底」描述与新判据对齐**(中英两份):旧文案写「15 分钟收不到收尾事件就报」,现在是「**本次运行至今 0 收尾** + 最近 20 分钟内持续有轮次开始 → 记一条 `warn`」,并写明正在进行的**长回合不会被误判**。③ 顺带核实日志里另一种措辞 `轮次收尾事件未触发(agent/turn-stopping 没到)` 最后出现于 **2026-09-24**,属于旧判据的历史残留,现行代码里已无这条路径(无需修复)。`npm test` 共 **306 项** |
|
|
578
|
-
| **v0.10.14** | 2026-10-09 | **「自动沉淀异常:轮次收尾事件疑似失效」误报根治**。现场:几十分钟的长回合里,`agent/inbox/claimed` 在**一轮对话中途**也会多次触发,而 `turn-stopping` 要等回合真的结束才来 —— 于是 20 分钟窗口必然「开始 ≥3、收尾 0」,判据把**还在进行中**的轮次当成「事件被改名」,直接把红警报推给用户(本会话实测那条「累计 39/18」就是这么来的)。修法:加一条硬前提 —— 「事件被改名/移除」是**进程生命周期**属性,不是窗口属性:本次运行里只要收到过哪怕一次收尾,通路就是好的,窗口内 0 收尾只说明当前轮次还没结束(`evaluateStall` 新增 `everStopped`,并在返回值里给出 `reason: wiring-proven | no-stop-ever`);只有「本次运行至今 **0** 收尾 + 窗口内持续开始」才报警。同时把这条**推断**从 `fail` 降级为 `warn` —— 不再由它把沉淀健康状态翻红、更不冒充「自动沉淀异常」(真故障会由写卡失败自己报红)。`tests/stall.test.mjs` 新增 3 项锁住新旧行为(长回合不报 / 真改名要报 / `everStopped` 可注入)。`npm test` 共 **305 项** |
|
|
579
|
-
| **v0.10.13** | 2026-10-09 | **孤儿 `web.js` 终于收得掉了(#11「待做 1」的 POSIX 分支)**。现场(alario-tang,macOS arm64):watchdog 先死 + 锁文件丢失 → 端口 7999 上留着一个 `PPID=1` 的旧 web;`stop`/`restart` 读锁槽找不到目标、`watchdog --reap` 只认 `watchdog.js`、面板按钮又走同一条链路 → 漂移只能报不能自愈,用户被迫手动 `kill`。查出**两个具体缺口**:① **POSIX 侧端口占用者只拿到进程名** —— `lsof -Fpcn` 的 `c` 字段是 `node` 而不是命令行,而「要不要收」的判据(`looksLikeOurWeb`)要求命令行含 `memory-eternal` 或本机 `web.js` 绝对路径 → 判据恒假 → `stopWatchdogs` / `clearResidentForTakeover` 的兜底循环**一次都不杀**(Windows 走 `Get-CimInstance` 拿完整命令行,所以本机从未暴露);现在补一次 `ps -p <pid> -o args=`,拿不到才退回旧行为(宁可不杀、也不误杀),并让 `platform` 可注入以便单测。② **`--reap` 扩到自家 web**:新增 `listWebs` 分支,把「同端口 + 是自家 web + **托管者已死**(该槽 watchdog 不存活)+ 不在保护集合(keepPid / 当前 pid / 父进程 / 带 `--reap` 的同类)」的 web 一并回收;**托管者还活着的 web 一律不动**,别的端口、别人的 `web.js` 一个不杀。新增 `tests/watchdog-orphan-web.test.mjs`(5 项),并把两条只谈 watchdog 的旧用例显式声明 `listWebs: []`(否则 reap 会去列本机真实进程表)。`npm test` 共 **302 项** |
|
|
580
|
-
| **v0.10.12** | 2026-10-09 | **两件「不显眼但每天都在扣钱 / 留洞」的事**。① **蒸馏输出上限默认 2000 → 4000(= 硬顶)**:日志实测高频出现「输出撞到 maxTokens=2000 上限 → 以 4000 重试同一候选」,每一次都是**白烧一次 LLM 调用**(先撞顶、再拿 4000 重跑同一个候选)。maxTokens 只是上限、按实际生成量计费,所以默认直接放到硬顶:同样的轮次一次调用就够;`maxTokenLadder` 仍保留给**显式**把 `captureMaxTokens` 调小的用户。② **插件 API 加跨站写入闸门**:`/memory-eternal/api/*` 无鉴权,本机任意网页都能 POST `/config`、`/audit/approve`、`/write`… 新增 `crossOriginError()` —— 状态变更请求(非 GET/HEAD/OPTIONS)必须与请求自身的 `Host` **同源**:浏览器跨站请求一定带 `Origin`(与 Host 不同源即 403 `CROSS_ORIGIN_BLOCKED`);同源页面(DSH 设置页/记忆页、独立页)与局域网访问都同源不受影响;没有 Origin 的 curl / CLI / MCP 照常放行;`app://` 这类桌面壳自定义 scheme 也放行以免误伤。宿主自己的拦截路由(`/config` POST、`/restart-self`、`/mcp/action`…)与共享 api 入口**都**装了这道闸门。新增 `tests/csrf-origin.test.mjs`(4 项:判定矩阵含局域网 / 无 Origin / null / app://、跨站 POST 被 403 且不进业务逻辑、两处入口都在、默认上限=硬顶且无 ladder 重试)。`npm test` 共 **297 项** |
|
|
581
|
-
| **v0.10.11** | 2026-10-09 | **蒸馏输出解析再补一层容错:字符串里的裸引号(`Expected ',' or '}' after property value`)**。现场:`fail` 日志里 `UNPARSEABLE_OUTPUT` 占大头(最近 600 条里 103 条),最近一条带全了现场 —— `JSON.parse 原始报错:Expected ',' or '}' after property value in JSON at position 1913`,摘录里能看到正文写着 `运行 "npm test" 时…`:模型把引号当正文直接写进字符串,于是它被当成字符串结束符。这与 #24 修过的「裸控制字符」是**不同**的一类(控制字符在字符串内非法,引号只是需要转义)。修法:新增 `repairUnescapedQuotes()` —— 一个 `"` 后面(跳过空白)紧跟 `:` `,` `}` `]` 或文本结尾才算字符串结束,否则当正文补 `\"`;它**只作为最后一个候选**参与解析,且必须仍通过卡片体检(标题合格 + 正文 ≥20 字)才被接受 —— 宁可不修,也不写坏卡。回归守卫 `tests/capture-json-repair.test.mjs`(4 项:合法 JSON 恒等变换、裸引号现场、控制字符与裸引号叠加、截断仍如实失败)。`npm test` 共 **293 项** |
|
|
582
|
-
| **v0.10.10** | 2026-10-09 | **审核中心能看正文了 + 就地批准/驳回(issue #27)**。现场(thinrflbtlm):审核中心过去只列「标题 / 类型 / 来源 / 严重度 / 原因」,**看不到正文**;而 v0.10.0 之后待审卡物理隔离在 `quarantine`、知识卡界面只列已审核卡 —— 于是「不人工确认就没法审」,审核形同虚设。① 每行新增 ▸/▾(点标题也行)**就地展开 frontmatter + 正文**,用与卡片详情**同一套 Markdown 渲染**(`renderMd` + `splitFrontmatter`),不是裸文本;② 正文**按需拉取**(`GET /card?path=…&status=pending|rejected`,服务端只放行确实在审核队列里的 path,未知 path 仍然 403 `CARD_NOT_APPROVED`),列表接口不塞整库正文;③ 展开块里直接给 **✓ 批准 / ✕ 驳回**(回收站页签是 ✓ 恢复 / 🗑 删除),批量勾选与外层工具条保持原样;④ 批/驳后自动收起并刷新,中英双语词条齐全。新增 `tests/issue-fixes-6.test.mjs`(2 项:待审正文读得到且批准后离开队列、源码守卫含「按需拉取」与「越权拒绝码」)。`npm test` 共 **289 项** |
|
|
583
|
-
| **v0.10.9** | 2026-10-09 | **升级期端口漂移把记忆页打成「拒绝连接」的根治(issue #29)**。现场:旧包遗留的 detached 实例占着 7999 且不自报版本 → 宿主 `ensureWebServer()` 认不出它、向后漂到 8000 并写进 `web-info`;而插件的自愈随后把 8000 上的实例当野实例收掉 → `web-info` 永久悬空,客户端 iframe 指向死端口(首次能进、刷新即「拒绝连接 127.0.0.1」)。① **记忆库页改走 host 同源壳**:宿主新增 `/memory-eternal/ui/app`(与配置页 `/ui/config` 同一份 index.html,其中 `<script src="app.js">` 相对路径正好解析到 `/ui/app.js`),iframe 地址从此与「常驻实例监听哪个端口」**彻底解耦**(旧宿主 404 时仍回落 `web-info`)。② **`/web-info` 实时收敛**:每次读之前先探「配置端口 → 记录端口」,谁在服务就用谁,都不通就如实 `alive:false`(不再把死地址当活的);自愈替换成功后与 interval 保活探到活时都就地回写。③ **不再盲目漂端口**:新增 `looksLikeOurWeb()`(宽松识别「自家但不自报版本」的占用者:/overview、/web-info、/budget 任一 ok:true 即认),疑似自家时先给自愈留 6s 接管窗口,等不到才漂并留日志。④ **体检退避重试**:`inspectResident()` 一次探针打空不再直接判「旧版不自报版本」(那个结论会触发误替换,每次启动都白起一次实例),退避重试后才下结论。新增 `tests/issue-fixes-5.test.mjs`(4 项:宽松识别真/假阳性、等自愈接管不漂端口、宿主与客户端源码守卫)。`npm test` 共 **287 项** |
|
|
584
|
-
| **v0.10.8** | 2026-10-08 | **独立 Web 端保存「点一下即生效」:把应用 pending 的上下文从插件回调搬到宿主 HTTP 请求(方案 A,实测驱动)**。现场:独立页保存 → 宿主连续重试 5 次后 `dropped`,报错恒为 `HMR transactions cannot be nested`(0.10.7 只修掉了它冒充「自动沉淀异常」与升级期未知键,同步本身没修)。**实测定位**(同一宿主、同一个 `settings.update`):从插件**回调**(`setInterval` / `fs.watch`)里调 → 抛守卫错且**一个字节都没提交**(只含宿主认识的键、值真的变了,同样失败);从插件 **HTTP 请求处理器**里调 → **真的提交**(`recallSummaryLen` 130→132→130,`revision` 4→6)。所以修法不是换 API 而是换**调用上下文**:① 宿主新增 `POST /memory-eternal/api/drain-pending`,在**请求处理器**里应用 pending(两道闸门:只接受本机来源 + 心跳里的一次性令牌,防本机任意页面乱写);② 宿主把自己 web server 的**监听端口 + 令牌**写进心跳(`webServer.port` 是服务公开的 getter),独立端保存后据此**主动叫醒宿主**,拿到真实结果再回给界面(新增 `savedApplied`「已保存并生效」);③ 叫醒失败(旧宿主没这条路由 404 / 端口对不上 / 超时)**不算错误** —— 回落到 0.10.5 的等待路径与「去 DSH 设置里改」的指路。新增 `tests/host-drain.test.mjs`(6 项:回环判定 / 守卫三件套 / 端到端叫醒 / 失败如实回报 / 404 回落 / 源码守卫)+ `tests/e2e-host-drain.mjs`(2 项:**真起进程**的端到端「独立页保存 → 叫醒宿主 → 宿主应用 → 回报已生效」,以及旧宿主心跳(无端口/令牌)时回落成「排队等待」)。`npm test` 共 **283 项** |
|
|
585
|
-
| **v0.10.7** | 2026-10-08 | **「启动 dsh 后运行中还是旧版本」根治:版本漂移自愈 + 一键重启常驻实例(#19 / #23 的正面解法)**。现场:升级 npm 包只换了磁盘文件,**常驻 web(默认 7999)比 DSH 宿主活得久** —— 端口上那个进程仍在跑「启动时加载进内存」的旧代码;而宿主激活时的策略是「同端口已有活着的 watchdog 就 `delegated`(不再 spawn、更不替换)」,于是**重启多少次 DSH 都不会换掉它**(实测本机:宿主 10:19 启动已是新版、设置页同源显示 0.10.6,而 7999 上的 web 是 08:15 启动的 0.10.0,`/config` 与 `/version-check` 各报一个版本)。① **启动自愈(`autoRestartOnDrift`,默认开)**:激活时先 `inspectResident()` 体检 —— 版本一律**问端口**(锁里的 `pkgVersion` 只是 watchdog 自述,旧实例写的还是空串,根本看不出版本漂移),再 `decideResidentAction()` 决策:一致 → `delegate`(保持 #19 的如实口径)、漂移 → `restart`、读不到本机版本 → 绝不乱动、刚替换过 → 防抖不重来(配置改动会让 effect 重跑)。**「更新运行中的程序」的正确形态由此定为「用新代码重启那个常驻进程」,而不是热替换内存里的模块**:ESM 模块缓存 + 闭包状态(settings / vault 路由 / SSE hub / `fs.watch` / 已监听端口)没有安全热换的余地,缓存失效重 import 只会得到第二份模块图(双实例、双监听、双写)。② **替换机制统一到一处**:新增 `restartResident()` = spawn 一个 detached 的 `watchdog.js --replace` 助手(收旧 watchdog / 旧 web → **等端口真的空出来** → 抢锁 → 拉起新 web),再自检「端口上真正服务的版本 = 本机版本」(#23 建议 2);端口没空出来就**中止接管**、不退让到 `port+1`(退让实例正是孤儿 web 的诞生地)。`dsh-memory restart`、宿主启动自愈、面板按钮现在共用这一份实现。助手是 detached + `stdio:ignore`(stderr 没人看),所以接管结论**必须**写进「自动沉淀日志」—— 否则「重启了却没生效」又会变成一条无迹可查的静默失败。③ **面板一键修复**:新增 `POST /memory-eternal/api/restart-self`,三条硬约束 —— 本进程很可能**就是**要被替换的旧 web,所以**先回响应再调度**替换;常驻实例已经是最新就回 `alreadyCurrent`(宿主面板点这个按钮时最常见,绝不为一个健康实例白折腾一次停机);同一时刻只允许一个替换在跑。④ **面板语义消歧**:「运行中」改为「**运行中(服务本页的进程)**」(独立 Web 页里它是常驻 web,DSH 设置页里是宿主 —— 这正是两个面板数字「打架」的根源),并新增「**常驻实例 vX**」徽标:两者不同时一眼看出该重启谁;漂移横幅给按钮 + 等价命令,若常驻实例已新而本页仍旧,则明说「需重启桌面版 / DSH」,不再给一个点了也没用的按钮。⑤ **`updateAvailable` 改按版本比较**:`latest !== onDisk` 在本地跑未发布版本时会**反过来劝你装回旧版**(磁盘 0.10.7 / npm 0.10.6),现在用 `compareVersions`(含预发布语义 `rc.2 < rc.10`)。⑥ 顺带清掉客户端词典里重复的 `saving` 键(esbuild 每次构建都在报警告)。新增 `tests/watchdog-restart.test.mjs`(17 项:决策矩阵 / 体检问端口 / 替换自检 / 接管前清场不误杀 / 路由先响应后替换 + 并发闸门 / 版本比较)。⑦ **README 改为中英两份独立文件**:`README.md` 是**默认**的简体中文(npm 与 DSH 插件市场展示这一份),`README.en.md` 是英文镜像;顶部加语言切换与「改一处要同步另一处」的说明,并补齐英文版缺失的「环境自检」小节;新增 `tests/readme-i18n.test.mjs` 锁住**结构 1:1**(标题层级序列 + 代码块数量 + 互链 + 默认语言标记),防止以后只改一份。⑧ **`secretHint` 凭证提示 + 凭据读取守卫**:填一句自己的约定就会被注入每个会话的 systemPrompt,让 agent 需要 API key / token 时**先查记忆里的「密钥目录」卡**(记忆库只存目录:名字/位置/取用方式,**值留在本机凭据库**);`dsh-memory setup` 同时注册 Claude Code 的 `PreToolUse` 守卫(拒读 `.credentials.yaml` / `.env` / `*-token` 并指路到加载器),并会刷新仍指向旧安装副本的 hook 路径。⑨ **发布前又抓到三个实测现场并修掉**:① 面板「重启常驻实例」按钮在**服务本页的进程**是旧版时也显示 —— 点了必然 404(旧宿主没有这条路由),用户只看到一句「未知接口」;现在按钮只在「端口上那个常驻实例落后于磁盘」时出现,404/405 还会给一条「重启桌面版 / DSH 或执行 dsh-memory restart」的指路。② 独立页(新版)提交整张表单、宿主还是旧版时:宿主按白名单丢掉它不认识的键(如 `secretHint`)→ 回读校验必然对不上 → 连试 5 次 `dropped`,用户只看到「HMR transactions cannot be nested」;现在应用前按宿主 schema **过滤未知键**(宿主升级后自然生效),并把「配置同步失败」与「自动沉淀异常」彻底分开(`config-warn` / `config-fail`,不再翻红沉淀健康状态)。③ `autoRestartOnDrift` 在宿主快照里没有这个键时按**默认开**渲染(实测现场:0.10.6 宿主 + 0.10.7 面板 → 复选框空着,而宿主实际按开处理,面板与真实行为不一致)。新增 `tests/issue-fixes-4.test.mjs`。`npm test` 共 **274 项** |
|
|
586
|
-
| **v0.10.6** | 2026-10-07 | **孤儿 web 有了回收机制 + 共享配置同步失败不再静默**。① **孤儿 web**:web 子进程是 detached 启动的,watchdog 被硬终止时(Windows 上 `process.kill(pid,'SIGTERM')` 即无条件终止)它的退出处理器不执行;若这个 web 当初因为目标端口被占而**退让到 port+1…**,登记它的锁条目又随 watchdog 一起消失 —— 于是 8001/8002 这类端口上会常驻没人认领的 web(实测本机就躺着两个,其中一个已跑了 20 小时,而 `status` 只探配置端口,永远发现不了)。现在 `startWatchdog` 启动时、以及 `stop`/`restart`(`checkPort` 路径)都会扫**退让窗口** `[port+1, port+9]` 并收掉它们;三道门缺一不可(端口在窗口内 / 命令行**确实是本插件的 web** / pid 不在锁里任何 watchdog 的 `pid` 或 `webPid` 上),**别人的 `node web.js` 一个都不杀**。另外 `spawnWeb` 发现目标端口上已有自家 web 时**不再重复拉起**(退让实例的主要来源),只交给 tick 探活。② **`syncConfigFile()` 不再静默吞错**:它写的是独立页 / hooks / MCP 共用的读源,写不进去就表现为「配置改了但他们看不到」——旧实现是 `catch { /* 静默 */ }`(实测到过一次:共享文件 mtime 停在激活前,而宿主里的值已经变了)。现在失败会进 stderr 与「自动沉淀日志」的 warn(同一原因只报一次,不刷屏);因为 `logCapture` 定义在 `apply()` 更靠后的位置,日志刻意**延迟一拍**再写,避开 `const` 的 TDZ。新增 `tests/watchdog-orphans.test.mjs`(7 项),`npm test` 共 **245 项** |
|
|
587
|
-
| **v0.10.5** | 2026-10-07 | **独立 Web 端的配置保存:不再空头承诺,也不再要求「必须装了 DSH」**。① **宿主无关的直写路径(#21)**:独立端保存后若 800ms 内没人消费 pending 文件、**且**本机没有活着的 DSH 宿主(心跳文件 `memory-eternal-config.host.json` 不新鲜、或里面的 pid 已死),就直接把改动**原子合并**写进共享配置、清掉 pending,响应回 `pendingOutcome: applied-direct` —— 于是 Codex / Claude Code / Cursor、以及只跑 `dsh-memory serve` 的用户,保存当场生效(此前那条路只会写「待应用」等一个**根本不存在**的宿主,界面上那句「下次启动生效」永远不会兑现)。宿主活着时**绝不**直写:共享配置是宿主的派生镜像,绕过它会被下一次 `syncConfigFile()` 盖回去(有专门的反向测试守着这条边界)。② **失败要指路,不只是重复报错(#21)**:识别「宿主守卫类」报错 —— `HMR transactions cannot be nested`(Cordis HMR 宿主)与 `root.events.emit is unavailable from a plugin activation`(dsh-tui 一类)是同一族缺陷的两种措辞 —— 并把「此宿主不允许插件从自己的回调上下文写配置」与「去哪改」(**DSH 设置 → 记忆**,或 profile 的配置补丁层)写进放弃时的报错、诊断信息与独立页提示;`POST /config` 同时回 `hostGuard` / `hint` 字段。③ **界面不再一律弹「已保存」(#21)**:客户端开始消费 `pendingOutcome` —— `queued` / `failed` 用**非成功样式**并说明「尚未确认生效」,`applied-direct` 明确写「本机没有 DSH 宿主,已直接写入共享配置」,只有真生效才显示「已保存」。④ 顺带修掉一个会让宿主/测试**无法退出**的清理竞态:心跳定时器与 pending 文件监听都是在异步 `import()` 之后才建立,若在它 resolve 之前就 dispose,回调就再没人清理(心跳定时器还额外 `unref()`)—— `tests/settings-compat.test.mjs` 用假 ctx 跑 `apply()` 时正是被它挂住的。新增 `tests/issue-fixes-3.test.mjs`(9 项),`npm test` 共 **238 项** |
|
|
588
|
-
| **v0.10.4** | 2026-10-07 | **四个「看着正常、实际静默错位」的 issue 集中修复(#21 / #22 / #23 / #24)**。① **宿主守卫误报的保存不再被当成失败(#21)**:dsh-tui 一类宿主的能力守卫会在 `settings.update` **内部**的 `describe()` 上抛 `root.events.emit is unavailable from a plugin activation`,而写入其实已经提交 —— 旧实现只认「有没有抛错」,会把**已生效**的改动重试 5 次后标成 `dropped`、日志里还报失败。新增 `applyPatchVerified`(抛错后**回读确认**,判据 `patchApplied` 浅比较,命中则记一行日志按成功处理),drain 与独立 Web 端共用这条路径;独立 Web 的 `POST /config` 也不再无条件回「一切正常」:等最多 800ms 回读 pending 文件,回 `pendingOutcome: applied / failed / queued` 并给对应措辞(`waitPendingOutcome`)。② **`/cards` 的 status 不再静默错位(#22)**:`status=deleted` 现在真的列**回收站**(与 `/recycle/list` 的数字对得上),未知取值显式回 `unknownStatus: true` 并回显 `appliedStatus` / `requestedStatus`(回落 approved 的老行为保留,但不再沉默)。③ **升级后以「端口上真正服务的版本」为准(#23)**:`/overview` 新增**当前服务进程**加载的 `version`;`dsh-memory status` 单独报「端口实际服务版本」,对不上就直说「升级尚未生效」(锁里的版本号只是 watchdog 自述;端口上是 **v0.10.4 之前**的旧 web 时探不到版本 —— 它的 `/overview` 还没有 `version` 字段 —— 这时会明说「是本插件的 web 但不会自报版本,即旧版」,而不是误报「没有服务在响应」);`stop` 停完再问一次端口占用者、仍被占就告警;`restart` 不再只信锁里登记的 webPid —— 按**端口占用者**兜底收掉锁里没登记的旧 web(先用命令行确认**确实是本插件的 web**(包名 `memory-eternal`,或本插件自己的 `lib/web.js` 绝对路径)才动手,别人的 `node web.js` 只告警不动手 —— 收紧这条判据是刻意的:误杀别人占着该端口的进程,比不杀更糟),端口没空出来就中止重启,起完自检「端口服务版本 = 本机版本」,对不上以非 0 退出(不再打印「已启动」就完事)。④ **蒸馏解析不再把「字符串里的裸控制字符」误判成无法解析(#24)**:模型把 markdown 正文的换行写成真实 U+000A(括号闭合、`looksTruncatedJson` 为 false)时,旧实现必然 `JSON.parse` 失败 → 整卡退成原文卡;新增 `repairJsonControlChars` **只在字符串内部**把裸控制字符转成合法转义(`\n` `\r` `\t` `\b` `\f`,其余 `\u00XX`;字符串外的换行是合法 JSON 空白,一律不动),候选串顺序为「原样 → 截 `{ … }` → 各自的已修复版」;解析失败的消息不再吞掉 `JSON.parse` 原始报错,现场摘要改为**首尾各留一段**(`describeOutputExcerpt`,旧的 `slice(0,160)` 恰好把「被切在 `"body":` 半截处」显示得像输出断了);撞输出上限的重试从「只翻倍一次」改为 `maxTokenLadder` **一路翻倍到 schema 上限**(1200 → 2400 → 4000,不再止步 2400)。新增 `tests/issue-fixes-2.test.mjs`(21 项),`npm test` 共 **229 项** |
|
|
589
|
-
| **v0.10.3** | 2026-10-06 | **修复 `audit list` 的计数语义(#20)**。① 总数与 `--limit` **解耦**:`共 N 张` 取**匹配总数**(`countCards`),`--limit` 只限制显示条数,截断时明确写出 `显示前 M 张 … 还有 X 张未显示`。② **默认不再静默截断**(不给 `--limit`、或 `--limit 0` = 全部显示)。③ `--json` 拆分为 `total`(匹配总数)/ `returned`(本次返回)/ `count`(= total 的兼容别名;0.10.2 里它误等于 returned)/ `limit`(null = 全部)/ `hasMore`。④ `--status all` 的语义写进用法与 README:= 审核队列(pending + rejected),**不含**已出队的 approved 与回收中心的 deleted;`approved` 可显式查询。⑤ **同源修复 MCP 的 `memory_audit_list`**:它同样存在「默认 20 静默截断 + 把截断条数报成待处理数」,现改为报匹配总数 + 未显示提示,上限 100 → 500。 |
|
|
590
|
-
| **v0.10.2** | 2026-10-06 | **五个存量 issue 集中修复(#15 / #16 / #17 / #18 / #19)**。① **按项目选库在宿主侧终于生效(#15-1)**:宿主过去拿进程 cwd 去匹配 `vaultProfiles[].match.workspace`,而那是「启动 dsh 的目录」——现在从 `session.header.cwd` 取**会话自己的工作区**(capture 与 memory_recall 两条路径都传),CLI/MCP/hooks 行为不变。② **蒸馏不再选错 provider(#15-2)**:声明 `supportsReasoningEffort:false` 的候选不再被传 `reasoningEffort`(能力未知一律按支持处理),并把它们排到支持者之后但**不删除**,避免兜底全无。③ **输出截断不再伪装成解析失败(#18)**:新增独立错误码 `MAX_TOKENS`(含实际上限值),撞上限自动以双倍上限重试同一候选;兜底上报改为**始终以第一候选为主因** + 其余错误按码汇总(不再被最后一个候选覆盖);默认 `captureMaxTokens` 900 → **2000**;失败兜底的原文卡加 `distill-failed` 标签便于过滤。④ **独立 Web 端保存配置不再静默丢失(#16)**:drain 报错不再被 `catch {}` 吞掉(进 stderr + 自动沉淀日志,5 分钟去重节流),共享配置 `memory-eternal-config.json` 改为 **tmp + rename 原子写**(原先非原子写会在截断窗口内被独立 web / MCP 读到半截内容 → JSON.parse 失败 → 设置静默回落成默认值),重试耗尽后**保留 pending 文件**并标注 `dropped:true` + `lastError`(不再删除用户改动),失败状态进 `/diagnostics` 与 `/config` 响应。⑤ **审核中心有了 CLI(#17)**:`dsh-memory audit list/approve/reject`(多路径批量、`--json` 便于 jq 筛选、复用同一套 `setCardStatus` 守卫与 `audit_log`);MCP 侧只新增**只读**的 `memory_audit_list`,不暴露 approve/reject。⑥ **看门狗有了生命周期(#19)**:新增 `dsh-memory status/stop/restart`,锁文件写入 `pkgVersion`(status 会提示「常驻实例是旧版」),宿主日志按抢锁结果如实打印 `spawned` / `delegated to existing pid`(不再每次都谎报 spawned),关闭 `watchdogAutoSpawn` 时明确提示既有实例仍在跑;并且 `dsh-memory stop` 会**连同该 watchdog 拉起的 web 一起停**(Windows 上 `process.kill(pid,'SIGTERM')` 是无条件终止,watchdog 的退出处理器根本不会执行 —— 旧写法会在 stop 后留下一个占着端口的孤儿 web;web 子进程 pid 现在记在锁里,杀之前还会用进程命令行确认确实是 `web.js`,防 pid 复用误杀);README 修正「默认值自相矛盾」与「重启即可关闭」的错误承诺。 |
|
|
591
|
-
| **v0.10.1** | 2026-10-02 | **修复「导出 OK、导入 0 张」**。根因:客户端「导出JSON」写出的是**裸数组**,而 `/import` 只读 `payload.cards` —— 裸数组被静默当成空备份(`ok:true` / `imported:0` / `skipped:0`),UI 只显示「导入完成:0」,看不出是格式不匹配。① `/import` 现在同时接受**裸数组**(v0.10.0 及以前的备份)与**信封对象** `{format, formatVersion, exportedAt, count, cards}`;形状不认识 → **400 + 人话原因**,空文件 → `warning`,坏 JSON → 原有的解析失败提示;② 导出改为**带信封**(老版本只认 `payload.cards`,信封对老版本也互通)并保留每张卡的 `status` / `store`;③ 导入去重基线改为**导入前快照**(`writeCard` 新增 `dedupAgainst`)—— 否则同一份备份里互相近似的卡会在写入过程中互相判重、被自己人吞掉一批(备份是真相源);④ 导入应答补 `total` / `skipped` / `quarantined` / `failed[]`(含每条未导入原因),提示语改为「导入完成:N 张 / 文件共 M 张 · 待审核 K · 跳过 D」,一张都没进来时按**失败样式**提示;⑤ 新增 `tests/import-roundtrip.test.mjs`(6 项:裸数组 / 信封 / 导出→导入全量往返 / 重复导入如实报重复 / 坏形状 / 审核守卫不可绕过)。⑥ **装坏了也要说人话(issue #14)**:从插件市场装出来的副本可能缺 `web/` 静态资源,那时侧边栏「记忆」只会弹一页 `{"ok":false,"error":"ENOENT …"}` —— 现在缺 `index.html` 但 `app.js` 还在就用**内置外壳**把界面拉起来(自救),缺 `app.js` 则回一段把诊断画进 `#root` 的 JS + 一页含「缺哪个文件 / 绝对路径 / 包版本 / 怎么重装」的说明;两种缺失都写进自动沉淀日志(`app.js` → `fail` 健康态亮红、只缺 `index.html` → `warn`),宿主启动时也自检一次(判据与措辞由新增的 `lib/web-assets.js` 统一,附 `tests/web-assets.test.mjs` 6 项)。⑦ 顺带修 `appendCaptureLog` 在 `DSH_HOME` 目录不存在时静默失败(诊断信息连一条都留不下)。⑧ **DSH 兼容范围放宽到 `>=0.1.5-alpha.2 <0.3.0-0`**:旧上界 `<0.2.0` 按 semver 会把 DSH `0.2.0-rc.x` 排除在外(DSH 守卫用 `includePrerelease:true`,实际装得上,但声明与事实不符)——现在覆盖整个 0.1/0.2 线,`<0.3.0-0` 仍挡住 0.3.0 及其预发布;`dsh.compatibility` 同步补 `"0.2.0-rc.2": "compatible"`(本机在官方桌面版 0.2.0-rc.2 上实测运行)。⑨ **修三个第三方插件清单长期非法 JSON**:`.claude-plugin` / `.codex-plugin` / `.cursor-plugin` 的 `description` 收尾引号被双重编码吃掉(最后改动停在 v0.9.23),DSH 不走这三个文件所以一直没暴露,但 Claude Code / Codex / Cursor 侧安装**必然解析失败**;现已重写为合法 UTF-8 JSON、`version` 跟到 0.10.1,并新增**随包体检守卫** `tests/manifests.test.mjs`(3 项:随包 JSON 全部可解析 / 三清单 `name`+`version` 与 package.json 一致 / 随包文本无编码事故乱码)。实测用户 **661 张真实备份(裸数组、3.9MB):修复前导入 0 张 → 修复后 661 张**(654 进主库 + 7 进隔离区)。`npm test` 共 **187 项** |
|
|
592
|
-
| **v0.10.0** | 2026-09-30 | **两套存储:未审核内容物理隔离**。① `cards`(主库)只存 `approved`,新增 `quarantine`(隔离区,含 `quarantined_at` / `quarantine_reason`)存 `pending`/`rejected`/`deleted`;② 召回/检索/图谱/去重/导出等读路径**不再需要状态条件**(查主库即安全),根治 v0.9 审计出的三处绕过审核漏洞(去重池把新知识写进待审卡、`readCard` 直读未审核正文、`/card` 无状态校验);③ 审核流转改为**跨表搬家**(事务内完成,id 由新增的 `card_sequence` 单一发号器分配,`card_updates` 随卡改绑);④ 首次启动**自动迁移**存量非 approved 卡(不删内容,幂等);⑤ 新增 `checkMainStoreInvariant()` 不变量体检与 `tests/two-store.test.mjs`(15 条);⑥ 入料噪声闸门:剥离 DSH 运行时注入(环境快照 / team 广播 / teammate 原文 / 工具说明),挡住碎片卡与糊标题;⑦ 修 `settings-compat` 在 Windows 上的原生崩溃(`fs.watch` 父目录 → 改监听文件) |
|
|
593
|
-
| **v0.9.23** | 2026-09-30 | **保存不再闪烁**。保存成功后客户端会立刻重拉 `/config`,而宿主 volatile 回流是滞后的 → 输入框会「先退回原配置、再跳回修改后的值」。现在客户端把「已保存但宿主未回显」的字段做**本地叠加**(新增 `src/client/config-merge.js` 纯函数 + 4 项单测),宿主回显一致后自动摘除,之后仍跟随宿主权威值;重置表单会清空叠加层。`npm test` 共 **139 项** |
|
|
594
|
-
| **v0.9.22** | 2026-09-30 | **配置准即时同步(SSE 推送 + 文件监听)**。① 新增 SSE 推送中心 `lib/sse.js`:DSH 内嵌宿主与独立 Web 宿主都暴露 `/memory-eternal/api/events`,配置一变更就推给所有已打开的页面,客户端订阅后自动重载 —— **正在编辑时不覆盖你的输入**(dirty 标记)。② 独立页保存 → DSH 应用:从 5 秒轮询升级为 `fs.watch` **毫秒级**触发(5 秒轮询保留为兜底)。③ 独立页监听共享配置文件:DSH 端一保存,独立页立刻同步。新增 `tests/sse.test.mjs`(5 项),`npm test` 共 **135 项** |
|
|
595
|
-
| **v0.9.21** | 2026-09-30 | **保存成功不再被误读为失败 + 配置页排版修复**。① 保存成功一律用**成功样式**提示(原来「已写入;宿主尚未回流」被弹成红色失败样式,用户以为保存失败),措辞改为「已写入配置文件;宿主仍是旧版,重启 DSH 后即以新值为准」;② 配置页字段网格列宽 150/160px → **200/220px**、间距加大、长中文标签允许换行,勾选框与说明文字顶端对齐,字段不再互相挤压。 |
|
|
596
|
-
| **v0.9.20** | 2026-09-30 | 独立 Web 页补**取值范围校验**:`/config` 镜像 Config schema 的 min/max(如 `recycleRetentionDays` 1–3650、`webPort` 1–65535),越界直接 **400** 并说明「需要 1–3650,当前 0」。至此「数字框清空 / 越界」这条最常见的保存失败链路在**客户端 + 独立页 + 宿主**三层都被拦住并给出字段级原因。 |
|
|
597
|
-
| **v0.9.19** | 2026-09-30 | **「保存失败」不再无信息量(根因:数字框清空=0 被 schema 拒绝,宿主回空 500)**。复现与实测:越界值 / 清空数字框 / 类型错误 → DSH 宿主返回 **HTTP 500 + 空响应体** → 客户端 `r.json()` 抛错 → 只能显示通用「保存失败」。修复:① **数字输入框清空不再静默变成 0**(JS 里空串转数字得 0,而 `recycleRetentionDays` 的 schema 是 `min(1)`,直接失败);② 保存前按 schema 范围**逐字段自检**,指名报错(例如「recycleRetentionDays」需要 1–3650 之间的数字,当前 0);③ 响应体兜底解析:非 JSON / 空响应体时显示 HTTP 状态码与原始片段,而不是「保存失败」;④ 宿主 POST 外层兜底,**绝不返回空响应体**,并对空字符串数值字段前置返回 400 + 字段名;⑤ 独立页 /config 增加类型与范围校验(拒绝空数值、类型不符),避免写入非法「待应用」值;⑥ `drainPendingConfig` 增加失败计数,连续 5 次失败后放弃并删除(不再每 5 秒无限重试)。`npm test` 共 **130 项** |
|
|
598
|
-
| **v0.9.18** | 2026-09-30 | **独立 Web 页终于能保存配置 + 只读状态不再「毫无反应」**。根因(issue #12 的另一半):独立 web server 进程**没有 DSH 的 settings 服务**,它的 `/config` 过去固定返回 `writable:false`,客户端 `save()` 第一行就 `if (readonly) return`,按钮还是 `disabled` —— 所以点保存**一点反应都没有**,与插件版本无关。修复:① 新增共享文件协议 `lib/config-sync.js`:独立页保存 → 原子写 `memory-eternal-config.pending.json` → **DSH 端激活时与每 5 秒轮询应用**(`settings.update(patch, undefined)` 跳过乐观并发校验),成功后删除,失败保留重试;DSH 没运行时改动留在文件里,下次启动生效。② 独立页 `/config` 返回 `writable:true, viaPending:true`,页面顶部显示蓝色说明「保存会写入待应用文件,由 DSH 自动同步」。③ **只读时按钮不再禁用**:点击必弹出明确原因(`🔒 只读` + 指引「桌面版 → 设置 → 记忆」),彻底消灭「按了没反应」。新增 `tests/config-sync.test.mjs`(4 项:读写往返、损坏内容容错、drain 成功即删/失败保留、clear 幂等)。`npm test` 共 **125 项** |
|
|
599
|
-
| **v0.9.17** | 2026-09-30 | **「更新了却还是旧界面」根治**。① 构建期把插件版本注入客户端 bundle,配置页新增「**页面脚本 vX**」徽标;② 当**页面脚本版本 ≠ 磁盘版本**时醒目提示「⚠ 当前页面仍在运行旧版脚本(页面 v{page} / 磁盘 v{disk}):**按 Ctrl+F5 刷新页面即生效**」;当「运行中(宿主加载)」落后于磁盘时,提示语也改为「先刷新页面,宿主半未更新才需重启桌面版 / DSH」。起因:issue #12「保存配置没有任何反馈」的真实原因之一,就是页面里跑的还是更新前加载的旧 JS —— 宿主已设 `Cache-Control: no-store`,但**已打开的页面不会自动重新加载脚本**,不刷新永远是旧的。另:独立 web 服务(`:7999`,9/29 启动的旧进程)已重启到新版并验证 `loaded=onDisk=latest` |
|
|
600
|
-
| **v0.9.16** | 2026-09-30 | **修复配置保存不生效(issue #12)+ 配置项全覆盖**。① **保存链路**:宿主 `dsh-settings.write()` 要求「面板持有的 revision 与 describe **完全一致**」,revision 一过期就抛 `SettingsConflictError` 导致保存失败 —— 现在遇到冲突会自动取最新 revision **重试一次**(提示里标 ♻)。② **宿主 volatile 回流滞后**:写成功后把改动叠加在本地视图(面板重开立刻是新值),等宿主快照追上再自动摘除;宿主 POST 新增**写入回读校验**,未回流时返回 `pending` 并提示「已写入,重启 DSH 后以配置文件为准」。③ **保存即时反馈**(你此前反馈过):保存按钮旁直接显示 ⏳ 保存中 / ✅ 已保存 / ⚠ 错误,并标明是否重试/是否回流;按钮文案修正(原来误显示「导出中…」)。④ **配置全覆盖**:补齐 7 个以前只能手改配置文件的字段 —— **总开关 `enabled`**、单库目录 `vaultDir`、蒸馏路由 `captureProvider` / `captureModel`、沉淀冷却 `captureCooldownMs`、语义召回 `recallEmbedding`、会话预算 `sessionBudgetChars`;宿主 `/config` 补 6 项、独立 web `/config` 补 9 项。⑤ 新增**永久守卫测试**:`tests/config-coverage.test.mjs` 保证每个 Config 字段都被「宿主 API / 独立 web API / 配置页」三处覆盖(漏一个就红),`tests/settings-save.test.mjs`(5 项)钉死 revision 重试、回流兜底与「错误不吞」。`npm test` 共 **121 项** |
|
|
601
|
-
| **v0.9.15** | 2026-09-29 | **只带服务器侧图谱提速(渲染与 v0.9.13 完全一致)**。① **建图落盘缓存**:按指纹缓存 nodes+edges 到 `<vault>/memory-eternal-graph-cache.json`(原子写、指纹失效、超 8MB 不缓存、写失败只影响下次重建),596 卡实测冷启动 **422ms → 11ms**;② **bigram 哈希化 + 稀有 gram 计数排序**:`bigramHashSet()` 把 bigram 压成 32 位整数入 `Set<number>`(**写卡去重仍用字符串版,判定语义不变**),稀有倒排改按 df 计数排序(202 → **54ms**);③ **MinHash + LSH 候选生成**(48 维签名 / 24 band,与稀有倒排取并集后**精确校验**):jaccard 调用 15,148 → 6,797,相似度段 210 → 130ms。全量冷建图 **600 → 422ms**,边数与 v0.9.13 一致(tag 2777 / similar 4)。**客户端零改动**:不做精灵/静态层/骨架降级(v0.9.14 那三项因影响手感已回退并 deprecated)。新增 `lib/minhash.js` + `tests/minhash.test.mjs`(4 项),`npm test` 共 **108 项** |
|
|
602
|
-
| **v0.9.13** | 2026-09-28 | **按用户反馈修 7 项**。① **版本跟踪**:配置页「插件信息」同时显示**运行中(宿主加载)/ 磁盘安装 / npm 最新**三个版本;磁盘比运行中新时给橙色警告(「磁盘已安装 X,但当前进程仍加载 Y —— 重启后才生效」),并新增「🔄 检查更新」(服务端查 npm,10 分钟缓存 + 4s 超时)。② **复制失败**:记忆弹窗在 iframe 里、默认没有 clipboard-write 权限,`navigator.clipboard.writeText` 必然被拒 —— 现在先试 Clipboard API,失败退 `document.execCommand('copy')`,再失败就把内容摊进只读文本框并给「全选」;iframe 加 `allow="clipboard-write; clipboard-read"`。③ **保存没有提示**:保存结果显示在保存按钮旁(⏳ 保存中… / ✅ 已保存 / ⚠ 错误),文案从错误的「导出中…」改为「保存中…」。④ **回收中心**:恢复 / 彻底删除 / 清空都有成功与失败提示,空库给说明与禁用态。⑤ **通用按键反馈**:审核中心的批准 / 驳回 / 删除原先 `catch{}` 吞掉一切,现逐条统计成功失败并提示;未选卡片也提示。⑥ **图谱缩放卡顿**:新增 `src/client/graph-lod.js` —— **视口剔除**(屏幕外节点/边不画)+ **标签预算**(≤120 全画 / ≤300 画 160 / ≤600 画 90 / 更多画 60,优先焦点、悬停、搜索命中、高度数)+ **文本宽度缓存** + **大图关阴影**。⑦ 新增 `tests/graph-lod.test.mjs`(5 项),`npm test` 共 **108 项** |
|
|
603
|
-
| **v0.9.12** | 2026-09-28 | **新增「反馈异常」入口(左栏 🐞)**:弹窗里填完问题描述后可 ① **一键打开 GitHub 预填 issue**(标题 / 正文 / 诊断信息 / `bug` 标签都已填好,再点一次 Submit 即提交),或 ② **复制一段给 AI 的提示词**(贴进对话框,让 AI 先用 `gh issue list` 查重、再建 issue 并回报链接)。**为什么不做「点一下直接提交」**:GitHub 不允许匿名创建 issue;插件内置 token 会被反编译提取;OAuth / 中转需要自建服务 —— 所以走「预填 + 一次点击」。诊断信息由新接口 `/memory-eternal/api/diagnostics` 生成并**自动脱敏**(home 目录 → `~`,`sk-` / `ghp_` / `github_pat_` / `api_key:` 等 → `***`,截断到 2400 字符),issue 链接用**二分截断**保证不超长(`URLSearchParams` 会把中文膨胀约 9 倍)。新增 `lib/feedback.js`(纯函数层)与 `tests/feedback.test.mjs`(4 项:脱敏 / 诊断字段与限长 / 预填链接编码与截断 / 中英双语提示词),`npm test` 共 **103 项** |
|
|
604
|
-
| **v0.9.11** | 2026-09-28 | **修复 `watchdog --reap` 把自己杀掉**:直接运行 `node lib/watchdog.js --reap` 时,调用者自己的命令行同样含 `watchdog.js` + 同端口、且不在锁里 —— reap 于是把**自己** SIGTERM 了,表现为「一条输出都没有 + 退出码 1」(走 `dsh-memory watchdog --reap` 时因命令行不含 `watchdog.js` 而侥幸没事,所以只在直跑时暴露)。现在保护集合固定包含 `keepPid` / 当前 pid / 父进程 pid,并跳过命令行带 `--reap` 的其它进程;输出改成 `fs.writeSync(2, …)` 同步写,不再被 `process.exit` 截断。回归测试补「自己与父进程都不得被杀」(用真实空转子进程当靶子,不再用假 pid)。`npm test` **98 项** |
|
|
605
|
-
| **v0.9.10** | 2026-09-28 | **多库与看门狗收尾 + 图谱帧优化**。① **多库(#10)**:`vaultProfiles` / `activeVault` 终于有了配置页表单(增删库、填目录、选激活库,并显示当前生效目录 / 来源 / workspace);vault 解析抽成 `lib/vault-resolve.js`,宿主与 CLI / MCP / hooks / 独立 web / sweep **共用同一份**,新增 `vaultProfiles[].match.workspace` **按项目目录自动选库**(最长前缀优先、opt-in,默认仍是单库);`memory_recall` 增加 `scope` 参数(库名 / 路径前缀 / `all` 跨库聚合),跨库结果标注来源库。② **watchdog(#11)**:锁改为**按端口分槽**(多端口 / 多 vault 并存不再互相覆盖),新增 `dsh-memory watchdog --reap` 回收升级前遗留的孤儿进程(只清同端口、且不在锁里的;锁中活跃实例与用户手动起的都不动)。③ **图谱帧时间**:边改为按「颜色 + 透明度 + 线宽」分桶后用 **Path2D 一次描边**(2.7k 条边从 2.7k 次绘制调用降到几个桶),节点径向渐变改为**按 颜色\|半径 缓存 + translate 复用**(593 次/帧 → 首帧之后基本不再创建)。④ 新增 `tests/vault-resolve.test.mjs`(7 项)、看门狗测试扩到 7 项;`npm test` 共 **98 项** |
|
|
606
|
-
| **v0.9.9** | 2026-09-28 | **修复侧边栏「记忆」入口被挤出可视区(issue #2)**:槽位宿主有两种形态 —— 壳自己的横向行容器 `.footerActions`,以及被其他插件(`dsh-diff-approval` / `dsh-footer-order` 等)改成 `flex-direction: column` 的 `div[data-slot="sidebar.footer.action"]`。旧 CSS 对两者都套了 `flex-wrap: wrap` + `.me-footer { flex: 1 1 100% }`:纵向容器里 wrap 的语义是「换列」、`basis: 100%` 指的是**高度 100%**,叠加后按钮被挤进右侧溢出列、裁切到只剩右边缘一条缝。现在 `flex-wrap` 只留给横向 `.footerActions`,`.me-footer` 改用方向无关的 `flex: 0 0 auto`,并新增 SSR 回归断言。另:README 双语新增「交流群 & 打赏」(群二维码 + 微信收款码 / 赞赏码),新增 `SPONSOR.md` 与 `.github/FUNDING.yml`(FUNDING 只支持文字链接,故 custom 第一项指向 SPONSOR.md 页面直接展示二维码) |
|
|
607
|
-
| **v0.9.8** | 2026-09-28 | **集中解决存量问题**。① **采集可信性**(issue #3 / #4):蒸馏路由不再写死 `providers[0]` —— 新增 `captureProvider` / `captureModel` 配置,并按注册顺序**依次重试**;**流末尾 `finish` 块里的适配器错误**(缺凭证 `MISSING_CREDENTIAL`、限流等)现在成为**可见失败**(沉淀日志写明 code/message + 健康态亮红 + 兜底原文卡注明原因),不再笼统记成「蒸馏无输出」;停滞探测从「累积计数差额」改为**滑动窗口**(20 分钟内持续开始且零收尾才告警),误报与漏报一并修掉。② **独立进程 vault 一致性**(dsh-memory-eternal#5):CLI / MCP / hooks / 独立 web / sweep 与宿主共用同一套优先级(`MEMORY_VAULT_DIR` → `activeVault` → `vaultDir` → 默认库),切库后不再数据分裂。③ **看门狗单例锁**(dsh-memory-eternal#6):同端口已有活着的 watchdog 时新进程直接退出并清理陈旧 pid 记录,止住「每次重启多一个孤儿进程」。④ **图谱性能**:标签边改「共享标签数 Top-K」(593 卡实测 **24,193 → 2,763** 条边)、相似度改**稀有 bigram 倒排候选**(冷启动 **948 → ~520ms**,warm 2-4ms)、payload **3,531KB → 579KB**;客户端 dim 判定改**邻接 Set**(每帧 **52ms → <1ms**)。⑤ 新增 5 个测试文件(路由/finish、停滞窗口、vault 解析、看门狗锁、图谱性能基线),`npm test` **85 项** |
|
|
608
|
-
| **v0.9.7** | 2026-09-28 | **打开卡片默认全部展开(可收起)+ 正文排版重做**:卡片阅读器不再默认折成 300px,而是**打开即读全文**,长卡可一键收起(折叠后底部给「展开 ⌄」提示)。frontmatter 从正文里抽出来,改成 kind / 审核状态 / 标签 / 时间 / 来源的**彩色 chips**(kind 沿用图谱配色),不再把一段 YAML 当正文露出来。正文排版换成新方案(`src/client/markdown.js`,宿主与独立 web 共用同一套):小标题按语义自动配 **emoji + 颜色分组**(结论 ✅绿 / 风险 ⚠️琥珀 / 根因 🔍蓝 / 方案 🛠️紫 / 示例 💡粉),连续 `- ` 合成真正的 `<ul>`、`1.` 合成带圆形序号的 `<ol>`、`- [x]` 变复选框、`>` 引用升级成带 emoji 的 callout、`---` 变分隔线、行首「短键:值」渲染成对齐样式、`**加粗**` 加荧光笔下划线;围栏代码整段保护,内部 `#` / `-` 不再被误排版。**新增 7 项排版单测**(含 XSS 转义、链接协议白名单、散文冒号不得误判成 key/value)。另含 PR #8 移植:Electron 宿主下的子进程 node 解析(`lib/node-bin.js`,修「独立 web server 拉不起来 → 侧边栏弹窗白屏」)与 `webServer` 就绪后再注册 API 路由(修「设置页永远加载中」) |
|
|
609
|
-
| **v0.9.6** | 2026-09-28 | **修复官方桌面版全屏浮层挡住窗口控制面板(点「关闭」会退出整个桌面壳)**:桌面壳把「最小化 / 最大化 / 关闭」画在页面顶部,并按 DSH 约定在 `<html>` 上打 `data-windows-titlebar` + 给出 `--dsh-windows-titlebar-height`(已在官方桌面版 `app.asar` 里确认这两个标记确实存在);记忆库全屏浮层原本是 `inset:0` + 内联 `100vh`,整条盖住标题栏,而我们右上角的「×」恰好压在窗口「关闭」上 —— 用户一点就直接退出整个桌面壳。现全屏态改用 `.me-modal-full` 类(不再写死内联 `100vh`:内联样式无法被 CSS 覆盖),并在 `[data-windows-titlebar]` 下把 `.me-overlay-top` / `.me-overlay` / `.me-modal-full` 一律下移 `--dsh-windows-titlebar-height`、高度改为 `calc(100vh - 该高度)`;浏览器里没有该属性时偏移 0px,行为与之前完全一致。导出图谱的全屏预览同步从 `100vh` 改为 `100%`,避免在缩短后的浮层里溢出。新增 1 项渲染回归断言(三处让位规则必须出现在打包产物中) |
|
|
610
|
-
| **v0.9.5** | 2026-09-28 | **修复官方桌面版(schemastery 3.18.4)下插件整体挂不上**:桌面版 profile 解析到的是 **schemastery 3.18.4**,它会把标了 `volatile` 的 Config 字段解析成 cosmokit 的**活引用**(`{ get(): snapshot }`,品牌 `Symbol.for('cosmokit.volatile.write')`),而 0.9.4 的业务代码把字段当普通值用 —— `cfg.vaultDir.trim()` 当场抛 `TypeError: cfg.vaultDir.trim is not a function`,插件在 `apply` 里就崩(桌面版实测报错原文)。现按 cosmokit 协议统一**深解引用**(引用取快照、数组/对象递归展开),`settings.get()` 对外只给普通值;每次读取都重新解,loader 原地提交的 volatile 热更新照样立刻可见,`JSON.stringify` 写共享配置也不再变成 `{}`。新增 2 项回归(3.18.4 真实引用形态 + 嵌套数组/对象),并用**桌面版真实模块解析布局**(schemastery 3.18.4 + 真 cosmokit)跑通 `apply` 验证 |
|
|
611
|
-
| **v0.9.4** | 2026-09-28 | **适配 DSH v0.1.7-rc.2(修复「升级后插件整体失联」)**:① **设置服务换血** —— 0.1.7 的 `ctx.settings` 只剩表单 API(`describe/update/replace/mutate/configure`),不再有 `register()`;旧代码 `ctx.settings.register(...)` 直接抛 `TypeError`,插件根本挂不上(记忆页消失、自动沉淀停摆)。现改为跨版本兼容层:旧版仍走 `register`,新版读 loader 传进来的**活配置引用**、订阅 `loader/volatile-update` 做热更新、写回走 `settings.update(条目 id, patch, revision)`(revision 冲突映射 HTTP 409),并注册 `configure({ auto:false })` 免得宿主再生成一张重复的表单页。② **字段 volatile 标记改为构造后统一打**:不再链式写 `.volatile()` —— 该方法 schemastery ≥3.18.4 才有,而插件在 profile 层实际解析到的是 **3.18.1**,链式调用会在 `import` 期抛 `volatile is not a function`,把插件整个打挂。③ **客户端去死引用**:`inject` 移除已随 `@deepseek-ai/dsh-client-runtime` 一起消失的 `settingsScope`(0.1.7 客户端服务表里已无此服务,插件会永远等待服务而永不激活 → 记忆页与侧边栏按钮全部不见);`dsh.client.inject` 同步去掉不存在的包名。④ 新增 **`tests/settings-compat.test.mjs`**(8 项:0.1.7 形状挂载 / 活引用读值 / volatile 热更新 / 写回与 revision 冲突 / 老版本 `register` 路径 / 字段 volatile 标记),已并入 `npm test`。已在**真实 DSH v0.1.7-rc.2** 上启动验证:宿主插件正常挂载(配置落盘 `memory-eternal-config.json`)、客户端 bundle 正常进入 boot graph(`plugins/??memory-eternal/client.js`) |
|
|
612
|
-
| **v0.9.3** | 2026-09-12 | **修复记忆页白屏("没画面")**:v0.9.1 把「滚动续拉」的 `useEffect` 写在了 `loadCards` 定义**之前**——React 在渲染期求值依赖数组,触发 TDZ(`Cannot access 'loadCards' before initialization`),**整个记忆页一渲染就崩**;现已移到定义之后。**新增渲染冒烟测试**(`tests/ssr.test.mjs`:真跑打包产物、把卡片/设置/用量/图谱四个入口各渲染一遍,已并入 `npm test`)——「构建全绿、页面白屏」这类 bug 以后发不出去 |
|
|
613
|
-
| **v0.9.2** | 2026-09-12 | **日志可见性 + 误报修复**:自动沉淀日志**落盘持久化**(`~/.dsh/memory-eternal-capture.jsonl`,最多 500 条)——独立 Web 页不再显示「日志仅在 DSH 宿主内可用」而是直接能看到记录,宿主重启后历史也不丢(启动时读回);修复**停滞探测误报刷屏**(轮次计数对不上其实是常态:子代理轮次、被取消的轮次都不发 `turn-stopping`;现在要求「有轮次在跑 **且** 15 分钟无任何沉淀活动」才报一次,活动恢复即自动撤销,不再每 10 分钟刷一条把面板挤满、健康状态永久亮红);蒸馏无输出/JSON 解析失败时**改为原文卡兜底**,内容不再白丢 |
|
|
614
|
-
| **v0.9.1** | 2026-09-12 | **性能与体验批次**:记忆页按视图**懒加载**(打开「设置」不再顺带拉 250KB 卡片 + 890KB 图谱,首屏只需约 0.5KB);知识图谱生成 **3232ms → 43ms**(bigram 集合只算一次 + 数学上界剪枝无损跳过 + 标签倒排),并加**指纹精确失效**的服务端缓存(写卡/审核/删卡立即重建,无脏读);全站 **brotli q5**(图谱 877KB → **54KB**,比 gzip 再小 34%;卡片 246KB → 83KB → br 20KB),大响应体同时压 br + gzip 取更小者,保证任何响应都不比 gzip 差;`/cards` 支持**真分页**(offset/limit/total + 服务端标题排序,跨页全局有序),首屏只拉 100 条、滚到底续拉;「用量/今日」的 Agent 卡清单改为服务端署名过滤(30 条);**审核红线**:插件在 Agent 上下文注入「禁止调用 approve/reject 代用户审批」;watchdog **回收备用端口的多余实例**(实测清掉一个常驻 76MB 的重复 web 进程) |
|
|
615
|
-
| **v0.9.0** | 2026-09-12 | **修复自动沉淀静默失效(一连 6 天不写卡)**:DSH 升级后会话事件接口由 `events` 改为 `ownEvents()/snapshotEvents()`,监听器读不到事件后静默返回、不留任何痕迹 —— 现改为三级自适应取法(`ownEvents()` → `snapshotEvents()` → `events`)+ 每会话接入留痕;日配额改为**只统计真实写卡**(此前「不值得保存」的判定也照样扣配额,40 次即耗尽 24h 配额)、`captureCooldownMs` 冷却真正生效、单次沉淀输入截尾 20000 字;新增**自动沉淀日志**面板与**异常主动提示**(页面红条 + 注入 Agent 上下文 + 轮次收尾停滞巡检);移除「每日回顾」自动简报(定时器 / `/todayBrief` 接口 / UI 按钮) |
|
|
616
|
-
| **v0.8.0** | 2026-09-06 | **全套 UI i18n**:跟随 DSH 系统语言实时切换(中/英),覆盖知识卡 / 知识图谱 / 审核中心 / 配置面板 / 新建卡模板;Windows 测试收尾 SQLite 连接 EBUSY 修复 |
|
|
617
|
-
| **v0.3.1** | 2026-09-03 | 知识图谱按需加载(点击时 fetch,关闭时注销) |
|
|
618
|
-
| **v0.3.0** | 2026-09-03 | **SQLite 化存储层**(`node:sqlite` 零依赖)+ 数据库层审核守卫 `enforceAudit()` + `audit_log` 审计日志 + 自动从 .md 迁移 |
|
|
619
|
-
|
|
620
|
-
</details>
|
|
621
|
-
| v0.2.0 | 2026-09-03 | recall 含正文修复(`search()` 返回 excerpt 字段) |
|
|
622
|
-
| v0.1.2 | 2026-09-03 | 审核守卫加固(`writeCard` 默认 `pending`,`mergeCards` 走审核) |
|
|
623
|
-
| v0.1.1 | 2026-09-03 | recall 含正文修复(excerpt 字段 + body 逻辑修正) |
|
|
624
|
-
| v0.1.0 | 2026-08-31 | 首发:自动沉淀 + 自动召回 + 图形化知识库 + 知识图谱 + 审核中心 + 回收中心 |
|
|
625
|
-
|
|
626
|
-
</details>
|
|
627
|
-
|
|
628
|
-
---
|
|
629
|
-
|
|
630
|
-
## 💬 交流群 & 打赏
|
|
631
|
-
|
|
632
|
-
**遇到问题、想提需求、或者想聊聊 DSH 插件开发 —— 欢迎进群:**
|
|
633
|
-
|
|
634
|
-
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/group-qr.jpg" width="260" alt="DeepSeek Harness 交流群二维码" />
|
|
635
|
-
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/group-wechat.png" width="260" alt="DeepSeek Harness 交流V群二维码" />
|
|
636
|
-
|
|
637
|
-
**如果这个插件帮你省了时间,可以请作者喝杯咖啡(两个码任选其一):**
|
|
638
|
-
|
|
639
|
-
| 微信收款码 | 微信赞赏码 |
|
|
640
|
-
|---|---|
|
|
641
|
-
| <img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/wechat-pay.jpg" width="230" alt="微信收款码" /> | <img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/wechat-reward.jpg" width="230" alt="微信赞赏码" /> |
|
|
642
|
-
|
|
643
|
-
> 打赏完全自愿,不影响任何功能;插件本体始终开源免费(MIT)。
|
|
644
|
-
> 仓库页的 **Sponsor** 按钮下拉里选 custom 第一项,会落到 [SPONSOR.md](SPONSOR.md)(同样是这一页的码)。
|
|
645
|
-
|
|
646
|
-
---
|
|
647
|
-
|
|
648
|
-
## 📄 License
|
|
649
|
-
|
|
650
|
-
MIT
|
|
651
|
-
|
|
652
|
-
---
|
|
653
|
-
|
|
654
|
-
> **让 AI 真正记住你:对话自动沉淀,知识随手可查。** ⭐ 觉得有用就点个 Star,Let's make AI not forget.
|
|
655
|
-
>
|
|
656
|
-
> English README: [README.en.md](README.en.md)
|
|
1
|
+
# 🧠 memory-eternal — a second brain for your AI
|
|
2
|
+
|
|
3
|
+
[](https://github.com/EternalNight996/memory-eternal/stargazers)
|
|
4
|
+
[](https://www.npmjs.com/package/memory-eternal)
|
|
5
|
+
[](https://github.com/EternalNight996/memory-eternal/releases)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**English (default)** · [简体中文](README.zh-CN.md)
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<a href="https://eternalnight996.github.io/memory-eternal/demo/"><img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/demo.gif" width="900" alt="memory-eternal 20-second tour: review queue, token ledger, knowledge graph" /></a>
|
|
12
|
+
</p>
|
|
13
|
+
<p align="center"><sub>▲ 20-second tour — review queue · token ledger · knowledge graph · <a href="https://eternalnight996.github.io/memory-eternal/demo/">live demo</a></sub></p>
|
|
14
|
+
|
|
15
|
+
> **Your AI forgets every session. Give it a memory it can trust.**
|
|
16
|
+
>
|
|
17
|
+
> Local-first, review-gated, measurable — one SQLite vault shared by DSH, Claude Code, Codex, Cursor and any MCP client.
|
|
18
|
+
|
|
19
|
+
- 🔍 **Review before it counts** — new cards wait in a review queue; approve or reject from the Web UI or terminal; every change is written to an immutable audit log.
|
|
20
|
+
- 🧾 **Token ledger** — see how much recall injected, how many distillation calls ran, and how often recall was actually useful.
|
|
21
|
+
- 🏠 **Local-first, no cloud** — SQLite + Markdown on your own disk. No third-party memory framework, no account, no outbound calls.
|
|
22
|
+
|
|
23
|
+
<p align="center"><strong>⭐ If it saves you from explaining the same context again, a star helps a lot.</strong><br/><sub>DSH one-liner: <code>dsh plugin --profile desktop add memory-eternal</code></sub></p>
|
|
24
|
+
|
|
25
|
+
## ⚡ Quick start
|
|
26
|
+
|
|
27
|
+
**DeepSeek Harness (desktop / web)**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
dsh plugin --profile desktop add memory-eternal
|
|
31
|
+
# reopen DSH → sidebar "Memory"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Claude Code, Codex or Cursor — same vault via MCP**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm i -g memory-eternal
|
|
38
|
+
dsh-memory connect claude # or: codex / cursor
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Any MCP client**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx -y -p memory-eternal dsh-memory mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Browser or terminal**
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
dsh-memory open # http://127.0.0.1:7999
|
|
51
|
+
npx -y -p memory-eternal dsh-memory tui # live queue in the terminal
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 🧾 Does it actually save tokens?
|
|
55
|
+
|
|
56
|
+
Most memory products <em>say</em> they save tokens. We think you should check. The plugin ships a built-in **memory ledger** (Runtime status → Memory ledger) that records:
|
|
57
|
+
|
|
58
|
+
| Recorded | What it means |
|
|
59
|
+
|---|---|
|
|
60
|
+
| recall: hits + injected characters | how much memory actually entered the context |
|
|
61
|
+
| capture: LLM calls + input/output characters | what distillation cost you |
|
|
62
|
+
| usefulness: useful / not-useful feedback | whether recalled memories actually helped |
|
|
63
|
+
|
|
64
|
+
The honest math:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
net benefit ≈ avoided context cost − (injected characters + distillation calls × price)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
| Scenario | You pay | You avoid |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| Multi-session project with decisions and pitfalls | ~0.6–0.9k tokens per recall | re-explaining context (~1.5–3k) or re-reading files (3–6k) |
|
|
73
|
+
| One-off question, low hit rate | recall injection | usually nothing |
|
|
74
|
+
|
|
75
|
+
> Illustrative numbers, not a benchmark — measure your own with the ledger. If recall usefulness stays below ~60%, tune it down (fewer results, shorter summaries) or turn memory off.
|
|
76
|
+
|
|
77
|
+
## 🛡️ Review first, prove later
|
|
78
|
+
|
|
79
|
+
A memory system that silently ingests everything will eventually recall a wrong fact as truth. memory-eternal defaults to **review-before-commit**:
|
|
80
|
+
|
|
81
|
+
- new cards land in a **pending queue**; nothing enters the main vault until a human approves it;
|
|
82
|
+
- approve, reject or bulk-process from the Web UI, the terminal TUI, or the CLI;
|
|
83
|
+
- every status change is appended to an **immutable audit log** (who / what / when / why) — enforced at the SQLite layer, not by convention;
|
|
84
|
+
- agents can read and propose, but **cannot approve their own cards**;
|
|
85
|
+
- deleted cards go to a recycle bin (30 days by default).
|
|
86
|
+
|
|
87
|
+
## 🧩 One vault, every agent
|
|
88
|
+
|
|
89
|
+
| Entry | What you get |
|
|
90
|
+
|---|---|
|
|
91
|
+
| **DSH desktop / web** | embedded memory UI: review, graph, config, ledger |
|
|
92
|
+
| **Claude Code / Codex / Cursor** | MCP server, same local vault, <code>dsh-memory connect</code> |
|
|
93
|
+
| **Any MCP client** | <code>npx -y -p memory-eternal dsh-memory mcp</code> |
|
|
94
|
+
| **Terminal** | <code>dsh-memory tui</code> — live queue, select-all, batch approve/reject, long-form reader |
|
|
95
|
+
| **Browser** | <code>dsh-memory open</code> → http://127.0.0.1:7999 |
|
|
96
|
+
|
|
97
|
+
## 📸 Screenshots
|
|
98
|
+
|
|
99
|
+
<p align="center">
|
|
100
|
+
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/graph-en.png" width="32%" alt="Knowledge graph: clusters by kind, tag edges" />
|
|
101
|
+
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/audit-en.png" width="32%" alt="Review inbox: type bar, queue and verdict pane" />
|
|
102
|
+
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/status-en.png" width="32%" alt="Runtime status: database, backups, capture, version" />
|
|
103
|
+
</p>
|
|
104
|
+
<p align="center"><sub>▲ Knowledge graph · review inbox · runtime status</sub></p>
|
|
105
|
+
|
|
106
|
+
<p align="center">
|
|
107
|
+
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/tui-audit.png" width="49%" alt="Native terminal audit: select-all, batch approve/reject" />
|
|
108
|
+
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/tui-reader.png" width="49%" alt="Native terminal long-form reader" />
|
|
109
|
+
</p>
|
|
110
|
+
<p align="center"><sub>▲ Terminal-native review and long-form reading — <code>dsh-memory tui</code></sub></p>
|
|
111
|
+
|
|
112
|
+
## 📚 Documentation & links
|
|
113
|
+
|
|
114
|
+
- [Full manual (English)](docs/GUIDE.md) · [完整手册(中文)](docs/GUIDE.zh-CN.md)
|
|
115
|
+
- [Private deployment (Docker / systemd)](docs/DEPLOY.md)
|
|
116
|
+
- [Changelog](docs/CHANGELOG.md) · [Architecture](docs/ARCHITECTURE.md)
|
|
117
|
+
- [Terminal plugin (dsh-tui)](dsh-tui-plugin/README.md) · [Orca launcher](orca-plugin/README.md) · [Cursor / VS Code extension](cursor-extension/README.md)
|
|
118
|
+
- [Report a bug](https://github.com/EternalNight996/memory-eternal/issues)
|
|
119
|
+
|
|
120
|
+
## 💬 Community
|
|
121
|
+
|
|
122
|
+
**Questions, ideas, or DSH plugin talk — join the groups:**
|
|
123
|
+
|
|
124
|
+
| QQ group | WeChat group |
|
|
125
|
+
|:---:|:---:|
|
|
126
|
+
| <img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/group-qr.jpg" width="240" alt="QQ group QR" /> | <img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/group-wechat.png" width="240" alt="WeChat group QR" /> |
|
|
127
|
+
|
|
128
|
+
**Buy the author a coffee (optional):**
|
|
129
|
+
|
|
130
|
+
| WeChat Pay | WeChat Reward |
|
|
131
|
+
|:---:|:---:|
|
|
132
|
+
| <img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/wechat-pay.jpg" width="200" alt="WeChat Pay QR" /> | <img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/support/wechat-reward.jpg" width="200" alt="WeChat Reward QR" /> |
|
|
133
|
+
|
|
134
|
+
> Sponsorship is optional; the plugin stays MIT and free. See [SPONSOR.md](SPONSOR.md).
|
|
135
|
+
|
|
136
|
+
## 📄 License
|
|
137
|
+
|
|
138
|
+
MIT — use it, self-host it, build on it.
|