@modusensus/dsh-mneme 0.9.0 → 0.9.2

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.
Files changed (106) hide show
  1. package/README.md +82 -19
  2. package/bin/cli.mjs +69 -9
  3. package/docs/CONFIGURATION.md +48 -0
  4. package/docs/SEMANTIC.md +3 -3
  5. package/lib/api-standalone.js +78 -12
  6. package/lib/api.js +339 -59
  7. package/lib/candidates.js +454 -454
  8. package/lib/client.js +669 -256
  9. package/lib/config.js +50 -2
  10. package/lib/dream/sleep.js +35 -12
  11. package/lib/dream.js +86 -14
  12. package/lib/embedding.js +153 -22
  13. package/lib/index.js +32 -0
  14. package/lib/inject.js +34 -3
  15. package/lib/lang.js +124 -0
  16. package/lib/pipeline-activities.js +178 -0
  17. package/lib/pipeline-attribution.js +165 -0
  18. package/lib/pipeline-command.js +279 -0
  19. package/lib/pipeline-confirm.js +142 -0
  20. package/lib/pipeline-exec.js +174 -0
  21. package/lib/pipeline-nudge.js +1281 -0
  22. package/lib/pipeline-observations.js +181 -0
  23. package/lib/pipeline-observe.js +133 -0
  24. package/lib/pipeline-runner.js +346 -0
  25. package/lib/pipeline-runs.js +442 -0
  26. package/lib/pipeline.js +128 -7
  27. package/lib/scope-state.js +277 -0
  28. package/lib/semantic.js +66 -10
  29. package/lib/service.js +80 -3
  30. package/lib/settings.js +139 -1
  31. package/lib/store.js +216 -3
  32. package/lib/summarize.js +2 -3
  33. package/lib/tools.js +71 -0
  34. package/package.json +3 -3
  35. package/scripts/changelog-prep.mjs +62 -7
  36. package/scripts/check-sync.js +8 -6
  37. package/scripts/release-docs.mjs +204 -0
  38. package/scripts/release-notes.mjs +162 -0
  39. package/src/api-standalone.js +78 -12
  40. package/src/api.js +339 -59
  41. package/src/candidates.js +454 -454
  42. package/src/config.js +50 -2
  43. package/src/dream/sleep.js +35 -12
  44. package/src/dream.js +86 -14
  45. package/src/embedding.js +153 -22
  46. package/src/index.js +32 -0
  47. package/src/inject.js +34 -3
  48. package/src/lang.js +124 -0
  49. package/src/pipeline-activities.js +178 -0
  50. package/src/pipeline-attribution.js +165 -0
  51. package/src/pipeline-command.js +279 -0
  52. package/src/pipeline-confirm.js +142 -0
  53. package/src/pipeline-exec.js +174 -0
  54. package/src/pipeline-nudge.js +1281 -0
  55. package/src/pipeline-observations.js +181 -0
  56. package/src/pipeline-observe.js +133 -0
  57. package/src/pipeline-runner.js +346 -0
  58. package/src/pipeline-runs.js +442 -0
  59. package/src/pipeline.js +128 -7
  60. package/src/scope-state.js +277 -0
  61. package/src/semantic.js +66 -10
  62. package/src/service.js +80 -3
  63. package/src/settings.js +139 -1
  64. package/src/store.js +216 -3
  65. package/src/summarize.js +2 -3
  66. package/src/tools.js +71 -0
  67. package/test/api-hardening.test.js +173 -0
  68. package/test/api.test.js +210 -1
  69. package/test/changelog-prep.test.js +74 -8
  70. package/test/cli-enums.test.js +37 -0
  71. package/test/cli-flag-value.test.js +86 -0
  72. package/test/client.test.js +216 -6
  73. package/test/distill-tx-embed-gap.test.js +110 -0
  74. package/test/docs-config-keys.test.js +65 -0
  75. package/test/dream-maxtokens-fallback.test.js +132 -0
  76. package/test/embed-text-reasons.test.js +112 -0
  77. package/test/helpers/write-admission-samples.js +6 -2
  78. package/test/lib-smoke.test.js +20 -0
  79. package/test/persona-api.test.js +172 -0
  80. package/test/persona-flag.test.js +36 -0
  81. package/test/persona-inject.test.js +145 -0
  82. package/test/persona-settings.test.js +145 -0
  83. package/test/pipeline-activities.test.js +155 -0
  84. package/test/pipeline-attribution.test.js +175 -0
  85. package/test/pipeline-command.test.js +243 -0
  86. package/test/pipeline-confirm.test.js +148 -0
  87. package/test/pipeline-exec.test.js +172 -0
  88. package/test/pipeline-nudge.test.js +1036 -0
  89. package/test/pipeline-observations.test.js +157 -0
  90. package/test/pipeline-observe.test.js +127 -0
  91. package/test/pipeline-r1r2.test.js +149 -0
  92. package/test/pipeline-runner.test.js +442 -0
  93. package/test/pipeline-runs.test.js +767 -0
  94. package/test/pipeline.test.js +137 -6
  95. package/test/readme-claims.test.js +68 -0
  96. package/test/reindex-backfill.test.js +83 -4
  97. package/test/release-docs.test.js +130 -0
  98. package/test/release-notes.test.js +240 -0
  99. package/test/release-workflow.test.js +16 -0
  100. package/test/route-policy.test.js +113 -0
  101. package/test/scope-state.test.js +481 -0
  102. package/test/security-doc.test.js +139 -0
  103. package/test/sensitive-scan.test.js +44 -0
  104. package/test/tools-dto-schema-matrix.test.js +12 -3
  105. package/test/tools.test.js +6 -6
  106. package/test/workflow-guards.test.js +156 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![npm version](https://img.shields.io/npm/v/@modusensus/dsh-mneme?color=blue&label=npm)](https://www.npmjs.com/package/@modusensus/dsh-mneme)
6
6
  [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
7
7
  [![Awesome](https://awesome-dsh-plugin.com/badge.svg)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
8
- [![tests](https://img.shields.io/badge/tests-1869%20passed-success)](https://github.com/slow-stack/mneme)
8
+ [![tests](https://img.shields.io/badge/tests-2230%20passed-success)](https://github.com/slow-stack/mneme)
9
9
  [![CI](https://img.shields.io/github/actions/workflow/status/slow-stack/mneme/ci.yml)](https://github.com/slow-stack/mneme/actions)
10
10
  [![node](https://img.shields.io/badge/node-22%2B-blue)](https://nodejs.org)
11
11
  [![npm downloads](https://img.shields.io/npm/d18m/@modusensus/dsh-mneme.svg?color=blue&label=downloads)](https://www.npmjs.com/package/@modusensus/dsh-mneme)
@@ -17,7 +17,7 @@
17
17
 
18
18
  ## ⏱️ 30 秒理解
19
19
 
20
- **一句话**:给 Agent 装上跨会话记忆——记住你、记住项目,并在后台自动整理,越用越懂你;**v0.8.0 起**记忆可按 agent 与工作区隔离(`scopeEnabled`),多 Agent / 多项目互不串台。
20
+ **一句话**:给 Agent 装上跨会话记忆——记住你、记住项目,并在后台自动整理,越用越懂你;**v0.8.0 起**记忆可按 agent 与工作区隔离(`scopeEnabled`),多 Agent / 多项目互不串台;**v0.9.0 起**「这件事怎么跑」也能落进项目仓库(M.O.,`actionPipelineEnabled`,默认关)。
21
21
 
22
22
  | ① 写入 | ② 存储 | ③ 进化 |
23
23
  |--------|--------|--------|
@@ -42,14 +42,14 @@ dsh web
42
42
 
43
43
  - **SQLite 主存储**:`~/.dsh/memory/memory.db`,`node:sqlite` 内置,零原生依赖
44
44
  - **Markdown 镜像**:按类型分文件(`preferences.md` / `projects.md` / `decisions.md` / `history.md` / `summary.md` / `patterns.md` / `pitfalls.md` / `constraints.md` / `rejected-solutions.md`,外加只读视图 `documents.md`),文件头是 YAML frontmatter(`type` / `generated.by` / `generated.at` / `covered` / `coverage` / `tags`)。人类可读,条目标题与正文可手工编辑(**人工修改优先**合并回库);`documents.md` 例外:它只有 document 指针行(id、标题、摘要首句、文件路径),不含正文也不参与人工回填。文件头与条目元数据行由机器维护
45
- - **9 种记忆类型**:`preference` 偏好 / `project` 项目 / `decision` 决策 / `history` 历史 / `summary` 会话总览 / `pattern` 模式 + 编码记忆三型 `rejected_solution` 被否方案 / `pitfall` 踩坑 / `constraint` 约束(v0.7.13 起,`codingRetrospect` 默认关;v0.7.11 近重写已收窄删除 user/fact 两型)
45
+ - **10 种记忆类型**:`preference` 偏好 / `project` 项目 / `decision` 决策 / `history` 历史 / `summary` 会话总览 / `pattern` 模式 + 编码记忆三型 `rejected_solution` 被否方案 / `pitfall` 踩坑 / `constraint` 约束(v0.7.13 起,`codingRetrospect` 默认关;v0.7.11 近重写已收窄删除 user/fact 两型)+ `document` 文档指针(#230,只存摘要与 `doc_path`,正文留在磁盘由 agent 自管)
46
46
  - **镜像同步状态机(v0.3.6+)**:mirror 与主库强一致,用 `generation`(期望轮次)/ `applied_generation`(已应用轮次)建模同步债务
47
47
  - 业务写操作在**自身事务内原子递增** desired generation——崩溃在 COMMIT 后、渲染前,重启也能凭 durable 债务恢复,绝不静默跳过(v0.3.8)
48
48
  - `generation` 用 SQLite 原子语句递增,多进程并发零丢失;带 `CHECK` 上界,负数/溢出拒绝
49
49
  - 逐 type 记录 `committed / failed / pending` 回执,健康端点区分 `ok / degraded / unknown`
50
50
  - 状态写失败不静默:同步失败落日志并留债务,重启自动收敛
51
51
 
52
- ### 模型工具(9 个)
52
+ ### 模型工具(10 个)
53
53
 
54
54
  | 工具 | 功能 |
55
55
  |------|------|
@@ -61,8 +61,26 @@ dsh web
61
61
  | `memory_delete` | 删除记忆(按记忆 ID 精确删除) |
62
62
  | `memory_forget` | 抑制注入(降权不删除,可恢复) |
63
63
  | `memory_archive` | 归档/恢复记忆(v0.2.5;归档后隐藏于列表/搜索/注入/整理,`archived=false` 可恢复) |
64
+ | `memory_register_document` | 登记 agent 产长文档的指针行(#230:摘要 + `doc_path` + evidence 落 `document` 类型;正文留在磁盘、由 agent 自管,注册口唯一,旁路写入被拒) |
64
65
  | `memory_runtime` | 本地推理运行时的状态 / 供给 / 校验(v0.7.32,自管运行时三档取件) |
65
66
 
67
+ ### 工作流(M.O. · 手法,v0.9 beta)
68
+
69
+ 记忆库答的是「这件事**为什么**这么办」,答不了「这件事**到底怎么跑**」。v0.9 把后者从记忆库里的一行文字变成项目里可提交、可 review、可被 CI 读的一份文件——一条工作流一个 JSON:
70
+
71
+ ```
72
+ <项目>/.mneme/pipelines/<id>.json ← 项目层(默认;跟着项目提交)
73
+ <插件包>/pipelines/<id>.json ← 全局层(兜底;今天包里没有这个目录,有意为空)
74
+ ```
75
+
76
+ - **两层来源,顺序即优先级**:同 id 两处都有,项目层赢,被挡住的那份进 `shadowed` 报出来(不静默丢弃);项目层的坏文件进 `unreadable` 而不拖死全局层——一层写坏不该让人连兜底那份也看不见
77
+ - **校验很硬**:`id` / `name` / `project` / `version` / `status` / `done` 与每一步的 `phase` / `title` / `when` / `do` / `done` / `why` 缺一不可,`risk` 限 R0–R3、`needs` 必须指向存在的步骤、图里有环直接报错——任何一处不合规**整条读不出来**,不做「跳过坏文件继续」:半个工作流比没有更危险
78
+ - **护栏,不是轨道**:绕行要留出口。自报 R0/R1 **且**不跨闸门 **且**兜底清单(`IRREVERSIBLE_PATTERNS`,机械匹配、不看自报)没命中 → 记账先走;其余 → 停下问人
79
+ - **候选分层**:草稿住 `<memoryDir>/candidates/<项目键>/`,只在本地。升格要同时满足「走过两次(至少一次成功)」**且**「写得出反事实」;够格之后系统只**提议**,`promoteCandidate()` 只返回内容不写盘,提交与推送是人握着的那道门
80
+ - **两个指标分开数**:跳过(这次不用这一步,≥3 次提议删**这一步**)与绕开(这条路整个不对,≥3 次提议重看**这条路径**)各自累加,混在一起两种提议都发不出来
81
+ - **只读路由** `GET /api/dsh-mneme/pipeline[?id=&project=]`(`project` 非绝对路径回 400 `project-not-absolute`,不回退 `process.cwd()`);答复契约 `mneme.pipeline/v1`,面板渲染与 agent 读的是同一份数据
82
+ - 默认关(`actionPipelineEnabled`),设计取舍见 [docs/WORKFLOWS.md](docs/WORKFLOWS.md)
83
+
66
84
  ### 自动注入 + 会话摘要
67
85
 
68
86
  - **自动注入**:新会话开局注入记忆摘要(`summary` 优先 + 少量高重要性条目)
@@ -146,7 +164,7 @@ dsh web
146
164
 
147
165
  ### 语义增强(Semantic)🧠
148
166
 
149
- v0.2 起新增**完全离线的语义记忆引擎**(本地模型 + 精排 + 聚类):
167
+ v0.2 起新增**可以完全离线运行的语义记忆引擎**(本地模型 + 精排 + 聚类):
150
168
 
151
169
  - **本地 Embedding**:三后端可选——ONNX(`Xenova/bge-small-zh-v1.5`,离线)/ Ollama / OpenAI 兼容,失败自动逐级降级,最差回退关键词搜索
152
170
  - **Rerank 精排**:`Xenova/bge-reranker-base` 对召回候选交叉编码精排,提升 Top-K 准确率
@@ -249,7 +267,14 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
249
267
 
250
268
  > 完整版本说明见 [CHANGELOG](CHANGELOG.md) 与各版本 [GitHub Release](https://github.com/slow-stack/mneme/releases);当前特性以本 README 正文与[配置表](#-配置)为准。早期版本中的实验性功能(Wiki-Link、tag 系统等)已在 v0.7.11 移除,详见 CHANGELOG 对应条目。
251
269
 
252
- 🧬 Gene → 🛡️ 审计加固 → 💤 Sleep Mode → 🕸️ 召回融合与图谱 → ✨ 面板体验 → 🌡️ 自进化记忆 → 🔐 作用域隔离 → 🌐 MCP 生态
270
+ 🧬 Gene → 🛡️ 审计加固 → 💤 Sleep Mode → 🕸️ 召回融合与图谱 → ✨ 面板体验 → 🌡️ 自进化记忆 → 🔐 作用域隔离 → 🌐 MCP 生态 → 🧭 M.O. 手法(项目级工作流,v0.9 beta)
271
+
272
+ | 版本 | 主题 |
273
+ |------|------|
274
+ | **v0.9.0**(2026-10-17) | M.O.(Modus Operandi · 手法)beta:工作流本体住 `<项目>/.mneme/pipelines/`、候选分层与升格判据、面板清单与图同屏(`actionPipelineEnabled` 默认关);设置面板折叠线挪到「新手之外」并按人话重写 22 个条目名(#427);安全与供应链加固(39 条路由声明式鉴权、OSV 有漏洞即失败、发布带 provenance) |
275
+ | **v0.9.1**(2026-10-18) | M.O. 从「看得见」跑到「跑得出账」:执行窗口由 `/mo-run start` / `/mo-run stop` 显式划开、运行账落 `<项目>/.mneme/pipelines/<id>.runs.jsonl`(账本坏了与从没跑过严格分开)、归属只出候选、观察旁挂与脱节检出、步骤 `assert` / `optional`、R1/R2 提议;沉淀链从真实会话日志挖 draft 工作流(判据恒 `null`,答案只能由人补);人格定义页 v1 → v1.2(按 agent 寻址多卡,默认关);面板、向量与检查更新的一圈修复(#448 / #450 / #460);Release 正文改分层渲染 |
276
+ | **v0.9.2**(2026-10-19) | 让「看不见的失败」显形:功能开关的第三态「待重启」把「面板显示已改 ≠ 运行时已改」摆到界面上(#481);作用域级可逆闸门让一路记忆能被单独按下与松开、不必改配置重启(#471,默认关);巩固用的 `dreamMaxTokens` 默认值撞服务商上限、八天 180 次全失败,面板现在能改、超上限还会自动降级重试(#478);CLI 把 `--` 开头的值当新旗标的真实缺陷(一次 1/4096 概率的 CI 红揪出来的)、人格卡删除 × 永远不可见、自动蒸馏写入的行 100% 拿不到向量(#477) |
277
+ | **v0.8.x** | 作用域隔离(agent / workspace 双维 + opt-in 硬过滤)、冲突队列人工裁决、生态化(stdio MCP server / 图召回轴 / 冷启动 / 注入截断) |
253
278
 
254
279
  ## 📦 安装
255
280
 
@@ -274,15 +299,44 @@ dsh web
274
299
 
275
300
  > 如需自定义配置(阈值、延迟等),可在 `~/.dsh/profiles/web/cordis.patch.yml` 中按 `id: dsh-mneme` 覆盖默认值(见下方配置表)。
276
301
 
277
- #### 方式二:从源码安装
302
+ #### 方式二:从源码安装(本地目录)
278
303
 
279
304
  ```bash
280
305
  git clone https://github.com/slow-stack/mneme.git
281
- cd dsh-mneme
282
- dsh plugin --profile web add .
306
+ cd mneme/dsh-mneme
307
+ dsh plugin --profile web add "$(pwd)"
283
308
  dsh web
284
309
  ```
285
310
 
311
+ > **路径必须绝对**:宿主对 `dsh plugin add` 的本地路径有一条硬校验(`parseInstallSpec` 里的 `a local path must be absolute`),相对路径(含 `.`、`..`)一律拒掉。上面用 `"$(pwd)"` 就地展开,就是为了不给它相对路径。
312
+
313
+ #### 方式三:直接从 Git 仓库安装
314
+
315
+ 本仓库是**单仓多包**:仓库根那份 `package.json` 是**仓库壳**(`private`、不带 `dsh` 字段),插件本体在 `dsh-mneme/`。所以 Git 地址必须带 `#path:` 指到子目录:
316
+
317
+ ```bash
318
+ dsh plugin --profile web add 'git+https://github.com/slow-stack/mneme.git#path:/dsh-mneme'
319
+ dsh web
320
+ ```
321
+
322
+ | 写法 | 结果 |
323
+ |------|------|
324
+ | `git+https://github.com/slow-stack/mneme.git#path:/dsh-mneme` | ✅ 装出 `@modusensus/dsh-mneme`(实测 0.9.0,38 个包) |
325
+ | `https://github.com/slow-stack/mneme#path:/dsh-mneme` | ✅ 同上;lockfile 会归一成 `github:slow-stack/mneme#path:/dsh-mneme` |
326
+ | `git+https://github.com/slow-stack/mneme.git` | ❌ 装到仓库根那份壳(`0.0.0`、无 `dsh` 字段),宿主不认它是插件 |
327
+
328
+ `#path:` 是 pnpm 的 git 参数(`parseGitParams` 里按 `path:` 前缀解析),**不是**本仓库自定义的语法;pnpm 官方文档没写这一条,所以这里给出实测结果。
329
+
330
+ #### 方式四:tarball
331
+
332
+ ```bash
333
+ cd dsh-mneme
334
+ npm pack # 产出 modusensus-dsh-mneme-<版本>.tgz
335
+ dsh plugin --profile web add "C:/path/to/modusensus-dsh-mneme-0.9.0.tgz"
336
+ ```
337
+
338
+ > 必须用 `npm pack`,别用 `pnpm pack`:`bin/cli.mjs` 在工作区里是 CRLF,pnpm 会以 `ERR_PNPM_BIN_CRLF` 直接失败。
339
+
286
340
  #### 自定义配置(可选)
287
341
 
288
342
  默认配置即可用。如需调整,在 `~/.dsh/profiles/web/cordis.patch.yml` 中覆盖:
@@ -443,7 +497,9 @@ dsh web
443
497
  | `conflictFreezeMaxPending` | `100` | 冲突冻结队列最大挂起数(1-1000):超出后不再入队(防队列无限膨胀) |
444
498
  | `escapePromptVariables` | `true` | 注入文本花括号转义(v0.8.0 恢复,issue #162):注入边界把 `{{...}}` 转义,防止 hot memory / 记忆原文里的 Go template / Vue 语法触发宿主 interpolate 抛错卡死会话(v0.7.4 曾修复、v0.7.11 误删) |
445
499
 
446
- > 🔐 **API 安全**:DSH 无内置鉴权且默认仅监听 `127.0.0.1`。插件 API 默认开放(便于 Web 面板即装即用)。如需防护(如局域网暴露),在配置中设置 `apiToken`:写操作(画像/规则/命令)与密钥端点(`vector-config`、`vector-reindex`)需携带 `Authorization: Bearer <token>`(前端设置面板可填入同一 token),只读的 `list` / `search` / `semantic` 保持开放。`/api/dsh-mneme/vector-config` 返回的 `apiKey` 已掩码(`sk-***…`),存储仍保留明文供调用;前端回传空或掩码值表示"不改 key"。
500
+ > 🔐 **API 安全**:DSH 无内置鉴权且默认仅监听 `127.0.0.1`。插件 API 默认开放(便于 Web 面板即装即用)——**未配 `apiToken` 时读和写都开放**,真正的围栏是回环 + 宿主监听器的 Host/Origin 校验(它防 DNS rebinding 与跨站浏览器请求,但不建立身份)。如需防护(如局域网暴露),在配置中设置 `apiToken`:此时**写操作**(新增/更新/删除/导入/画像/规则/命令/开关/开关计费类)与**敏感读**(`/export`、`/external-api`、`/vector-config`、`/vector-reindex`、`/maintenance/*`、`/runtime/provision`)需携带 `Authorization: Bearer <token>`(前端设置面板填同一个 token);只读的 `list` / `search` / `semantic` / `inject-preview` 等**仍然开放**,不要把它们当成被 token 保护的面。`/api/dsh-mneme/vector-config` 返回的 `apiKey` 已掩码(`sk-***…`),存储仍保留明文供调用;前端回传空或掩码值表示"不改 key"。
501
+ >
502
+ > 安全模型全表(哪些路由免鉴权、为什么)见 [SECURITY.md](../SECURITY.md#5-security-model-what-actually-protects-what)。
447
503
 
448
504
 
449
505
  > ⚠️ **已知边界:极简模式(minimal agent preset)下不注入**(宿主设计,非插件缺陷):minimal 预设的组合文件显式 `complete: true` + `includeRuntimeContext: false`——整条系统提示词被 persona 钉死、全部 runtime context 快照被压制,**记忆注入 / 用户画像 / hot memory 在极简模式会话中一律不送达模型**(宿主内置 context 同样消失)。判断与解法:会话头部显示「极简模式」即为该形态;需要记忆注入请改用标准模式(会话级切换,或 `~/.dsh/settings.yaml` 设 `agent-presets.default: standard`——注意 `--dump-config` 显示的是 bundle 默认值,会被用户层覆盖,不能反映实际生效值);极简模式下的过渡方案是把画像/规则写入 `AGENTS.md`(agent-instructions 走 section 路径,不受该压制影响)。
@@ -474,6 +530,7 @@ dsh web
474
530
  | `GET` | `/search?q&mode=keyword\|vector\|hybrid\|auto&topK&occurred_from&occurred_to&agent_scope&workspace_scope` | 搜索(关键词 / 向量 / hybrid=向量领位关键词补位 / 自动)。scope 两参声明调用方身份(不传 = 不解析,行为与旧版一致;任一给出则 strictScope 硬过滤与 A2 软加权随之生效) |
475
531
  | `GET` | `/context?q&topK&threshold&agent_scope&workspace_scope` | 注入候选一站式聚合(issue #370,#363 承诺):宿主注入管线 `injectCandidates` 全语义(优先级分层 / 编码门控 / heat / scope 软加权 / hybrid 语义路 / pin 前置)+ 画像 + 规则,返回 `{profile, rules, items, pinnedCount}` 结构化条目,不含渲染文本。`threshold` 是 **importance 阈值**(默认 3,与 `/search` 的相似度阈值同名不同义);`topK` 默认 5(对齐宿主 `maxInjectedItems`);scope 语义与 `/search` 同口径;跨轮轮换 / 热记忆 / 时间前缀不含(调用方自管);recall_runs 注入分账与宿主注入同口径(#217 `mode='inject'`) |
476
532
  | `POST` | `/bootstrap` | 冷启动(v0.8.4):从仓库目录反向构建初始记忆,body `{dir}` 必填;幂等((type,title,scope) 去重 + `_overwrite` 原地刷新),零 LLM、必有产出 |
533
+ | `POST` | `/maintenance/reclaim` | 无损回收(#275 / #411,与面板同口径):**只有 POST**(独立面没有 GET 预估分支)。字段只从 JSON body 读;`dryRun` 默认 `true`,只有 `dryRun:false` 且 `confirm:true` 才真删;`vacuum` 只允许显式指定 |
477
534
 
478
535
  ### curl 示例
479
536
 
@@ -541,7 +598,7 @@ dsh-mneme-serve --embed off # 纯关键词 + BM
541
598
 
542
599
  ### MCP Server(任意 MCP 客户端接入)
543
600
 
544
- > Claude Code / Codex / Hermes / OpenCode / OpenClaw 等各客户端的最小挂载配置速查表见[根 README](../README.md#用在其他-ai-工具里mcp);本节是完整配置与安全说明。规划中的独立分发包 `mneme-memory` 落地后,挂载命令将保持兼容(详见仓库 Discussions #300)。
601
+ > Claude Code / Codex / Hermes / OpenCode / OpenClaw 等各客户端的最小挂载配置速查表见[根 README](../README.md#用在其他-ai-工具里mcp);本节是完整配置与安全说明。独立分发包 [`mneme-memory`](https://www.npmjs.com/package/mneme-memory)(bin `mneme-mcp`)已发布,与插件内置的 `dsh-mneme-mcp` 并存、挂载命令互相兼容。
545
602
 
546
603
  插件自带 stdio MCP server(`bin/dsh-mneme-mcp.mjs`,零依赖,随 npm 包发布,bin 名 `dsh-mneme-mcp`)。任何支持 Model Context Protocol 的客户端(Claude Code、Cursor 等)挂载后即可获得与 DSH 内一致的记忆工具六件套:`memory_save` / `memory_search` / `memory_list` / `memory_get` / `memory_update` / `memory_delete`——工具名、参数与去重合并、重要性等语义与 DSH 内工具对齐(测试锁漂移)。
547
604
 
@@ -584,7 +641,7 @@ Claude Code 挂载示例(项目根 `.mcp.json`;token 在面板「设置 →
584
641
  │ 服务层:saveWithDedupe / injectCandidates │
585
642
  │ / mergeHumanEdits / onWrite 钩子 │
586
643
  ├─────────────────────────────────────────────────┤
587
- │ 模型接口:9 个工具 + 自动注入 + 会话摘要 │
644
+ │ 模型接口:10 个工具 + 自动注入 + 会话摘要 │
588
645
  ├─────────────────────────────────────────────────┤
589
646
  │ autoDream:阈值调度 → LLM 决策清单 │
590
647
  │ → 校验(fail-safe)→ 应用 → 摘要 │
@@ -602,7 +659,7 @@ src/
602
659
  ├── service.js # 领域逻辑(去重合并、注入筛选、写入钩子、scope 加权/硬过滤、冲突队列)
603
660
  ├── scope.js # 作用域解析(agentPreset + workspace registry 反查,issue #17)
604
661
  ├── config.js # schemastery 配置 schema
605
- ├── tools.js # 9 个模型工具(defineTool)
662
+ ├── tools.js # 10 个模型工具(defineTool)
606
663
  ├── inject.js # systemPrompt.context 动态注入(含花括号转义)
607
664
  ├── summarize.js # 会话结束 LLM 摘要
608
665
  ├── dream.js # autoDream 调度 + runDream(LLM 决策 + 摘要)
@@ -617,8 +674,8 @@ src/
617
674
  ├── embedding.js # OpenAI 兼容 embeddings 客户端 + 向量检索
618
675
  ├── api.js # HTTP 路由(Web 面板数据通道,含 /conflicts 冲突队列)
619
676
  └── index.js # 插件接线
620
- lib/ # src 的同步分发产物(npm run sync;发布前由 root prepack 的 check-sync.js 校验一致性;唯一手写例外 lib/client.js——Web 面板 bundle,sync 不覆盖)
621
- test/ # 1869 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验)
677
+ lib/ # src 的同步分发产物(npm run sync,prepack 钩子在发布时自动跑;一致性由 scripts/check-sync.js 在 CI 与 release.yml 校验——它只查不同步;唯一手写例外 lib/client.js——Web 面板 bundle,sync 不覆盖)
678
+ test/ # 2230 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验)
622
679
  scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压测 · sync-lib.js 同步 · check-sync.js 发布闸门 · benchmark-recall.js / benchmark-embed.js / benchmark-rerank.js 基准 · sync-test-badge.mjs 测试徽章 · build-runtime-manifest.mjs 运行时清单
623
680
  ```
624
681
 
@@ -627,7 +684,7 @@ scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压
627
684
  ```bash
628
685
  cd dsh-mneme
629
686
  npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试)
630
- npm test # 运行 1869 个测试
687
+ npm test # 运行 2230 个测试
631
688
  npm run stress # 三轴线压测:长会话检索 / 冲突仲裁 / 多 Agent 并发(离线 mock LLM)
632
689
  npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行)
633
690
  ```
@@ -638,13 +695,19 @@ npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动
638
695
 
639
696
  ## 📄 设计文档
640
697
 
641
- > 设计文档位于仓库根 `docs/`,链接以 `../docs/` 相对路径指向(GitHub 上从本目录打开可正常跳转)。
698
+ > 设计文档在 `dsh-mneme/docs/`(本目录内,直接相对链接);只有能力对比表在仓库根 `docs/`,用 `../docs/` 指过去。GitHub 上从本目录打开均可正常跳转。
642
699
 
643
700
  - [配置说明(全键参考)](docs/CONFIGURATION.md)
644
- - [蒸馏原文留存与记忆级溯源](docs/DISTILL.md)
701
+ - [蒸馏原文留存与记忆级溯源](docs/DISTILL.md)(#399)
645
702
  - [实体结构化记忆设计](docs/ENTITIES.md)
646
703
  - [语义增强架构](docs/SEMANTIC.md)
647
704
  - [本地模型部署指南](docs/LOCAL_MODEL.md)
705
+ - [工作流(M.O. · 手法):本体住项目,证据住记忆库](docs/WORKFLOWS.md)
706
+ - [连续性抢救(#249 N3):触发器口径与事件契约](docs/CONTINUITY.md)
707
+ - [Sleep Mode:空闲四阶段深度维护](docs/SLEEP.md)
708
+ - [存储与回收:表结构、保留期、`dsh-mneme reclaim`](docs/STORAGE.md)
709
+ - [独立服务 daemon(`dsh-mneme-serve`)](docs/DAEMON.md)
710
+ - [与 Zep / Graphiti / Mem0 / Letta / Hindsight 的能力对比](../docs/COMPARISON.md)
648
711
 
649
712
  ## 🙏 致谢
650
713
 
@@ -666,7 +729,7 @@ dsh-mneme 的机制设计与下列公开研究相互印证(以下引用均逐
666
729
  - **何时该忘 / scope 门控**:[PersistBench](https://arxiv.org/abs/2602.01146)(arXiv:2602.01146);呈现层缓解见 [Structured Memory](https://arxiv.org/abs/2608.08300)(arXiv:2608.08300)——scope 语义域门控的问题出处与零契约变更改法。
667
730
  - **领域全景**:[Write–Manage–Read 综述](https://arxiv.org/abs/2603.07670)(arXiv:2603.07670)——「写入 / 管理 / 读取」三段式与插件整体形状互证。
668
731
  - **数月尺度的可靠性工程**:[Memory as Infrastructure](https://arxiv.org/abs/2609.05510)(arXiv:2609.05510)——与 sha256 审计链、fail-safe 降级同属「记忆即基础设施」路线。
669
- - **相邻系统(对照)**:[Zep](https://arxiv.org/abs/2501.13956)(时序知识图谱)、[Mem0](https://arxiv.org/abs/2504.19413)(生产级抽取式记忆)、[Hindsight](https://arxiv.org/abs/2512.12818)(retain / recall / reflect 三操作)走「写入时 LLM 抽取 + 向量/图检索」路线;dsh-mneme 的差异点在于:巩固由 LLM 参与但产物全程留痕可回放,冲突冻结待人工复核,默认离线。
732
+ - **相邻系统(对照)**:[Zep](https://arxiv.org/abs/2501.13956)(时序知识图谱)、[Mem0](https://arxiv.org/abs/2504.19413)(生产级抽取式记忆)、[Hindsight](https://arxiv.org/abs/2512.12818)(retain / recall / reflect 三操作)走「写入时 LLM 抽取 + 向量/图检索」路线;dsh-mneme 的差异点在于:巩固由 LLM 参与但产物全程留痕可回放,冲突冻结待人工复核,记忆只落本机。
670
733
 
671
734
  ## 📜 License
672
735
 
package/bin/cli.mjs CHANGED
@@ -19,8 +19,14 @@ const CLI_NAME = 'dsh-mneme';
19
19
  const DEFAULT_URL = 'http://127.0.0.1:8790';
20
20
  const CONFIG_DIR = path.join(os.homedir(), '.dsh-mneme');
21
21
  const CONFIG_PATH = path.join(CONFIG_DIR, 'cli.json');
22
- const MEMORY_TYPES = ['preference', 'project', 'decision', 'summary', 'history'];
23
- const SEARCH_MODES = ['keyword', 'vector', 'auto'];
22
+ // 与 src/store.js 的 TYPES 对齐(CLI 是零依赖 HTTP 客户端,不 import 服务端
23
+ // 模块,所以这里是手写字面量)。document 是唯一例外:它只能经
24
+ // memory_register_document 铸造,saveWithDedupe 会拒绝旁路写入,所以不作为
25
+ // --type 的选项——test/cli-enums.test.js 锁住「差值恰好是 document」。
26
+ const MEMORY_TYPES = ['preference', 'project', 'decision', 'history', 'summary', 'pattern', 'rejected_solution', 'pitfall', 'constraint'];
27
+ // hybrid:API 的 searchMemories 支持(向量先、关键词补位、双路命中做分数混合,
28
+ // 见 src/api.js 的 mode 注释)。此前漏列,search --mode hybrid 会被客户端自己拒掉。
29
+ const SEARCH_MODES = ['keyword', 'vector', 'auto', 'hybrid'];
24
30
  const REQUEST_TIMEOUT_MS = 10_000;
25
31
 
26
32
  function readPkgVersion() {
@@ -96,6 +102,40 @@ const USAGE = `${CLI_NAME} —— DSH 记忆插件(@modusensus/dsh-mneme)外
96
102
 
97
103
  /* ---------------------------------- 参数解析 ---------------------------------- */
98
104
 
105
+ // `--flag value` 形态下,「下一个词以 -- 开头就别当值」这条规则有一个致命反例:
106
+ // 值本身就可能是 `--` 开头的字符串。外部 API 的 token 由
107
+ // `crypto.randomBytes(24).toString("base64url")` 生成(src/api-standalone.js),
108
+ // base64url 的字母表含 `-`,所以约 1/4096 的 token 头两位恰好都是 `-`:
109
+ //
110
+ // dsh-mneme reclaim --url http://127.0.0.1:55423 --token --UYoOHxr6PtE2-bfqW...
111
+ //
112
+ // 旧规则把 `--UYoOHxr6PtE2-bfqW...` 当成一个新旗标(键名 `uyoohxr6pte2-bfqw...`),
113
+ // 于是 token 解析成 `true`,flagValue 视作「没给」→ 退回环境变量/配置文件 → 全空
114
+ // → 报「未配置访问 token」。而 CLI 生成 token 的另一半同样会撞:`--token=--abc`
115
+ // 走等号分支没事,`--token --abc` 就废。这不是测试的随机性,是真实用户的随机性。
116
+ //
117
+ // 修法:区分「已知旗标」与「未知词」。下一个词以 `--` 开头时,只有它确实是一个
118
+ // 已注册的旗标才当作旗标,否则当作当前 flag 的值。于是 `--limit --json` 仍按
119
+ // 旧语义把 --json 当旗标,而 `--token --UYo...` 正确取到 token。
120
+ // 已知旗标表必须与下面 flagValue/flagBool 的实际用法保持同步;漏登记的代价是
121
+ // 该旗标被当成值吞掉,所以 test/cli-flag-value.test.js 用文本锁对齐两者。
122
+ const KNOWN_FLAGS = new Set([
123
+ // 取值的旗标
124
+ 'url', 'token', 'type', 'title', 'content', 'importance', 'tags', 'source',
125
+ 'limit', 'offset', 'mode', 'topk', 'top-k', 'min-importance', 'minimportance',
126
+ 'older-than', 'olderthandays',
127
+ // 布尔旗标
128
+ 'apply', 'vacuum', 'json', 'help', 'version'
129
+ ]);
130
+
131
+ /** `--foo=bar` / `--foo` 取出小写键名;非旗标词返回 null。 */
132
+ function flagKeyOf(word) {
133
+ if (typeof word !== 'string' || !word.startsWith('--') || word === '--') return null;
134
+ const key = word.slice(2);
135
+ const eq = key.indexOf('=');
136
+ return (eq >= 0 ? key.slice(0, eq) : key).toLowerCase();
137
+ }
138
+
99
139
  function parseArgv(argv) {
100
140
  const positionals = [];
101
141
  const flags = {};
@@ -112,10 +152,14 @@ function parseArgv(argv) {
112
152
  if (eq >= 0) {
113
153
  value = key.slice(eq + 1);
114
154
  key = key.slice(0, eq);
115
- } else if (i + 1 < argv.length && !argv[i + 1].startsWith('--') && argv[i + 1] !== '--') {
116
- value = argv[++i];
117
155
  } else {
118
- value = true;
156
+ const next = argv[i + 1];
157
+ const nextIsFlag = next !== undefined && flagKeyOf(next) !== null && KNOWN_FLAGS.has(flagKeyOf(next));
158
+ if (next !== undefined && next !== '--' && !nextIsFlag) {
159
+ value = argv[++i];
160
+ } else {
161
+ value = true;
162
+ }
119
163
  }
120
164
  flags[key.toLowerCase()] = value;
121
165
  } else {
@@ -685,7 +729,23 @@ async function main(argv) {
685
729
  }
686
730
  }
687
731
 
688
- main(process.argv.slice(2)).catch((err) => {
689
- console.error(err?.stack ?? String(err));
690
- process.exit(1);
691
- });
732
+ // 直接运行才进 main(import 只取 parseArgv 做单测时必须安静):npm 全局安装走
733
+ // node_modules/.bin 的符号链接,argv[1] 是链接路径而非真身,所以要 realpath 两侧
734
+ // 再比,不能用字符串相等。
735
+ const invokedDirectly = (() => {
736
+ if (process.argv[1] == null) return false;
737
+ try {
738
+ return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
739
+ } catch {
740
+ return false;
741
+ }
742
+ })();
743
+
744
+ if (invokedDirectly) {
745
+ main(process.argv.slice(2)).catch((err) => {
746
+ console.error(err?.stack ?? String(err));
747
+ process.exit(1);
748
+ });
749
+ }
750
+
751
+ export { parseArgv, KNOWN_FLAGS, flagValue, flagBool, flagKeyOf };
@@ -28,6 +28,29 @@
28
28
  | `scopeEnabled` | `false` | 存储层总开关:**两条写入路径**(工具调用 `memory_save`/`memory_update`,以及机器提炼·会话蒸馏)都自动标注 agent_scope / workspace_scope(标注只贴**新建行**:并入既有行时不重贴,既有行的归属属于它原来被提炼出来的语境),去重键扩展;检索与注入排序按当前会话作用域加权(命中 ×1.25、他 scope ×0.5 保留可见,issue #339 补齐注入通道) | opt-in;关 = 写入不标注、行为与 A1 前逐字节一致 |
29
29
  | `strictScope` | `false` | 硬过滤模式(A3):他 scope 完全不可见、未标注恒可见(fail-closed);硬过滤先于软加权执行 | 依赖 `scopeEnabled` 打开才有意义;关 = A2 软隔离(他 scope 降权保留可见,检索与注入同权重) |
30
30
 
31
+ ## 作用域级可逆闸门(issue #471)
32
+
33
+ 上面两个键是**事实轴**——答「这条记忆属于谁」,改的是归属数据、跟着记忆行走。下面两个键是**政策轴**——答「这个作用域现在通不通」,由人按作用域整体按下、跨会话存续、松开即原样回来,**一条记忆行都不动、一个配置键都不改、不用重启**。两条轴落在同一个判定出口(`gateByScope`,全仓唯一的过滤实现),可叠加。
34
+
35
+ 按下与松开走工具 `set_scope_state`(`dimension`: `agent`/`workspace`,`scope_value`: 标签本身或 `*` 表示该维度全局,`state`: `paused`/`active`)。状态存 `scope_state` 表,键 = 维度 + 作用域值,`UNIQUE(dimension, scope_value)` 保证幂等:**重复按下同一状态不刷新 `decided_at`**(同 `memory_archive` 的取舍——否则「最后一次真正变更」的时刻会被噪声盖掉)。
36
+
37
+ 两级语义与 `writeAdmission` / `preInjectGate` 同构:先观察、再拦。
38
+
39
+ | 键 | 默认 | 作用 | 开启后果 / 冲突 |
40
+ |---|---|---|---|
41
+ | `scopeState.enabled` | `false` | 跑政策轴判定:被按下的作用域其候选**照常注入**,但每次落一条 `llm_audit_logs`(`trigger_source=scopeState`,`metadata.paused` 记被按下的是哪些作用域、`metadata.gated` 记本应拦下几条、`metadata.enforced=false`) | 关 = 判定不跑、行为与今天逐字段一致。先看「如果按下会少掉多少」的真实分布,再决定真按——`gated` 恒为 0 就说明这一路本来就没在供料 |
42
+ | `scopeState.enforce` | `false` | 真的不注入:被按下的作用域其候选出池(检索侧同款,`gateByScope` 一处生效;巩固与睡眠两侧的读入口与写入路径同款) | 关 = 仅观察(`metadata.enforced=false`)。**enforce 无 enabled 时无效果**(同 `preInjectGate`)。按下的是政策不是事实:记忆行、向量、归档位、作用域标注全部不动,松开即恢复 |
43
+
44
+ 覆盖范围:`gateByScope` 的三条检索路(融合池、`entity:`、`attr:` 前缀路)与注入候选池(`injectCandidates` 内、图召回合并之后、pin 池之前)。
45
+
46
+ **不覆盖检视面**:`memory_list`、`memory_get`、面板列表**刻意不过这道闸**(它们走 `strictVisibility` 与 `store.list` 的 `visibility` 参数,是另一条平行的链)。按下之后人还要能查「我按下了什么、里面有什么」——检视面关掉,人就没有入口确认该不该松开。这也正是「按下的是政策不是事实」的具体含义:行还在,只是不再喂给模型。
47
+
48
+ **巩固(dream)侧另接四处**:`activeStoreSize`、叙述条 `inputs`、`runDream` 的读入口、总览 `summaryAll`——否则被按下的行会照常参与阈值判定与合并。**总览那一处只能从源头挡**:总览落库时是**无作用域**的 summary 行,而 `gateInjection` 只拦匹配暂停作用域的行,没有标注的行永远不会被它拦下;被按下的内容一旦进了总览就变成「洗干净的全局行」,此后一直注入。
49
+
50
+ **睡眠(sleep)侧另接四个相位**:冲突消解、归档降级、模式发现、关系补全的候选池。这一侧比日间更要紧,因为**它不只读**——`phaseDemotion` 会 `setArchived` 与 `demoteToSummary`(真改归档面),`phasePatterns` 会铸出 `type=pattern` 的新行;不接的话闸门开着而每 6 小时一次的睡眠照样在被按下的作用域上动手,「按下」就只等于让它在白天看不见。两处位置纪律写在注释里:降级的过滤放在循环之外(不是循环体里 `continue`,免得将来有人加副作用时漏掉),模式发现的过滤排在 `.slice(0, limit)` 之前(否则被按下的行照样占名额,它们既不会被扫、也不该挤掉别人)。
51
+
52
+ `llmAudit.enabled=false` 时观察档一行都不写(同 `writeAdmission` 的口径)。不进 lightMode 强制关闭清单——它只是查一张小表的判定,不引入重型后台路径、不加常驻文本(同 `actionPipelineEnabled` 的定性)。
53
+
31
54
  ## 审计
32
55
 
33
56
  | 键 | 默认 | 作用 | 开启后果 / 冲突 |
@@ -43,6 +66,7 @@
43
66
  |---|---|---|---|
44
67
  | `writeAdmission.enabled` | `false` | 跑第 1 级确定性判据:空白 / 纯噪声 + 密钥 / PII(判据来源是 #164 A2,经 `sensitiveScan` 注入) | 关 = 不判定、不拒绝,`evaluate` 返回与只计量阶段逐字段一致。命中 → 审计行带 `metadata.deny` |
45
68
  | `writeAdmission.enforce` | `false` | 命中时真的拒绝(`store.save` 之前返回) | 关 = 仅告警:判据照跑、审计照留、写入不拦(读审计时「`deny` 非空且 `decision=allow`」即这一档的指纹)。被拒写入不落库、不通知、不排嵌入,`memory_save` 返回 `action:"denied"` + `reason` |
69
+ | `sensitiveScanEnabled` | `false` | A2 密钥 / PII 判据的来源(`sensitive-scan.js`)。开 = 判定结果可被写入准入消费;只覆盖带 sessionKey 的写入路径 | 关 = 扫描器不参与,`writeAdmission` 第 1 级只剩空白/噪声判据。**经 MCP / 8790 / `/import` 的写入不带 sessionKey,不进这道闸**(`write-admission.js` 的文件头注释点名了这条边界) |
46
70
 
47
71
  第 1 级的判据面刻意收窄:**去重键命中不进第 1 级**(归 write-update 放行——同步近重复门跑在异步矛盾检测之前会把本该被裁决的矛盾直接拒掉),G1/G2 阈值也只是计量。两个键都走 feature_flags 白名单(面板可启停)。
48
72
 
@@ -114,6 +138,8 @@
114
138
  | `hotMemoryRounds` | `5` | 热记忆覆盖最近几轮对话(≤50) | — |
115
139
  | `hotMemoryMaxTokens` | `2000` | 热记忆块 token 上限(200–32000) | — |
116
140
  | `escapePromptVariables` | `true` | 注入内容连续花括号转义(#162) | 关 = 原样透传(DSH interpolate 可能 throw) |
141
+ | `injectTimePrefix` | `false` | 对话开始注入一次当前时间(#34,v0.7.11 误删后随 #333 恢复) | opt-in;格式与键名与 v0.7.2 一致,存量 feature_flags 恢复即生效 |
142
+ | `personaEnabled` | `false` | 人格段:用户自写的一小段常驻注入文本 | opt-in;只是轻量常驻文本、不引入重型后台路径,故不进 `LIGHT_MODE_OFF` |
117
143
 
118
144
  ## 巩固(autoDream)
119
145
 
@@ -223,6 +249,21 @@
223
249
  | `rerankScoreThreshold` | `0.1` | 重排分数阈值 | — |
224
250
  | `rerankDtype` | `q8` | 量化档(#188) | 非法值由 transformers 抛出 → 重排降级告警 |
225
251
 
252
+ ## 图谱(graph,opt-in,#24)
253
+
254
+ 图谱四条链路都依赖实体抽取产出(`entityExtractionEnabled`),默认全关;lightMode 把前四个布尔键一并强制关。级联与预算键只是阈值,跟着开关走。
255
+
256
+ | 键 | 默认 | 作用 | 开启后果 / 冲突 |
257
+ |---|---|---|---|
258
+ | `graphAnchoringEnabled` | `false` | 块1 图谱锚定:以查询命中的实体为种子沿关系表级联扩散(默认深度 2) | opt-in;关 = 与 #219 单跳轴逐字节一致;需 `entityRecallEnabled` 之上再开 |
259
+ | `graphSeedCap` | `12` | 锚定种子集上限(1–30) | 只影响一次扩散的宽度 |
260
+ | `graphCascadeDepth` | `2` | 级联深度(1–3) | 深度与召回面线性相关,3 容易把远邻拉进来 |
261
+ | `graphWeightEnabled` | `false` | 块2 边权重演化:被寻回路径的边权重抬升(只加不减、封顶 1.0) | opt-in;关 = 边初值纯由建边来源决定 |
262
+ | `graphWeightDelta` | `0.1` | 单次有效触达的抬升幅度(0–1) | 与其他 graph 开关无关的纯阈值 |
263
+ | `graphPassiveConfirm` | `false` | 块4 被动确认:heat 触达(召回/注入)即给挂联关系边抬一格 | opt-in;关 = 零行为变化 |
264
+ | `graphInjectHint` | `false` | 块3 关联提示标注:图召回候选在注入文本里以「[检索线索] …」标出 | 关 = 只参与排序、不改变注入块构成(保守档) |
265
+ | `graphInjectBudget` | `1` | 图召回候选在注入块里的独立预算(0–5 条) | 不占 `maxInjectedItems` 名额;0 = 关闭 |
266
+
226
267
  ## 热度
227
268
 
228
269
  | 键 | 默认 | 作用 | 开启后果 / 冲突 |
@@ -242,6 +283,8 @@
242
283
  | `externalApiHost` | `127.0.0.1` | 绑定地址 | 改成非回环 = 把整个记忆库暴露给网络,自负其责 |
243
284
  | `disableMemorySearch` | `false` | 对模型隐藏 `memory_search` 工具(v0.8.5) | 慢/轻量模型的工具往返节流;隐藏即不可调 |
244
285
  | `disableMemoryArchive` | `false` | 对模型隐藏 `memory_archive` 工具 | 同上 |
286
+ | `actionPipelineEnabled` | `false` | M.O.(手法)只读视图的门控:做法图谱 + 对话行结算卡 | opt-in beta;只是纯 UI 入口、不引入重型后台路径,故不进 `LIGHT_MODE_OFF`。写账另有独立的执行记账键(`/mo-run`),看图 ≠ 写账 |
287
+ | `actionRunEnabled` | `false` | M.O. 执行记账:`/mo-run` 开一次执行窗口,窗口内命令执行才可能被记成一条 run | opt-in beta;挂在热路径(每个会话事件都过一次监听),故 `LIGHT_MODE_OFF` 里有它 |
245
288
 
246
289
  ## 运行时与本地模型
247
290
 
@@ -260,6 +303,9 @@
260
303
  | `runtimeDir` | `""` | 自管运行时目录(transformers + onnxruntime 闭包,#131) | 空 = `~/.dsh/mneme/runtime`;把重依赖挪出宿主 profile 的依赖图 |
261
304
  | `runtimeTarballDir` | `""` | 本地 `.tgz` 离线取件目录 | 有货优先于联网(弱网下装 onnxruntime-node 用) |
262
305
  | `runtimeMirror` | `""` | registry 镜像前缀(如 `https://npmmirror.com/mirrors/npm/`) | 空 = manifest 里的官方地址 |
306
+ | `reindexMaxPerBoot` | `500` | 启动期向量回填的单次上限(原先硬编码 500) | `0` = 不限、本次跑完整个积压(4k 行积压靠重启九次才清空的旧行为就是被这个上限卡住的) |
307
+ | `reindexBatchSize` | `10` | 回填批大小(1–64) | 越大单批 token/耗时越高 |
308
+ | `reindexRateLimitMs` | `200` | 回填批间隔毫秒(限速) | 0 = 不限速 |
263
309
 
264
310
  ## lightMode 强制关闭清单(`LIGHT_MODE_OFF`)
265
311
 
@@ -269,6 +315,8 @@
269
315
  `entityExtractionEnabled` · `autoDream` · `sleepModeEnabled` · `rerankEnabled` ·
270
316
  `autoReindexOnBoot` · `hybridInject` · `injectGuidanceEnabled` · `searchSemanticDedup` ·
271
317
  `selectiveInjectEnabled` · `bm25SearchEnabled` · `entityRecallEnabled` ·
318
+ `graphAnchoringEnabled` · `graphWeightEnabled` · `graphInjectHint` · `graphPassiveConfirm` ·
319
+ `actionRunEnabled` ·
272
320
  `dreamNarrativeEnabled` · `documentMemoryEnabled` · `heatEnabled` · `continuityRescueEnabled` ·
273
321
  `preInjectGate.enabled`(#380) · `distillRecords.enabled`(#399)
274
322
 
package/docs/SEMANTIC.md CHANGED
@@ -3,7 +3,7 @@
3
3
  - **日期**:2026-08-15
4
4
  - **状态**:✅ 已实现(本地 Embedding / Rerank / 聚类随 v0.2+ 落地;本文档转为历史设计存档,当前能力以 [README](../README.md) 正文为准)
5
5
  - **范围**:为 dsh-mneme 增加"完全离线的语义记忆引擎",覆盖本地 Embedding、Rerank 精排、autoDream 向量聚类三个阶段
6
- - **相关文档**:[本地模型部署指南](LOCAL_MODEL.md) · [从 v0.1 升级说明](MIGRATION.md)
6
+ - **相关文档**:[本地模型部署指南](LOCAL_MODEL.md) · [全键配置参考](CONFIGURATION.md)
7
7
 
8
8
  ## 1. 背景与目标
9
9
 
@@ -59,7 +59,7 @@ scripts/
59
59
  docs/
60
60
  ├── SEMANTIC.md # 本文档
61
61
  ├── LOCAL_MODEL.md # 本地模型部署指南
62
- └── MIGRATION.md # 从 v0.1 升级说明
62
+ └── CONFIGURATION.md # 全键配置参考
63
63
  ```
64
64
 
65
65
  ## 3. 搜索流水线
@@ -207,7 +207,7 @@ LocalEmbedder(ONNX) ──失败──► OllamaEmbedder ──失败──► O
207
207
 
208
208
  ## 7. 配置新增项
209
209
 
210
- 见 [MIGRATION.md](MIGRATION.md) 完整表格。核心:`embedProvider`(`local` / `ollama` / `openai`,默认 `openai` 保持 v0.1 行为)、`localEmbedModel`、`localEmbedDimension`、`localEmbedDevice`、`localEmbedBatchSize`、`ollamaBaseUrl`、`ollamaModel`、`embedModelCacheDir`、`rerankEnabled`、`rerankModel`。
210
+ 见 [CONFIGURATION.md](CONFIGURATION.md) 的检索与运行时键表。核心:`embedProvider`(`local` / `ollama` / `openai`,默认 `openai` 保持 v0.1 行为)、`localEmbedModel`、`localEmbedDimension`、`localEmbedDevice`、`localEmbedBatchSize`、`ollamaBaseUrl`、`ollamaModel`、`embedModelCacheDir`、`rerankEnabled`、`rerankModel`。
211
211
 
212
212
  ## 8. 测试与基准
213
213