memory-eternal 0.9.23 → 0.10.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 +11 -2
- package/README.md +20 -2
- package/examples/mcp-memory/memory-eternal.cordis.yml +59 -0
- package/index.js +11 -3
- package/lib/api.js +558 -536
- package/lib/capture-run.js +134 -126
- package/lib/capture.js +166 -7
- package/lib/client.js +17 -14
- package/lib/config-sync.js +5 -4
- package/lib/db.js +150 -3
- package/lib/vault.js +325 -101
- package/lib/web.js +3 -3
- package/package.json +3 -2
- package/web/app.js +20 -17
package/README.en.md
CHANGED
|
@@ -306,14 +306,21 @@ Diagnostics:
|
|
|
306
306
|
|
|
307
307
|
## 📋 Changelog
|
|
308
308
|
|
|
309
|
-
> **Withdrawn versions**: **v0.9.14 is deprecated** (its client-rendering changes hurt the feel and were reverted; its server-side algorithm work shipped in **v0.9.15**, whose rendering is identical to v0.9.13). **v0.9.0 – v0.9.5 are all deprecated on npm, and the release tags for v0.9.1 / v0.9.2 / v0.9.3 / v0.9.4 have been removed from git** (v0.9.5 is merely superseded, so its tag stays) — v0.9.1/v0.9.2 ship the blank-memory-page defect, v0.9.0 false-alarms its capture warnings, v0.9.3 does not support DSH v0.1.7-rc.2 (the plugin stops mounting entirely: the settings service changed and the client-side `settingsScope` is gone), v0.9.4 crashes under the official desktop build (schemastery 3.18.4 volatile live references) with `cfg.vaultDir.trim is not a function`, and **v0.9.5's full-screen overlay covers the desktop shell's window controls (its top-right "×" sits on the window Close button — one click quits the whole shell)**. Use **v0.9.6+** (`npm i memory-eternal@latest`).
|
|
310
|
-
|
|
309
|
+
> **Withdrawn versions**: **v0.9.16 – v0.9.22 are all withdrawn** (problem-period releases: git tags deleted, npm entries deprecated — use **v0.9.23**). **v0.9.14 is deprecated** (its client-rendering changes hurt the feel and were reverted; its server-side algorithm work shipped in **v0.9.15**, whose rendering is identical to v0.9.13). **v0.9.0 – v0.9.5 are all deprecated on npm, and the release tags for v0.9.1 / v0.9.2 / v0.9.3 / v0.9.4 have been removed from git** (v0.9.5 is merely superseded, so its tag stays) — v0.9.1/v0.9.2 ship the blank-memory-page defect, v0.9.0 false-alarms its capture warnings, v0.9.3 does not support DSH v0.1.7-rc.2 (the plugin stops mounting entirely: the settings service changed and the client-side `settingsScope` is gone), v0.9.4 crashes under the official desktop build (schemastery 3.18.4 volatile live references) with `cfg.vaultDir.trim is not a function`, and **v0.9.5's full-screen overlay covers the desktop shell's window controls (its top-right "×" sits on the window Close button — one click quits the whole shell)**. Use **v0.9.6+** (`npm i memory-eternal@latest`).
|
|
311
310
|
> **DSH compatibility**: `>=0.1.5-alpha.2 <0.2.0`. **v0.9.4 adds support for DSH v0.1.7-rc.2**, which replaced the settings service with a forms-only API and removed `@deepseek-ai/dsh-client-runtime` / `settingsScope`; **v0.9.5 adds support for schemastery ≥3.18.4 volatile live references** (the shape the official desktop profile uses, while the web profile still ships 3.18.1 — both work); **v0.9.6 keeps full-screen overlays clear of the desktop shell's window controls**. The 0.1.5 line still uses the legacy `settings.register` path; both paths are kept.
|
|
312
311
|
|
|
313
312
|
| Version | Date | Highlights |
|
|
314
313
|
|---|---|---|
|
|
315
314
|
| **v0.9.23** | 2026-09-30 | **No more save flicker.** After a successful save the client immediately re-fetches `/config`, but the host volatile echo lags, so inputs visibly reverted to the old values and then jumped back. The client now keeps a **local overlay** for fields that were saved but not yet echoed (`src/client/config-merge.js`, pure function + 4 checks), dropping each key once the host echoes it, and following the host value afterwards; resetting the form clears the overlay. `npm test` now runs **139 checks** |
|
|
315
|
+
|
|
316
|
+
<details>
|
|
317
|
+
<summary>📜 Previous releases (click to expand)</summary>
|
|
318
|
+
|
|
319
|
+
| Version | Date | Highlights |
|
|
320
|
+
|---|---|---|
|
|
316
321
|
| **v0.9.22** | 2026-09-30 | **Near-instant config sync (SSE push + file watching).** ① A new SSE hub (`lib/sse.js`): both hosts (DSH-embedded and standalone web) expose `/memory-eternal/api/events`, so a config change is pushed to every open page and the client reloads automatically — **without overwriting what you are currently typing** (dirty flag). ② Standalone save → DSH apply now triggers via `fs.watch` in **milliseconds** instead of a 5-second poll (the poll remains as a fallback). ③ The standalone page watches the shared config file, so a DSH-side save shows up there immediately. Adds `tests/sse.test.mjs` (5 checks); `npm test` now runs **135 checks** |
|
|
322
|
+
| **v0.9.21** | 2026-09-30 | **A successful save is no longer shown as a failure + config layout fix.** ① Success always uses the success style (the "written, host has not echoed yet" case used to pop up in the red failure style, which read as "save failed"), reworded to "saved to the config file; restart DSH to make it effective"; ② config field grid widened from 150/160px to **200/220px** with larger gaps and wrapping long labels, checkbox and text top-aligned — fields no longer crowd each other. |
|
|
323
|
+
| **v0.9.20** | 2026-09-30 | Standalone web page gains **range validation**: `/config` mirrors the Config schema min/max (`recycleRetentionDays` 1–3650, `webPort` 1–65535), returning **400** with "needs 1–3650, now 0" for out-of-range values. The most common save-failure path (cleared/out-of-range number) is now blocked with a field-level reason at all three layers: client, standalone page and host. |
|
|
317
324
|
| **v0.9.19** | 2026-09-30 | **"Save failed" now says why (root cause: clearing a number box yields 0, the schema rejects it, and the host answered with an empty 500).** Reproduced against the live host: out-of-range value / cleared number box / wrong type → **HTTP 500 with an empty body** → the client `r.json()` throws → only the generic "Save failed" is shown. Fixes: ① clearing a number input **no longer silently becomes 0** (an empty string coerced to a number is 0, while `recycleRetentionDays` requires `min(1)`); ② pre-save **per-field range validation** that names the field ("recycleRetentionDays must be a number between 1 and 3650, now 0"); ③ defensive response parsing: on a non-JSON or empty body the panel shows the HTTP status and the raw snippet instead of "Save failed"; ④ the host POST is wrapped so it **never returns an empty body**, and returns 400 plus the field names for empty numeric values; ⑤ the standalone `/config` validates types and ranges so invalid pending values cannot be written; ⑥ `drainPendingConfig` counts failures and gives up (deleting the file) after 5 tries instead of retrying forever. `npm test` now runs **130 checks** |
|
|
318
325
|
| **v0.9.18** | 2026-09-30 | **The standalone web page can finally save config — and a read-only page no longer feels dead.** Root cause (the other half of issue #12): the standalone web server has **no DSH settings service**, so its `/config` always returned `writable:false`; the client `save()` starts with `if (readonly) return` and the button was `disabled` — clicking Save did **nothing at all**, independent of the plugin version. Fixes: ① new shared-file protocol in `lib/config-sync.js`: the standalone page writes `memory-eternal-config.pending.json` atomically, and the **DSH host applies it on activation and every 5 seconds** via `settings.update(patch, undefined)` (skipping the optimistic-concurrency check), deleting the file on success and retrying on failure; if DSH is not running the change waits in the file for the next start. ② The standalone `/config` now reports `writable:true, viaPending:true` and the page shows a blue note explaining the sync. ③ **The button is no longer disabled when read-only** — clicking always explains why (`🔒 Read-only` + "desktop app → Settings → Memory"), so "no reaction" can no longer happen. Adds `tests/config-sync.test.mjs` (4 checks). `npm test` now runs **125 checks** |
|
|
319
326
|
| **v0.9.17** | 2026-09-30 | **"I updated it but the UI is still old" — fixed for good.** ① The build now injects the plugin version into the client bundle and the settings page shows a "**Page script vX**" chip; ② when the page-script version differs from the on-disk version it says so loudly: "⚠ This page is still running an older script (page v{page} / disk v{disk}): **press Ctrl+F5**"; the running-vs-disk hint now also leads with "refresh first, restart the desktop app / DSH only if the host half is old". Root cause of issue #12 ("saving shows no feedback"): the page was still executing the JS loaded before the update — the host sends `Cache-Control: no-store`, but **an already-open page never reloads its scripts**, so without a refresh you keep seeing the old UI. The standalone web service on `:7999` (an old process started 9/29) was restarted and now reports `loaded=onDisk=latest` |
|
|
@@ -341,6 +348,8 @@ Diagnostics:
|
|
|
341
348
|
| v0.1.1 | 2026-09-03 | Recall body fix (`excerpt` field + body logic) |
|
|
342
349
|
| v0.1.0 | 2026-08-31 | First release: auto capture + auto recall + graphical knowledge base + knowledge graph + audit center + recycle bin |
|
|
343
350
|
|
|
351
|
+
</details>
|
|
352
|
+
|
|
344
353
|
---
|
|
345
354
|
|
|
346
355
|
## 💬 Community & Support
|
package/README.md
CHANGED
|
@@ -232,6 +232,14 @@ dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
|
|
|
232
232
|
|
|
233
233
|
> **v0.3.0 起:审核守卫下沉到数据库层。** 所有写入路径(自动沉淀 / MCP / UI 新建 / 导入)强制经过 `enforceAudit()`,调用方无法绕过。`setCardStatus()` 是唯一审核操作入口,每次变更写入 `audit_log` 审计日志(who/what/when/why),不可篡改。
|
|
234
234
|
|
|
235
|
+
> **v0.10.0 起:升级为「两套存储」——未审核内容物理隔离。**
|
|
236
|
+
> `cards`(**主库 / 正常区**)物理上**只存 approved**;`quarantine`(**隔离区 / 异常区**)存 `pending` / `rejected` / `deleted`。
|
|
237
|
+
> 意义:召回、检索、知识图谱、去重池、导出这些读路径**查主库即安全**——不再依赖「每处 SQL 都记得写 `WHERE status='approved'`」。
|
|
238
|
+
> (v0.9 审计实测漏过三处:去重池会把新知识追加进待审卡、`readCard` 可按 path 直读未审核正文、`/card` 接口无状态校验。)
|
|
239
|
+
> 审核流转 = **跨表搬家**:批准 → 搬进主库,驳回/软删 → 搬进隔离区;搬家在一个事务里完成,更新记录与审计日志跟着走。
|
|
240
|
+
> 升级说明:首次启动会自动把存量非 approved 卡搬进隔离区(**不删除任何内容**,都在同一个 `.db` 文件里)。
|
|
241
|
+
> 若回滚到旧版本,审核队列会显示为空(旧代码看不到隔离区表),升级回来即恢复。
|
|
242
|
+
|
|
235
243
|
<p align="center">
|
|
236
244
|
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/audit-center.png" width="880" alt="审核中心" />
|
|
237
245
|
</p>
|
|
@@ -330,14 +338,22 @@ curl http://127.0.0.1:7999/memory-eternal/api/web-info # 独立 web 是否活
|
|
|
330
338
|
|
|
331
339
|
## 📋 更新日志
|
|
332
340
|
|
|
333
|
-
> **版本撤回说明**:**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`)。
|
|
334
|
-
|
|
341
|
+
> **版本撤回说明**:**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`)。
|
|
335
342
|
> **DSH 兼容性**:`>=0.1.5-alpha.2 <0.2.0`。**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` 路径,两条路径都保留。
|
|
336
343
|
|
|
337
344
|
| 版本 | 日期 | 关键改动 |
|
|
338
345
|
|---|---|---|
|
|
346
|
+
| **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` 父目录 → 改监听文件) |
|
|
339
347
|
| **v0.9.23** | 2026-09-30 | **保存不再闪烁**。保存成功后客户端会立刻重拉 `/config`,而宿主 volatile 回流是滞后的 → 输入框会「先退回原配置、再跳回修改后的值」。现在客户端把「已保存但宿主未回显」的字段做**本地叠加**(新增 `src/client/config-merge.js` 纯函数 + 4 项单测),宿主回显一致后自动摘除,之后仍跟随宿主权威值;重置表单会清空叠加层。`npm test` 共 **139 项** |
|
|
348
|
+
|
|
349
|
+
<details>
|
|
350
|
+
<summary>📜 历史版本(点击展开)</summary>
|
|
351
|
+
|
|
352
|
+
| 版本 | 日期 | 关键改动 |
|
|
353
|
+
|---|---|---|
|
|
340
354
|
| **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 项** |
|
|
355
|
+
| **v0.9.21** | 2026-09-30 | **保存成功不再被误读为失败 + 配置页排版修复**。① 保存成功一律用**成功样式**提示(原来「已写入;宿主尚未回流」被弹成红色失败样式,用户以为保存失败),措辞改为「已写入配置文件;宿主仍是旧版,重启 DSH 后即以新值为准」;② 配置页字段网格列宽 150/160px → **200/220px**、间距加大、长中文标签允许换行,勾选框与说明文字顶端对齐,字段不再互相挤压。 |
|
|
356
|
+
| **v0.9.20** | 2026-09-30 | 独立 Web 页补**取值范围校验**:`/config` 镜像 Config schema 的 min/max(如 `recycleRetentionDays` 1–3650、`webPort` 1–65535),越界直接 **400** 并说明「需要 1–3650,当前 0」。至此「数字框清空 / 越界」这条最常见的保存失败链路在**客户端 + 独立页 + 宿主**三层都被拦住并给出字段级原因。 |
|
|
341
357
|
| **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 项** |
|
|
342
358
|
| **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 项** |
|
|
343
359
|
| **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` |
|
|
@@ -365,6 +381,8 @@ curl http://127.0.0.1:7999/memory-eternal/api/web-info # 独立 web 是否活
|
|
|
365
381
|
| v0.1.1 | 2026-09-03 | recall 含正文修复(excerpt 字段 + body 逻辑修正) |
|
|
366
382
|
| v0.1.0 | 2026-08-31 | 首发:自动沉淀 + 自动召回 + 图形化知识库 + 知识图谱 + 审核中心 + 回收中心 |
|
|
367
383
|
|
|
384
|
+
</details>
|
|
385
|
+
|
|
368
386
|
---
|
|
369
387
|
|
|
370
388
|
## 💬 交流群 & 打赏
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 记忆核心(memory-eternal)· MCP 对外通道示例 overlay
|
|
2
|
+
#
|
|
3
|
+
# 用途:把 memory-eternal 的 MCP stdio 服务挂进 DSH —— 让 **DSH 自己**也能通过官方
|
|
4
|
+
# `@deepseek-ai/dsh-mcp-client` 使用记忆库(与 Claude Code / Codex / Cursor 走同一条协议路径)。
|
|
5
|
+
#
|
|
6
|
+
# 什么时候需要它:
|
|
7
|
+
# · 你没装 memory-eternal 插件本体,只想用它的记忆库 → 用这个 overlay;
|
|
8
|
+
# · 你已经装了插件(推荐):插件是「一等公民」,这条 MCP 通道**不需要**,因为插件
|
|
9
|
+
# 自带 memory_recall 工具与系统提示段,还能拿到会话事件做自动沉淀。
|
|
10
|
+
#
|
|
11
|
+
# 用法(示例文件不在交付包里自动生效,必须显式 --patch 或合并进用户 patch 层):
|
|
12
|
+
# dsh web --patch "<仓库>/examples/mcp-memory/memory-eternal.cordis.yml"
|
|
13
|
+
# 或把下面的 insert 合并进 $DSH_HOME/cordis.patch.yml(本机所有 profile 生效)
|
|
14
|
+
# 或 $DSH_HOME/profiles/<name>/cordis.patch.yml(只对该 profile 生效)
|
|
15
|
+
#
|
|
16
|
+
# 前置条件(二进制已随包提供,无需额外安装):
|
|
17
|
+
# · Node 22+(memory-eternal engines 要求)
|
|
18
|
+
# · 全局安装或 profile 内安装 memory-eternal(提供 bin/dsh-memory.mjs)
|
|
19
|
+
# 全局:npm i -g memory-eternal
|
|
20
|
+
# profile 内:cd ~/.dsh/profiles/<name> && pnpm add memory-eternal
|
|
21
|
+
# 下面的 args 用的是「profile 内安装」路径;全局安装请改成
|
|
22
|
+
# args: ['dsh-memory', 'mcp'](Windows 下为 dsh-memory.cmd)。
|
|
23
|
+
|
|
24
|
+
- insert:
|
|
25
|
+
- id: memory-eternal-mcp
|
|
26
|
+
name: '@deepseek-ai/dsh-mcp-client'
|
|
27
|
+
config:
|
|
28
|
+
serverName: eternal
|
|
29
|
+
transport: stdio
|
|
30
|
+
command: node
|
|
31
|
+
args:
|
|
32
|
+
# 如果是全局安装,改成 ['dsh-memory', 'mcp']
|
|
33
|
+
- 'C:/Users/<你>/.dsh/profiles/web/node_modules/memory-eternal/bin/dsh-memory.mjs'
|
|
34
|
+
- 'mcp'
|
|
35
|
+
# 注意:stdio 桥接会主动**剥离名字像凭据的环境变量以及所有 DSH_* 变量**,其余继承。
|
|
36
|
+
# 因此:
|
|
37
|
+
# · 想让 MCP 进程用哪个库,显式在这里写 MEMORY_VAULT_DIR(不要依赖继承);
|
|
38
|
+
# · 想让 MCP 的 memory_capture 具备「LLM 蒸馏」能力,需要在这里显式传 MEMORY_LLM_*
|
|
39
|
+
# (这类变量名字含 KEY/TOKEN,很可能被桥接当凭据剥掉);
|
|
40
|
+
# · 不传就是「原文卡」降级模式,仍然可用,只是不压缩。
|
|
41
|
+
env:
|
|
42
|
+
# 留空 = 用插件自己的解析顺序(共享配置 memory-eternal-config.json → 默认 ~/.dsh/memory-vault)
|
|
43
|
+
MEMORY_VAULT_DIR: ''
|
|
44
|
+
# 可选:MCP 侧蒸馏用的 OpenAI 兼容端点
|
|
45
|
+
# MEMORY_LLM_BASE_URL: 'https://api.deepseek.com'
|
|
46
|
+
# MEMORY_LLM_MODEL: 'deepseek-chat'
|
|
47
|
+
# MEMORY_LLM_API_KEY: '<在此填 key>'
|
|
48
|
+
cwd: !!js process.cwd()
|
|
49
|
+
|
|
50
|
+
# 挂上之后 DSH 侧会看到这些工具(前缀由 serverName 决定):
|
|
51
|
+
# mcp__eternal__memory_recall —— 检索记忆(CJK 整词 + 字符 bigram,只读主库/approved)
|
|
52
|
+
# mcp__eternal__memory_capture —— 手动沉淀一段内容(走审核闸门,默认落隔离区 pending)
|
|
53
|
+
# mcp__eternal__memory_stats —— 记忆库概览
|
|
54
|
+
#
|
|
55
|
+
# 验证(与官方文档同一套三步骤):
|
|
56
|
+
# 1) 会话 A:让模型调用 mcp__eternal__memory_capture 写入一个唯一值;
|
|
57
|
+
# 2) 新建会话 B(不必重启宿主):问「我的验证饮料是什么?查记忆」→ 应调用 memory_recall 并返回该值;
|
|
58
|
+
# 3) 继续在 B 里用该偏好做一次推断。
|
|
59
|
+
# 本仓库 tests/e2e-recall.test.mjs 是同一套步骤的自动化版本(走 vault API)。
|
package/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import { migrateFromMarkdown, setAuditConfig, backupDb } from './lib/db.js'
|
|
|
30
30
|
import { evaluateStall, MAX_STAMPS } from './lib/stall.js'
|
|
31
31
|
import { resolveVaultDir, currentWorkspace } from './lib/vault-resolve.js'
|
|
32
32
|
import { createHub } from './lib/sse.js'
|
|
33
|
-
import { summarizeTurn, summarizeTurnDetailed, routeCandidates, extractLastTurn, sliceNewEvents, sessionEvents, sessionEventApi, createCaptureHealth, resolveRoute, captureCard, captureUpdate, pickNeighbors } from './lib/capture.js'
|
|
33
|
+
import { summarizeTurn, summarizeTurnDetailed, routeCandidates, extractLastTurn, sliceNewEvents, sessionEvents, sessionEventApi, createCaptureHealth, resolveRoute, captureCard, captureUpdate, pickNeighbors, hasUsableContent, deriveTitle, looksTruncated } from './lib/capture.js'
|
|
34
34
|
import { createApi, json, encodeBody } from './lib/api.js'
|
|
35
35
|
import { appendCaptureLog, readCaptureLog, rotateCaptureLog } from './lib/capture-log.js'
|
|
36
36
|
import { nodeBinary, childEnv } from './lib/node-bin.js'
|
|
@@ -435,6 +435,12 @@ export function apply(ctx, config) {
|
|
|
435
435
|
const raw = extractLastTurn(events)
|
|
436
436
|
const text = raw.length > CAPTURE_TEXT_MAX ? raw.slice(-CAPTURE_TEXT_MAX) : raw
|
|
437
437
|
logCapture(sessionId, 'listen', `会话事件 ${events.length} 条 → 有效对话 ${text.length} 字${raw.length > text.length ? '(超长已截尾)' : ''}`, { preview: preview(text) })
|
|
438
|
+
// 噪声闸门(P0):extractLastTurn 已剥掉运行时注入(环境快照 / team 广播 / 工具说明),
|
|
439
|
+
// 这里再判一次「剥完还有没有真实内容」——只有注入残留的轮次不进 LLM、不写卡。
|
|
440
|
+
if (!hasUsableContent(text)) {
|
|
441
|
+
logCapture(sessionId, 'skip', `有效内容不足:剥离运行时注入后只剩 ${text.trim().length} 字(多为环境快照 / 广播 / 工具说明)`)
|
|
442
|
+
return
|
|
443
|
+
}
|
|
438
444
|
if (text.length < (cfg.captureMinChars ?? 200)) {
|
|
439
445
|
logCapture(sessionId, 'skip', `内容太短:${text.length} < ${cfg.captureMinChars ?? 200} 字(调小「捕获最小长度」可放宽)`)
|
|
440
446
|
return
|
|
@@ -455,9 +461,10 @@ export function apply(ctx, config) {
|
|
|
455
461
|
// 成本控制:distillEnabled=false 时不调 LLM,直接存原文卡(零蒸馏成本)
|
|
456
462
|
const source = DSH_AGENT
|
|
457
463
|
if (cfg.distillEnabled === false || !llm) {
|
|
464
|
+
const rawTitle = deriveTitle(text)
|
|
458
465
|
const out = await captureCard(vaultDir(), {
|
|
459
466
|
kind: 'content',
|
|
460
|
-
title:
|
|
467
|
+
title: rawTitle.length >= 6 && !looksTruncated(rawTitle) ? rawTitle : '对话原文记录',
|
|
461
468
|
tags: ['raw'],
|
|
462
469
|
body: text,
|
|
463
470
|
source,
|
|
@@ -497,9 +504,10 @@ export function apply(ctx, config) {
|
|
|
497
504
|
if (failure) { health.fail(`蒸馏失败(${failure.provider}):${failure.code} ${failure.message}`); touchPrompt() }
|
|
498
505
|
if (!result) {
|
|
499
506
|
// 蒸馏失败不能让内容白丢:退成原文卡(与「关闭蒸馏」同一条降级路径)。
|
|
507
|
+
const rawTitle = deriveTitle(text)
|
|
500
508
|
const raw = await captureCard(vaultDir(), {
|
|
501
509
|
kind: 'content',
|
|
502
|
-
title:
|
|
510
|
+
title: rawTitle.length >= 6 && !looksTruncated(rawTitle) ? rawTitle : '对话原文记录',
|
|
503
511
|
tags: ['raw'],
|
|
504
512
|
body: text,
|
|
505
513
|
source,
|