@modusensus/dsh-mneme 0.11.0 → 0.13.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.
Files changed (174) hide show
  1. package/README.md +54 -21
  2. package/docs/CONFIGURATION.md +45 -4
  3. package/docs/DIRECTORIES.md +114 -0
  4. package/docs/DISTILL.md +64 -0
  5. package/docs/DSH-GO.md +97 -0
  6. package/docs/ENTITIES.md +5 -3
  7. package/docs/FORGET.md +77 -0
  8. package/docs/KB.md +110 -0
  9. package/docs/SLEEP.md +28 -3
  10. package/docs/STORAGE.md +23 -0
  11. package/lib/api-standalone.js +4 -1
  12. package/lib/api.js +744 -26
  13. package/lib/attachment-text.js +122 -0
  14. package/lib/choro-command.js +9 -2
  15. package/lib/client.js +3752 -819
  16. package/lib/config.js +171 -2
  17. package/lib/continuity.js +7 -6
  18. package/lib/diary/auto.js +190 -0
  19. package/lib/diary/compose.js +440 -0
  20. package/lib/diary/dir.js +59 -0
  21. package/lib/diary/export.js +97 -0
  22. package/lib/diary/index.js +26 -0
  23. package/lib/diary/library.js +189 -0
  24. package/lib/diary/qq-inbox.js +142 -0
  25. package/lib/diary/rollover.js +61 -0
  26. package/lib/diary/session-provider.js +122 -0
  27. package/lib/document.js +9 -3
  28. package/lib/dream/clustering.js +6 -1
  29. package/lib/dream/decisions.js +56 -5
  30. package/lib/dream/sleep.js +616 -15
  31. package/lib/dream.js +45 -13
  32. package/lib/dsh-go/host.js +162 -0
  33. package/lib/dsh-go/panel.js +2091 -0
  34. package/lib/dsh-go/text.js +342 -0
  35. package/lib/dsh-go/views/common.js +68 -0
  36. package/lib/dsh-go/views/entity.js +48 -0
  37. package/lib/dsh-go/views/memory.js +198 -0
  38. package/lib/dsh-go/views/root.js +30 -0
  39. package/lib/dsh-go/views/settings.js +398 -0
  40. package/lib/dsh-go/views/status.js +51 -0
  41. package/lib/embedding.js +42 -8
  42. package/lib/entities/extractor.js +36 -7
  43. package/lib/hot-memory.js +8 -9
  44. package/lib/index.js +131 -5
  45. package/lib/inject.js +192 -19
  46. package/lib/kb/index.js +309 -0
  47. package/lib/kb/routes.js +220 -0
  48. package/lib/kb/tools.js +148 -0
  49. package/lib/lang.js +115 -4
  50. package/lib/peak-hours.js +57 -2
  51. package/lib/profile-facts.js +356 -0
  52. package/lib/semantic.js +347 -60
  53. package/lib/service.js +302 -70
  54. package/lib/settings.js +117 -14
  55. package/lib/store.js +830 -55
  56. package/lib/summarize.js +38 -11
  57. package/lib/tools.js +173 -25
  58. package/lib/user-settings.js +175 -23
  59. package/lib/vector-index.js +76 -6
  60. package/lib/wiki/cli-hint.js +90 -0
  61. package/lib/wiki/queries.js +264 -10
  62. package/lib/wiki/t1-jobs.js +232 -0
  63. package/lib/wiki/t1.js +7 -1
  64. package/package.json +25 -1
  65. package/scripts/sync-test-badge.mjs +1 -1
  66. package/scripts/test-count-sync.mjs +9 -5
  67. package/src/api-standalone.js +4 -1
  68. package/src/api.js +744 -26
  69. package/src/attachment-text.js +122 -0
  70. package/src/choro-command.js +9 -2
  71. package/src/config.js +171 -2
  72. package/src/continuity.js +7 -6
  73. package/src/diary/auto.js +190 -0
  74. package/src/diary/compose.js +440 -0
  75. package/src/diary/dir.js +59 -0
  76. package/src/diary/export.js +97 -0
  77. package/src/diary/index.js +26 -0
  78. package/src/diary/library.js +189 -0
  79. package/src/diary/qq-inbox.js +142 -0
  80. package/src/diary/rollover.js +61 -0
  81. package/src/diary/session-provider.js +122 -0
  82. package/src/document.js +9 -3
  83. package/src/dream/clustering.js +6 -1
  84. package/src/dream/decisions.js +56 -5
  85. package/src/dream/sleep.js +616 -15
  86. package/src/dream.js +45 -13
  87. package/src/dsh-go/host.js +162 -0
  88. package/src/dsh-go/panel.js +2091 -0
  89. package/src/dsh-go/text.js +342 -0
  90. package/src/dsh-go/views/common.js +68 -0
  91. package/src/dsh-go/views/entity.js +48 -0
  92. package/src/dsh-go/views/memory.js +198 -0
  93. package/src/dsh-go/views/root.js +30 -0
  94. package/src/dsh-go/views/settings.js +398 -0
  95. package/src/dsh-go/views/status.js +51 -0
  96. package/src/embedding.js +42 -8
  97. package/src/entities/extractor.js +36 -7
  98. package/src/hot-memory.js +8 -9
  99. package/src/index.js +131 -5
  100. package/src/inject.js +192 -19
  101. package/src/kb/index.js +309 -0
  102. package/src/kb/routes.js +220 -0
  103. package/src/kb/tools.js +148 -0
  104. package/src/lang.js +115 -4
  105. package/src/peak-hours.js +57 -2
  106. package/src/profile-facts.js +356 -0
  107. package/src/semantic.js +347 -60
  108. package/src/service.js +302 -70
  109. package/src/settings.js +117 -14
  110. package/src/store.js +830 -55
  111. package/src/summarize.js +38 -11
  112. package/src/tools.js +173 -25
  113. package/src/user-settings.js +175 -23
  114. package/src/vector-index.js +76 -6
  115. package/src/wiki/cli-hint.js +90 -0
  116. package/src/wiki/queries.js +264 -10
  117. package/src/wiki/t1-jobs.js +232 -0
  118. package/src/wiki/t1.js +7 -1
  119. package/test/api-routes-gaps.test.js +63 -0
  120. package/test/api.test.js +377 -4
  121. package/test/attachment-text.test.js +140 -0
  122. package/test/bm25-corpus-cache.test.js +145 -0
  123. package/test/capacity-warning.test.js +262 -0
  124. package/test/client.test.js +908 -138
  125. package/test/config.test.js +79 -0
  126. package/test/conflict-freeze.test.js +45 -1
  127. package/test/continuity.test.js +8 -1
  128. package/test/diary-library.test.js +187 -0
  129. package/test/diary.test.js +576 -0
  130. package/test/document.test.js +32 -0
  131. package/test/dream-maxtokens-fallback.test.js +3 -3
  132. package/test/dream.test.js +62 -1
  133. package/test/dsh-go-host.test.js +134 -0
  134. package/test/dsh-go-panel-actions.test.js +854 -0
  135. package/test/dsh-go-panel.test.js +251 -0
  136. package/test/embedding-cache-f32.test.js +276 -0
  137. package/test/embedding-ready.test.js +33 -3
  138. package/test/entities.test.js +73 -0
  139. package/test/forget-corrections.test.js +70 -0
  140. package/test/image-memory.test.js +171 -0
  141. package/test/inject-pin-explicit.test.js +300 -0
  142. package/test/inject-pin-vector-crowd.test.js +225 -0
  143. package/test/issue-617-step1-query-and-fusion.test.js +361 -0
  144. package/test/kb-api-wiring.test.js +128 -0
  145. package/test/kb-collections.test.js +272 -0
  146. package/test/kb-routes.test.js +379 -0
  147. package/test/mint-source.test.js +26 -0
  148. package/test/peak-hours-observability.test.js +70 -0
  149. package/test/persona-api.test.js +3 -3
  150. package/test/persona-by-session.test.js +568 -0
  151. package/test/persona-settings.test.js +38 -7
  152. package/test/profile-facts.test.js +492 -0
  153. package/test/reflection-update-gate.test.js +129 -0
  154. package/test/reindex-backfill.test.js +334 -0
  155. package/test/reindex-scheduler.test.js +369 -0
  156. package/test/scope-candidates.test.js +249 -0
  157. package/test/session-gate.test.js +1 -1
  158. package/test/sleep-action-set.test.js +14 -0
  159. package/test/sleep-heat.test.js +49 -0
  160. package/test/sleep.test.js +598 -0
  161. package/test/standalone-api.test.js +24 -0
  162. package/test/store.test.js +20 -0
  163. package/test/summarize.test.js +26 -0
  164. package/test/test-count-sync.test.js +14 -3
  165. package/test/tool-state-ignore.test.js +65 -0
  166. package/test/tools-dto-schema-matrix.test.js +23 -2
  167. package/test/tools.test.js +144 -7
  168. package/test/user-settings-segments.test.js +528 -0
  169. package/test/vector-index.test.js +120 -6
  170. package/test/wiki-api.test.js +476 -1
  171. package/test/wiki-cli-hint.test.js +118 -0
  172. package/test/wiki-queries.test.js +91 -1
  173. package/test/wiki-t1-jobs.test.js +125 -0
  174. package/test/wiki-tools.test.js +131 -76
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-2808%20passed-success)](https://github.com/slow-stack/mneme)
8
+ [![tests](https://img.shields.io/badge/tests-3262%20total-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)
@@ -49,7 +49,7 @@ dsh web
49
49
  - 逐 type 记录 `committed / failed / pending` 回执,健康端点区分 `ok / degraded / unknown`
50
50
  - 状态写失败不静默:同步失败落日志并留债务,重启自动收敛
51
51
 
52
- ### 模型工具(默认注册 11 个;开 `organizeEnabled` 后 12 个)
52
+ ### 模型工具(默认注册 12 个;开 `organizeEnabled` 后 13 个)
53
53
 
54
54
 
55
55
  | 工具 | 功能 |
@@ -62,11 +62,14 @@ dsh web
62
62
  | `memory_delete` | 删除记忆(按记忆 ID 精确删除) |
63
63
  | `memory_forget` | 抑制注入(降权不删除,可恢复) |
64
64
  | `memory_archive` | 归档/恢复记忆(v0.2.5;归档后隐藏于列表/搜索/注入/整理,`archived=false` 可恢复) |
65
+ | `memory_pin` | 钉住一条记忆:无论类型、无论它和其他记忆怎么竞争,每轮都注入在记忆块最前面(#249 第二批;受 `pinnedInjectBudget` 上限约束,钉的顺序是「先钉的占位」,预算不够时最早钉的优先)。**只在她明确说「这条必须一直在」时用**,不是标记重要性的手段——重要性本来就参与排序。钉着 ≠ 会注入:遗忘 / 归档 / 暂停作用域都优先于 pin,回执里的 `injectable` 如实报告这件事。`unpin: true` 松开 |
65
66
  | `set_scope_state` | 按下/松开某个作用域的**可逆闸门**(#471:政策轴。暂停期间该作用域的记忆照旧存着,只是不注入、不参与巩固,松开即原样回来;不改任何一条记忆的标注,与上面逐条操作的 `memory_forget` / `memory_archive` 是两根不同的轴)。两级开关 `scopeState.enabled` / `.enforce` 默认都关,没开时按下不产生拦截效果 |
66
67
  | `memory_register_document` | 登记 agent 产长文档的指针行(#230:摘要 + `doc_path` + evidence 落 `document` 类型;正文留在磁盘、由 agent 自管,注册口唯一,旁路写入被拒) |
67
68
  | `memory_organize` | 主动整理(#231,**要 `organizeEnabled` 打开才注册**):`mode=dryRun` 先出比对报告(每条候选 `exact` / `near` / `new` + 命中行,**不写一行库**、只留审计回执),agent 判断后 `mode=apply` 引用那份报告的 `run_id` 落地。两种「不要它」要分清——**`discard`** = 这条候选不写,什么都不动(只进回执);**`archive`** = 把报告标为被取代的那条**既有行**归档(可恢复,绝不物理删除)。没有比对过的候选写不进库,一份报告也只许落地一次 |
68
69
  | `memory_runtime` | 本地推理运行时的状态 / 供给 / 校验(v0.7.32,自管运行时三档取件) |
69
70
 
71
+ 开启 `chorographyEnabled` 后可用仓库地志工具。多个仓库已登记时请传仓库 ID。若搜索只读到部分页面,结果会标明漏查的页。
72
+
70
73
  ### 工作流(M.O.,v0.9 beta)
71
74
 
72
75
  记忆库答的是「这件事**为什么**这么办」,答不了「这件事**到底怎么跑**」。v0.9 把后者从记忆库里的一行文字变成项目里可提交、可 review、可被 CI 读的一份文件——一条工作流一个 JSON:
@@ -276,25 +279,28 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
276
279
  - **选择性注入**:query 向量可用时注入候选按主题相似度重排(`selectiveInjectEnabled` 可关);**搜索时语义去重**为激进选项(`searchSemanticDedup=true` 显式开启,近重复行 Rerank 前丢弃)
277
280
  - **召回基准**(`scripts/benchmark-recall.js`):标准查询集驱动,计算 Recall@5 与 MRR,`legacy`(三特性全关)vs `fused`(默认配置)双跑对比
278
281
 
279
- ## 📜 版本历史与路线图
282
+ ## 📜 版本历史
280
283
 
281
- > 完整版本说明见 [CHANGELOG](CHANGELOG.md) 与各版本 [GitHub Release](https://github.com/slow-stack/mneme/releases);当前特性以本 README 正文与[配置表](#-配置)为准。早期版本中的实验性功能(Wiki-Link、tag 系统等)已在 v0.7.11 移除,详见 CHANGELOG 对应条目。
284
+ 🧬 基因 → 🛡️ 审计加固 → 💤 睡眠巩固 → 🕸️ 召回融合与图谱 → 🌡️ 自进化 → 🔐 作用域隔离 → 🌐 MCP 生态 → 🧭 M.O. → 💭 情绪归因 → 📔 日记本
282
285
 
283
- 🧬 Gene → 🛡️ 审计加固 → 💤 Sleep Mode → 🕸️ 召回融合与图谱 → ✨ 面板体验 → 🌡️ 自进化记忆 → 🔐 作用域隔离 → 🌐 MCP 生态 → 🧭 M.O.(项目级工作流,v0.9 beta)
286
+ 当前版本的能力以本 README 正文的配置章节为准——**这张表只留一句话头条**,每一版具体怎么实现见 [CHANGELOG](CHANGELOG.md) 与 [Releases](https://github.com/slow-stack/mneme/releases)。
284
287
 
285
- | 版本 | 主题 |
288
+ | 版本 | 头条 |
286
289
  |------|------|
287
- | **v0.9.0**(2026-10-17) | M.O.(Modus Operandi)beta:工作流本体住 `<项目>/.mneme/pipelines/`、候选分层与升格判据、面板清单与图同屏(`actionPipelineEnabled` 默认关);设置面板折叠线挪到「新手之外」并按人话重写 22 个条目名(#427);安全与供应链加固(39 条路由声明式鉴权、OSV 有漏洞即失败、发布带 provenance) |
288
- | **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 正文改分层渲染 |
289
- | **v0.9.2**(2026-10-19) | 让「看不见的失败」显形:功能开关的第三态「待重启」把「面板显示已改 ≠ 运行时已改」摆到界面上(#481);作用域级可逆闸门让一路记忆能被单独按下与松开、不必改配置重启(#471,默认关);巩固用的 `dreamMaxTokens` 默认值撞服务商上限、八天 180 次全失败,面板现在能改、超上限还会自动降级重试(#478);CLI 把 `--` 开头的值当新旗标的真实缺陷(一次 1/4096 概率的 CI 红揪出来的)、人格卡删除 × 永远不可见、自动蒸馏写入的行 100% 拿不到向量(#477) |
290
- | **v0.9.3**(2026-10-20) | M.O. 从「挖得出来」到「会被叫去挖」:沉淀链算法自 v0.9.1 起就完整实现(多帧 zstd → 重复序列 → 缺席对照 → draft 草稿),运行时却没有任何调用点——机器挖得出来、却永远不会被叫去挖。这一版把归属与触发接上:按会话头 `cwd` 反查项目桶(桶名是路径的有损变形,查不到整跑跳过而不是降级成全局普查)、挂 `turn/end`、冷却 10 分钟、单次预算 4 秒、延到宏任务、一次一个项目、抛错只 `warn`,**只产提议不产草稿**(`actionNudgeEnabled`,beta,默认关);面板加「现在沉淀一次」,与自动跑同一条入口,区别只有它不改仓库;另修作用域反查自 v0.8.0 起每次都抛错走降级(#487)、`/nudge` 三处「没看全」被报成「确实没有」(#489 评审)、多帧 zstd 反证断言绑了 Node 整块解语义(#490),并把整个 M.O. 的「手法」改回「工作流」 |
291
- | **v0.9.4**(2026-10-21) | 三条链各自补上「最后一环」——东西早就造好了,只差没人能叫它:**agent 主动整理**(`memory_organize`,先 dryRun 出比对报告 → agent 判断 → apply 落库,筛除只归档不删、全程留审计回执;`organizeEnabled` 默认关,关时工具根本不注册);**画像提取草稿**(面板按钮读活跃记忆 + 一次模型调用,产一段「助手眼中的用户」草稿,`profileExtractEnabled` 默认关)——**只出草稿**,采用是用户再点一次,手写的画像原文不会被模型改写,空库与解析失败都直接报错、绝不回空草稿冒充「没提炼出东西」;**人格寻址收口**(`default` 卡以前无条件落给所有没建卡的身份,宿主里自带身份的 bot 每帧收到两句「你是谁」;`personaStrictAddressing` 默认关,把「没建卡」与「卡被显式关掉」都收回为不注入)。同版补蒸馏的**转录围栏 + 解析失败护栏**(#494:一条会话被同一窗口卡死、重试 20 次越试越贵的根因;失败行现在恒带原始输出形状),把面板自称「注入生效文本」的只读区改成后端单源渲染(并修它从不显示人格行),评审另收掉两处「坏了却不报」:整理报告侧的存在性探测口(`strictScope` 下按调用方可见性过滤落点并重算计数)、画像保存不判 `res.ok` 却提示已保存 |
292
- | **v0.9.5**(2026-10-30) | 记忆能安全地递给别人,也能整包收回来:**分享包 `mneme.share/v1`**(`dsh-mneme share` / `receive` / `undo-share`,面板「⋯」里同一批三件事)只含活跃行,scope 与 home 前缀不进包,命中密钥 / PII 的行整条不进;出包先给报告再给文件,超 3 MiB 水位回 413、按 `--type` / `--tag` 分批;入包一条不合格整包拒、干跑为默认、按 `share_external_id` 幂等,整包退出走物删。「本机巩固不许改写别人导入的原文」由单一谓词 `isImportedRow` 守在汇流口与九个行池上。同版给记忆来源加了读出口 `GET /memory/provenance`(#421,@z2Ace0107 实现;409 = 用户关了、503 = 装配坏了),修掉同标题重复入库(#505)、扫描谎报「没东西可扫」(#515)、五处未围栏的巩固 prompt(#501)与 dream 合并护栏整类误拦(#521),面板新增设置页分组目录(#518) |
293
- | **v0.10.0**(2026-11-02) | 归因与闸门——给已经发生的事补上它自己的身份:**蒸馏有了运行身份**(窗口级 `distill_runs` 记 `ok`/`degraded`/`noop`/`failed`/`aborted` 与 `audit_id`,行级 `memory_mints` 记「这条记忆出生自哪一轮巩固」;历史行留空**不猜**,失败轮也真记一行),此前归因只到「会话区间」——那是输入的坐标不是运行的身份;**溯源补上反方向** `GET /memory/provenance?sessionId=`(两个主键互斥,`limit` 越界 400 不静默截断,撞上限带 `truncated: true`);**会话级无痕闸门** `sessionGateEnabled`(指定会话与子代理不进不出,其余会话照常;子代理判据只认宿主委派标记,`parentSession` 不是判据——本机 641 个会话头实测会误判 3 个 fork 主会话);**防谄媚补到写入面**(蒸馏与总览加「立场纪律」四条与「叙述纪律」,排在与时间换算的指令之前)。另修 `differentiate` 正反馈闭环(注记改落 `differentiated_pairs` 账本、正文一个字节都改不了,实测单条曾堆 19 段)、sleep 冲突相位的输入侧预算与续排(分批后不再隔夜才轮到)、面板「最近巩固」区分 organize 回执(#495)、面板首次有移动端与 481–519px 中间带断点(#542) |
294
- | **v0.10.1**(2026-11-03) | 打开记忆详情不再崩掉整个宿主:0.10.0 里点开侧边栏任意一条记忆的详情抽屉,渲染进程立刻抛 `ReferenceError: provenance is not defined`,整个宿主跟着重启、进行中的会话被打断,100% 复现(#553)。根因是两个隔了三千多行的组件——`MemoryDrawer` 的归因 effect 与渲染块在读 `provenance` / `setProvenance`,而那句 `useState` 落在了同文件**上一个**组件 `EntityPanel` 里,在那里是纯死代码(整个函数体除声明外对 `provenance` 零引用)。这个形状躲得过眼睛、语法检查与原有用例,所以补的不是「锁一个名字」的用例,而是按组件扫「调了 `setXxx(` 却没有声明」。同版带上 v0.10.0 之后合入的图谱锚定层收尾(#24):Tag 与向量两路起点接线(`normalizePath` 补回通路身份、`findEntitiesByTags` 走全等匹配并按 500 分块、反查改 UNION 三源以覆盖只看关系不建 attr 行的记忆、向量路复用同一份 query 向量并滤掉 0 分种子),以及被动确认的复核出口(`GET /semantic/graph/boosted-relations` 按「合法抬升量 > 0」列出、ego 边补 `weight`/`source`、面板默认收起的审计视角) |
295
- | **v0.11.0**(2026-11-05) | 仓库地志(chorography)落地并接到消费面:读一个 JS/TS 仓库,把文件树与 import 图编译成模块划分与 `.chorography/` 下的 Markdown 地志页——T0 骨架全程零模型零网络、`plan.yaml` 正面控制、`CHORO:HUMAN` 保留区跨重生成逐字节存活;T1 逐模块一次 headless 会话补架构/约定/技术栈/构建四段语义正文并落可核验的 claims(三重上限、编造锚点整轮拒)。消费面三张脸:面板第二座位 `dsh-mneme-wiki`、三个只读工具、`/choro/*` 读路由,外加每轮开局一小块标题级提纲注入(`chorographyInject` = light/full/off)与只读 `/choro` 命令(过期只提示、绝不自动重跑)。`chorographyEnabled` opt-in 默认关。wiki 行与记忆行物理隔离,永不进巩固/质量过滤任何行池。另修 #573:宽态侧栏的记忆入口落在「更多」行下面、被展开的菜单盖住 |
296
-
297
- | **v0.8.x** | 作用域隔离(agent / workspace 双维 + opt-in 硬过滤)、冲突队列人工裁决、生态化(stdio MCP server / 图召回轴 / 冷启动 / 注入截断) |
290
+ | **v0.13.0**(2026-11-10) | 情绪终于有地方可放:`affective_context` 带情绪词与归因,三个独立会话才升成稳定倾向,不足就降级不丢弃 |
291
+ | **v0.12.0**(2026-11-07) | pin 从「按类型猜」变成你亲手钉的事实;向量缓存从恒 0% 命中改为跟工作集自适应(1.8–2.4s → 370–424ms) |
292
+ | **v0.11.0**(2026-11-05) | 记忆读得懂仓库:文件树与 import 图编译成模块划分与地志页 |
293
+ | **v0.10.1**(2026-11-03) | 打开记忆详情不再崩掉整个宿主 |
294
+ | **v0.10.0**(2026-11-02) | 记忆开始按「哪一轮」算账:蒸馏有了运行身份,溯源补上反方向 |
295
+ | **v0.9.5**(2026-10-30) | 记忆能安全地递出去,也能整包收回来 |
296
+ | **v0.9.4**(2026-10-21) | 三条链各自补上「最后一环」:主动整理、画像提取草稿、人格寻址收口 |
297
+ | **v0.9.3**(2026-10-20) | M.O. 从「挖得出来」跑到「会被叫去挖」 |
298
+ | **v0.9.2**(2026-10-19) | 让「看不见的失败」显形 |
299
+ | **v0.9.1**(2026-10-18) | M.O. 从「看得见」跑到「跑得出账」 |
300
+ | **v0.9.0**(2026-10-17) | M.O.(工作流)进入 beta:工作流住进项目仓库、可提交可 diff |
301
+ | **v0.8.x** | 作用域隔离(agent / workspace 双维)、冲突队列人工裁决、stdio MCP server、图召回轴、冷启动 |
302
+
303
+ > 早期版本中的实验性功能(Wiki-Link、tag 系统等)已在 v0.7.11 移除,详见 CHANGELOG 对应条目。
298
304
 
299
305
  ## 📦 安装
300
306
 
@@ -377,6 +383,31 @@ dsh plugin --profile web add "C:/path/to/modusensus-dsh-mneme-0.9.0.tgz"
377
383
  dreamDelayMs: 2000
378
384
  ```
379
385
 
386
+ ## 怎么升级(设置不会丢)
387
+
388
+ **升级只动代码,不动你的记忆和设置。**
389
+
390
+ 设置与记忆是**同一个 SQLite 文件**里的表(`user_settings` 与 `memories` 等),文件位置由 `memoryDir` 决定,默认在 `~/.dsh/memory`——**不在插件安装目录下**。所以卸插件、装插件都不碰它。
391
+
392
+ 升级就是覆盖安装,装完重启一次:
393
+
394
+ ```bash
395
+ # --profile 后面写你当初安装它时用的那个名字,默认是 web
396
+ dsh plugin --profile web remove @modusensus/dsh-mneme
397
+ dsh plugin --profile web add @modusensus/dsh-mneme
398
+ dsh web
399
+ ```
400
+
401
+ > 上面是 npm 来源的装法;当初从 Git、tarball 或本地目录装的,按下同一种来源重装即可。唯一要求是**先 remove 再 add**。
402
+ > 不用重装 DSH。重装 DSH 动的是 profile,是更大范围的改动,与插件版本无关。
403
+ > 也不要只 `add` 不 `remove`——宿主可能拿到缓存的旧副本,版本号没变就说明没换成。
404
+
405
+ **这件事是实测过的,不是推断**:用 v0.12.0 的代码建库、写入画像、规则、人格卡与四个功能开关,再换成本次发布的代码打开同一个库——四项全部原样读回。反向也测了:新代码写的库,v0.12.0 同样读得出来。
406
+
407
+ **但有两处不对称要提前知道**:人格卡的按会话分配(`bySession`)以及 `scenario` / `mesExample` 两个字段,v0.12.0 都不认识——用旧版打开新版写的库,这些键读不出来,旧版再写回时可能把它们覆盖掉。所以**回退前先备份整个记忆库目录**(复制 `memoryDir`,默认 `~/.dsh/memory`),需要回退时把库一起退。另外,撤销某个开关只影响之后的行为,不保证把已经写下的数据(例如已经铸出的情绪倾向)一并回滚——要真正清掉得手动删对应条目。
408
+
409
+ 唯一会让你丢数据的是**把库指进 profile 作用域**(形如 `…/profiles/<名字>/…`),宿主清理 profile 时会连带带走——把 `memoryDir` 保持在 profile 之外即可(默认就是),要迁移库则:停止写入 → 复制整个目录 → 改配置指向新位置 → 重启。
410
+
380
411
  ## ⚙️ 配置
381
412
 
382
413
  | 键 | 默认值 | 说明 |
@@ -514,6 +545,7 @@ dsh plugin --profile web add "C:/path/to/modusensus-dsh-mneme-0.9.0.tgz"
514
545
  | `writeAdmission` | `{enabled:false, enforce:false}` | 写入准入(issue #254,默认关):把「这条该不该进库」前移到 LLM 之前。第 1 级是零 LLM 的确定性判据——空白 / 纯噪声 + 密钥 / PII(判据来源是 #164 A2,经 `sensitiveScan` 注入,本批只定义接口)。`enabled` 跑判据并落审计行(`llm_audit_logs` 的 `metadata.deny`),`enforce` 才真的拒(`store.save` 之前返回,`memory_save` 返回 `action:"denied"` + `reason`);只开 `enabled` = 仅告警、写入不拦。去重键命中不进第 1 级(归 write-update 放行),G1/G2 阈值只计量。也走 feature_flags 白名单 |
515
546
  | `preInjectGate` | `{enabled:false, enforce:false}` | 注入前判定(issue #380,默认关):每帧注入候选出池后、进入 system prompt 前,一次池级 LLM 调用判出「会向本次请求注入意见/立场」的记忆(判定协议与 E12 实验同源;实测过滤把谄媚 26.9%→2.5%,且判定结果绝不进模型上下文——给模型看标记的线已被 E2/E3 关闭)。`enabled` 跑判定并落审计行(真实 token 用量 + 被标记记忆 id),`enforce` 才在缓存命中帧真滤除;冷缓存首帧降级为原样注入(宿主注入渲染是同步回调,判定走 prefetch+cache)。pin 池与检索线索行豁免;判定失败原样注入不阻塞;整池被标记时放弃本次滤除(fail-open);判定连续失败 3 次熔断一个冷却期;无默认路由时审计行 `model_id` 落 `unknown` 占位;`llmAudit` 关闭时本闸不运行;lightMode 强制关。也走 feature_flags 白名单 |
516
547
  | `distillRecords` | `{enabled:false, retentionDays:90}` | 蒸馏原文留存 + 记忆级溯源(issue #399,默认关):把蒸馏从「替换」降级为「叠加索引」——蒸过的窗口原文旁路落 `distill_records`(一个事件一行,主键 `(session_id, seq)`,`INSERT OR IGNORE` 幂等),每条新记忆落 `memory_provenance` 回指来源窗口(`(memory_id, session_id, seq_start, seq_end)` 唯一)。**渲染与落盘共用同一隐私谓词**:私有推理链、插件注入的机器内容(AGENTS.md / skill 正文 / 文件变更通知)两条路都不进;工具调用入参与结果、code 输出**只进渲染、不落盘**(渲染是 `codingRetrospect` 的输入,落盘只留对话)。留存文本**不受** `distillMaxChars` / 工具裁剪约束(原文就是原文)。到期裁剪只删 `distill_records`,溯源链接保留指向空区间(面板显示「来源已过期」),**绝不级联删记忆**;删记忆时显式清链接。无历史回填、从未蒸馏的窗口不留记录。进 `dsh-mneme reclaim` 报告第三项;lightMode 强制关。也走 feature_flags 白名单 |
548
+ | `capacityWarning` | `{dbSizeWarnMb:256, archivedRowsWarn:5000, archivedRatioWarn:0.95}` | 容量预警(issue #275,空间维度):库体积 / 归档区规模过线时在 `GET /api/dsh-mneme/health` 的 `capacity` 块与面板「库体积」卡上报。三道阈值都是**观察口**,不是动作触发——没有周期性消费者,预警也永不自动归档 / 删除(真删由人显式确认),出口是维护卡的无损回收(`dsh-mneme reclaim`)。阈值判 `>=` 过线、**0 = 关掉该条检查**;判级 fail-safe(读失败显式 unknown,绝不伪装 ok)。默认值锚定 #275 议题的形态区间(活跃数百 / 归档数千;占比健康区就在 0.8~0.9,故 0.95 起才算过线)。答复只带数字、不带库文件路径;归档净增速率是 recall-stats `archive` 块的第五指标,不在这里重复。也走 feature_flags 白名单(改动启动快照生效,面板显示「待重启」) |
517
549
  | `conflictFreezeEnabled` | `false` | 冲突冻结(v0.4.4):dream 发现矛盾对不自动裁决,冻结进冲突队列;状态页「冲突队列」支持并排对比与人工确认(保留 A / 保留 B / 仅标记已处理,v0.8.0) |
518
550
  | `conflictFreezeMaxPending` | `100` | 冲突冻结队列最大挂起数(1-1000):超出后不再入队(防队列无限膨胀) |
519
551
  | `escapePromptVariables` | `true` | 注入文本花括号转义(v0.8.0 恢复,issue #162):注入边界把 `{{...}}` 转义,防止 hot memory / 记忆原文里的 Go template / Vue 语法触发宿主 interpolate 抛错卡死会话(v0.7.4 曾修复、v0.7.11 误删) |
@@ -692,7 +724,7 @@ Claude Code 挂载示例(项目根 `.mcp.json`;token 在面板「设置 →
692
724
  │ 服务层:saveWithDedupe / injectCandidates │
693
725
  │ / mergeHumanEdits / onWrite 钩子 │
694
726
  ├─────────────────────────────────────────────────┤
695
- │ 模型接口:11 个工具 + 自动注入 + 会话摘要 │
727
+ │ 模型接口:12 个工具 + 自动注入 + 会话摘要 │
696
728
  ├─────────────────────────────────────────────────┤
697
729
  │ autoDream:阈值调度 → LLM 决策清单 │
698
730
  │ → 校验(fail-safe)→ 应用 → 摘要 │
@@ -710,7 +742,7 @@ src/
710
742
  ├── service.js # 领域逻辑(去重合并、注入筛选、写入钩子、scope 加权/硬过滤、冲突队列)
711
743
  ├── scope.js # 作用域解析(agentPreset + workspace registry 反查,issue #17)
712
744
  ├── config.js # schemastery 配置 schema
713
- ├── tools.js # 11 个模型工具(defineTool;memory_organize 要 organizeEnabled 打开才注册,开了是 12 个)
745
+ ├── tools.js # 12 个模型工具(defineTool;memory_organize 要 organizeEnabled 打开才注册,开了是 13 个)
714
746
  ├── user-settings.js # 「用户设置」注入块(人格 → 画像 → 规则)的唯一渲染处,注入与面板预览共用
715
747
  ├── profile-extract.js # 从活跃记忆提取「助手眼中的用户」画像草稿(只出草稿,不写 user_settings)
716
748
  ├── organize.js # agent 主动整理:dryRun 比对报告 → agent 判断 → apply 落库(筛除=归档不删,留 dream_runs 回执)
@@ -729,7 +761,7 @@ src/
729
761
  ├── api.js # HTTP 路由(Web 面板数据通道,含 /conflicts 冲突队列)
730
762
  └── index.js # 插件接线
731
763
  lib/ # src 的同步分发产物(npm run sync,prepack 钩子在发布时自动跑;一致性由 scripts/check-sync.js 在 CI 与 release.yml 校验——它只查不同步;唯一手写例外 lib/client.js——Web 面板 bundle,sync 不覆盖)
732
- test/ # 2808 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验)
764
+ test/ # 3262 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验)
733
765
  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 运行时清单
734
766
  ```
735
767
 
@@ -738,7 +770,7 @@ scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压
738
770
  ```bash
739
771
  cd dsh-mneme
740
772
  npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试)
741
- npm test # 运行 2808 个测试
773
+ npm test # 运行 3262 个测试
742
774
  npm run stress # 三轴线压测:长会话检索 / 冲突仲裁 / 多 Agent 并发(离线 mock LLM)
743
775
  npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行)
744
776
  ```
@@ -760,6 +792,7 @@ npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动
760
792
  - [连续性抢救(#249 N3):触发器口径与事件契约](docs/CONTINUITY.md)
761
793
  - [Sleep Mode:空闲四阶段深度维护](docs/SLEEP.md)
762
794
  - [存储与回收:表结构、保留期、`dsh-mneme reclaim`](docs/STORAGE.md)
795
+ - [选择性遗忘与事实更正:行粒度契约与失效关系缺口(#532)](docs/FORGET.md)
763
796
  - [记忆分享包:出包与收包的口径、信任边界与实测(`dsh-mneme share` / `receive` / `undo-share`)](docs/SHARE.md)
764
797
  - [独立服务 daemon(`dsh-mneme-serve`)](docs/DAEMON.md)
765
798
  - [与 Zep / Graphiti / Mem0 / Letta / Hindsight 的能力对比](../docs/COMPARISON.md)
@@ -18,6 +18,8 @@
18
18
  那里还会单独列出「白名单登记了、但 schema 没给默认值」的键——这类键在面板上有输入框,
19
19
  留空时却不会进「当前生效值」。
20
20
 
21
+ 路径键改完落点,见 [DIRECTORIES.md](DIRECTORIES.md)(注意各路径键的 `~` 展开规则不一致)。
22
+
21
23
  ## 全局与存储
22
24
 
23
25
  | 键 | 默认 | 作用 | 开启后果 / 冲突 |
@@ -26,6 +28,18 @@
26
28
  | `memoryDir` | `~/.dsh/memory` | SQLite 记忆库目录 | WAL + busy_timeout 已为多进程就绪;两个宿主共用库时 `externalApiEnabled` 与 `autoDream` 只能一边开 |
27
29
  | `lightMode` | `false` | 轻量档总闸:一键关掉全部重型路径,保留核心循环(见文末清单) | 面板 `panel_mode=light` 等效为 `true` 且优先于 bundle 配置 |
28
30
 
31
+ ## 容量预警(#275,空间维度)
32
+
33
+ 库体积 / 归档区规模过线时在 `GET /api/dsh-mneme/health` 的 `capacity` 块与面板「库体积」卡上报。三道阈值都是**观察口**,不是动作触发:没有周期性消费者(`/health` 被读到才算一次),预警也永不自动归档 / 删除——真删由人显式确认(#275 拍板),出口是维护卡的无损回收(`dsh-mneme reclaim`,见 [STORAGE.md](STORAGE.md))。判级实现 `service.getCapacityHealth`,与镜像健康 fail-safe 同款:读失败显式 `status:"unknown"`,绝不伪装 ok。
34
+
35
+ | 键 | 默认 | 作用 | 开启后果 / 冲突 |
36
+ |---|---|---|---|
37
+ | `capacityWarning.dbSizeWarnMb` | `256` | 库磁盘足迹(主文件 + WAL + SHM)预警线,整数 MB | **0 = 关掉这条检查**。默认锚定议题实测:55 MB 库 VACUUM 0.8s,256 MB 起代价才值得先看数字再动手(议题「现在便宜、以后贵」) |
38
+ | `capacityWarning.archivedRowsWarn` | `5000` | 归档区行数预警线 | **0 = 关**。锚定议题形态区间「归档是数千条这个量级」的上沿(实测 2,400 时还没坏) |
39
+ | `capacityWarning.archivedRatioWarn` | `0.95` | 归档占比(归档 / 全部)预警线,浮点 0–1 | **0 = 关**。健康形态本身就落在 0.8~0.9(活跃数百 / 归档数千),0.9 会对着健康库报警;0.95 起才是「归档区吞掉活跃区」的形态 |
40
+
41
+ 三个键都走 feature_flags 白名单(面板可改,改动是**启动快照**,与 `panel_mode` 同节奏,面板显示「待重启」);两个整数走 INT_RANGES、占比浮点走 NUMBER_RANGES。不设 `enabled` 总开关:数字恒在 `/health`,把某条检查关掉就是把对应阈值设 0。`:memory:` 库没有磁盘足迹,`db_size` 检查缺席(不是 ok);答复只带数字、不带库文件路径。归档增速(净增速率)是 recall-stats `archive` 块的第五指标,不在这里重复。
42
+
29
43
  ## 逐会话无痕(issue #539)
30
44
 
31
45
  命中会话后,Mneme 不向该会话注入记忆、用户设置或工具指引,不接受其 `memory_*` / `set_scope_state` 工具调用,不对它蒸馏,也不由压缩抢救、M.O. 记账或自动提议路径保存该会话的内容。
@@ -114,7 +128,7 @@
114
128
  | `summarizeMinWindowChars` | `0` | 窗口可蒸馏文本下限(字符),不足直接跳过 LLM(#239) | 被挡窗口照常消费游标并留 skip 审计 |
115
129
  | `summarizeMaxRunsPerSession` | `0` | 每会话蒸馏次数预算,只计真实发起的调用(≤1000) | 0 = 不限;进程内计数,重启清零 |
116
130
  | `summarizeParseFailureMaxRetries` | `0` | 连续解析失败几次后放弃当前窗口(0–10,0 = 关,#493) | 达到阈值强制消费游标跳过该窗口;失败行 metadata 恒带 `raw_head`/`raw_has_bracket`/`streak` |
117
- | `summarizePeakHours` | `""` | 高峰时段串(`"09:00-18:00"` / `"mon-fri 08:00-12:00,14:00-18:00"`,支持跨零点) | 命中高峰不调 LLM、顺延补跑;任一写法非法整串按未配置处理 |
131
+ | `summarizePeakHours` | `""` | 高峰时段串(`"09:00-18:00"` / `"mon-fri 08:00-12:00,14:00-18:00"`,支持跨零点;全角逗号/冒号/数字自动归一,#424) | 命中高峰不调 LLM、顺延补跑;任一写法非法整串按未配置处理。**非法不静默(#622)**:非空且解析失败时启动日志 warn 一条(带键名与原串)、面板该键输入行下出红色提示(`/features` 的 `invalidPeakHours`);空串是合法的未配置,不告警 |
118
132
  | `summarizePeakMaxDeferMinutes` | `120` | 高峰顺延上限(分钟,≤1440) | 0 = 不设上限;到点仍处高峰照跑(bypassPeak) |
119
133
  | `summarizeDedupeMode` | `off` | 落库前去重档位:`off` / `title`(拦完全同名)/ `vector`(同会话语义近邻) | `vector` 复用已有 embedding 列,无 LLM 调用 |
120
134
  | `summarizeDedupeMinSim` | `0.92` | `vector` 档并入阈值(0.5–0.99) | 仅 `vector` 档生效 |
@@ -168,6 +182,7 @@
168
182
  | `personaEnabled` | `false` | 人格段:用户自写的一小段常驻注入文本 | opt-in;只是轻量常驻文本、不引入重型后台路径,故不进 `LIGHT_MODE_OFF` |
169
183
  | `personaStrictAddressing` | `false` | 人格寻址口径。关(历史行为):会话身份没建卡、或该卡被显式关掉时,落 `default` 卡 | 开:这两种情况都不注入人格,`default` 只兜「解析不到会话身份」的无名会话。用途是宿主里跑着自带身份的外部 bot 时,别让某个人格无条件进它每一帧。默认关 = 升级零变化 |
170
184
  | `profileExtractEnabled` **light** | `false` | 画像提取草稿:`POST /api/dsh-mneme/profile/extract` 从活跃记忆反向写一段「助手眼中的用户」草稿(面板「Agent 眼中的你」卡上的按钮) | opt-in;一次调用一轮 LLM + 读上百行记忆,故 `LIGHT_MODE_OFF` 里有它。**只出草稿副本**:它不写 `user_settings`,面板也只把结果当副本展示、可一键复制(2026-11-04 改口径,此前「采用为画像」会直接覆盖手写画像且无撤销)——画像正文必须是用户自己手写的原文。空库直接报 `no-input`、模型没按格式回报 `unparseable`,两者都不回空草稿 |
185
+ | `profileFactsEnabled` **light** | `false` | 自动画像事实:「Agent 眼中的你」按会话身份分卡维护一份逐条事实清单(对标 ChatGPT 记忆 / DS Bot 每 bot 的 MEMORY.md)——dream 巩固轮成功后逐卡增量维护(有新记忆才跑),注入在手填画像之后;面板可手动触发与逐条增改归档,账本逐 op 可查 | opt-in;自动维护轮是 dream 级 LLM 调用 + 读上百行记忆,故 `LIGHT_MODE_OFF` 里有它(注入段本身是几十行常驻文本,关掉省的是维护调用不是注入)。事实住 `profile_facts` 独立三表,**绝不写 `user_settings`**(#497 口径不变:手填画像是用户原文,模型只有读权)。输入行严格按 `agent_scope` 分区、排除外来行;账本 `profile_fact_journal` 每卡裁剪到最近 200 条 |
171
186
 
172
187
  ## 巩固(autoDream)
173
188
 
@@ -178,7 +193,8 @@
178
193
  | `dreamDelayMs` | `2000` | 触发后的延迟(毫秒) | — |
179
194
  | `dreamMinIntervalMinutes` | `0` | 两次开跑最小间隔(分钟,≤10080,#89) | 失败/degraded run 也占用;0 = 不限 |
180
195
  | `autoDreamFailureBackoff` | `false` | 连续失败指数退避:有效间隔 = 基数 × 2^连败,封顶 30 分钟(#292) | opt-in;基数取 `dreamMinIntervalMinutes`(0 = 无闸可翻倍);成功一次清零;重启归零 |
181
- | `dreamPeakHours` | `""` | 巩固错峰,与 `summarizePeakHours` 同一份时段语法(#239) | 命中高峰不做梦、阈值继续累积、留 skip 审计;非法写法按未配置 |
196
+ | `dreamInputSummaryEnabled` | `false` | dream 审计行输入快照瘦身(#557):auto 轮只落骨架(id/type/title/importance/updated_at,不含 content) | opt-in;LLM 输入与 `snapshot_hash` 都不受影响;organize 轮重放载荷不受影响;开启后审计行无法离线重算 hash(骨架级回放) |
197
+ | `dreamPeakHours` | `""` | 巩固错峰,与 `summarizePeakHours` 同一份时段语法(#239;全角逗号/冒号/数字同样归一,#424) | 命中高峰不做梦、阈值继续累积、留 skip 审计;非法写法按未配置,且同 `summarizePeakHours` 的 #622 可见性(启动 warn + 面板提示) |
182
198
  | `dreamPeakMaxDeferMinutes` | `120` | 巩固顺延上限(分钟) | 0 = 不设上限;到点仍处高峰照跑 |
183
199
  | `dreamProvider` / `dreamModel` | 空 | 巩固专用路由(settings「巩固模型」) | 建议非思考模型——思考模型易烧光预算返回空体(#135) |
184
200
  | `dreamMaxTokens` | `131072` | 巩固输出预算(256–131072) | 流式计费按实际用量,调大不增加成本;**调小则相反**——撞顶 → 无完整 JSON → 整轮失败 → baseline 不刷新 → 同输入重跑(#135 实测 cap=16384 净成本 +54%) |
@@ -203,6 +219,19 @@
203
219
  | `documentDir` | `""` | managed 落盘目录(#296) | 空 = `<memoryDir>/documents/`;目录之外只登记指针、正文零读零写 |
204
220
  | `policyEpoch` | `0` | 裁决规则版本号 | bump 后旧 dream_runs 降为历史证据(receipt 不再驱动现行决策) |
205
221
 
222
+ ## 日记本(diary,v0.13)
223
+
224
+ | 键 | 默认 | 作用 | 开启后果 / 冲突 |
225
+ |---|---|---|---|
226
+ | `diaryEnabled` **light** | `false` | QQ 日记本总闸:跨天判定 + 手写入口 + 面板页 | opt-in;与 `documentMemoryEnabled` **成对但不相依**——关着也能写正文文件,只是不铸造 `document` 指针行(回执里如实标 `pointer.skipped`) |
227
+ | `diaryDir` | `""` | 日记正文根目录 | 空 = `<memoryDir>/diaries/<项目键>/`;`~` 展开到 home,绝对路径原样,相对路径落 memoryDir 下。**与 `memoryDir` 一样是路径配置,不是行为开关**,故不进 `settings.js` 白名单、也不上面板 |
228
+ | `diaryWriteEveryMinutes` | `0` | 日记自动写的定时唤醒间隔(分钟) | `0` = 只在 turn/end 与插件启动时判定(此前行为);`>0` 时每隔这么多分钟扫一遍已知项目再判定。**不是 cron**:定时器只负责唤醒「该不该写」的判定,写不写仍由 compose 按落盘日期去重——一天最多一篇,填 5 也不会多写。跑完一篇再排下一次,所以不会与上一次的模型调用重叠。opt-in、可热改(走 `INT_RANGES` 表,0–1440)|
229
+ | `diaryInboxDir` | `""` | QQ 入站素材目录(只读) | 空 = 从 `memoryDir` 推 `<memoryDir>/../media/onebot`(onebot 的默认落点)。本模块**不 import `dsh-onebot-qq` 任何模块**,边界只靠这份「目录 + 文件形状」约定 |
230
+
231
+ 日记不落用户绑定的项目目录:那目录实测非 git、无 `.gitignore`、混着探针脚本,
232
+ 写进去没有版本也没有回滚;mneme 成文的写盘面只有 `memoryDir` 与 `documentDir` 两处。
233
+ 「入库到项目」是面板上一个**人主动点**的导出动作(复制而非移动)。
234
+
206
235
  ## 睡眠模式(sleep,空闲深维护)
207
236
 
208
237
  | 键 | 默认 | 作用 | 开启后果 / 冲突 |
@@ -216,6 +245,9 @@
216
245
  | `sleepPatternMinMemories` | `100` | 模式发现的扫描窗口(条) | — |
217
246
  | `sleepPatternLookbackDays` | `30` | 属性变更回看(天) | — |
218
247
  | `sleepMaxPatternPerRun` | `3` | 单轮模式条上限(0–10) | 0 = 关闭模式发现 |
248
+ | `sleepAffectiveEnabled` **light** | `false` | 情感归因相位(#619):情境情绪写成 `affective_context` 关系,跨多次独立会话的稳定倾向才铸成 pattern | opt-in;`sleepModeEnabled` 的子相,父关着不跑;要调模型且依赖实体抽取 |
249
+ | `sleepAffectiveTraitMinSessions` | `3` | 判「稳定倾向」所需的**独立会话**数(2–10) | 下界 2 是故意的:N=1 就是单次场合,正是必须禁止升特质的那个情形 |
250
+ | `sleepAffectiveMaxRelationsPerRun` | `10` | 单轮最多写多少条 `affective_context` 关系(0–50) | 同 `sleepMaxPatternPerRun` 的角色;0 = 一条也不写 |
219
251
  | `sleepProvider` / `sleepModel` | 空 | sleep 专用路由 | 空 = 用巩固路由 / 当前模型 |
220
252
  | `sleepReasoningEffort` | 未配置 | 同 `dreamReasoningEffort` | — |
221
253
  | `sleepMaxTokens` | `8192` | 冲突/模式阶段输出预算(#257) | 原硬编码 2048 对 `full` 档必然截断 |
@@ -235,6 +267,8 @@
235
267
  | `reflectionFailureRetentionDays` | `90` | `failure_memories`(失败/纠正记录)的启动期清理保留天数(1–3650) | 只读 bundle 配置:未进 feature_flags 白名单,面板改不了。`dsh-mneme reclaim` 不读这个键(它对三个表用同一条 `--older-than`,本表不在其中)。此前这个 90 天是写死的(#275 伞下的欠账) |
236
268
  | `conflictFreezeEnabled` | `false` | 冲突不自动合并、冻结待人工复核 | opt-in |
237
269
  | `conflictFreezeMaxPending` | `100` | 冻结队列上限(≤1000) | — |
270
+ | `conflictAutoResolveEnabled` | `false` | 相似度极高的**同 scope** 重复对自动裁决(保留新条、归档旧条),不进人工队列 | opt-in。会归档记忆,所以默认关;跨 scope 对永不自动(归属是用户的决定,#170)。用 supersede 而非 conflict:注记写在被归档侧,幸存记忆正文不动 |
271
+ | `conflictAutoResolveMinSimilarity` | `0.95` | 自动裁决的相似度门槛(0.85–1) | 实测 live 库 345 条有相似度的历史行里 ≥0.95 只占 5.8%、≥0.94 占 8.1%——这一档减的是峰值,不是把队列清零。低于 0.85 就不是「重复」而是「相似」,不该自动归档任何一条 |
238
272
 
239
273
  ## 实体
240
274
 
@@ -317,10 +351,15 @@
317
351
  | `organizeEnabled` | `false` | 对模型开放 `memory_organize` 工具(#231:先 dryRun 出比对报告,agent 判断后 apply 落库) | 与上面两个**反向**:那两个藏默认在场的工具,本键开默认不在场的能力。关 = 工具根本不注册(模型看不到就不会调)。dryRun→apply 是两步协议,弱模型容易漏 `run_id` 或跳过报告,所以 `LIGHT_MODE_OFF` 里有它;apply 只能引用一次真实报告、且一份报告只许落地一次 |
318
352
  | `chorographyEnabled` | `false` | chorography(仓库地志,spec 见 docs/specs/)暴露闸:对模型开放三个只读工具(`choro_search` / `choro_read_page` / `choro_module_map`)+ 面板「仓库地志」浏览页 + 每轮的开局提纲注入 + `/choro` 命令 | 与 `organizeEnabled` 同款反向语义:关 = 工具根本不注册、面板页空态、注入块不渲染、命令不注册,行为与旧版逐字节一致。本键是「是否启用这个子系统」,不是「每轮付多少」——所以它仍**不进 `LIGHT_MODE_OFF`**,轻量档要压的是下面那条 `chorographyInject`。检索是关键词子串(M1);生成走 `dsh-mneme-choro` CLI(T0 确定性骨架 + `--t1` headless 语义富化) |
319
353
  | `chorographyInject` | `"light"` | 开局注入档位:`light` 只列文件数最多的 6 个模块(约 8 行)、`full` 列全部模块、`off` 不注入 | 只注模块名 / 文件数 / 页状态 / 页相对路径,**不含正文**(正文由模型 `choro_read_page` 取);注入块折进既有 memory context,不新开 context(spec §7「合并成一次拼装」)。`lightMode: true` 时在消费侧压成 `off`(见 `wiki/inject.js` 的 `resolveInjectTier`)——它**不进 `LIGHT_MODE_OFF` 表**,因为那张表对平铺键一律赋 `false`,枚举值塞进去会与 schema 不符 |
354
+ | `chorographyAutoRefresh` | `false` | 重建骨架后是否顺手把「过期」标记也刷掉:`POST /choro/run` 带 `refresh: true` 时,面板发起的一次重建会同时重算 `choro_repos` 的 git 基线与 plan 指纹 | opt-in 默认关。它与 `chorographyEnabled` 同属「谁按下按钮」而非「每轮付多少」,故不进 `LIGHT_MODE_OFF`;关着时重建照常跑,只是过期判据留旧基线,面板会继续显示「代码已变更」 |
355
+ | `chorographyT1FromPanel` | `true` | 面板「补语义正文(T1)」按钮的暴露闸:开 = 地志页能发起 `POST /choro/t1`,后台 spawn 一次 `dsh-mneme-choro --t1 --apply` 并轮询 `/choro/t1/status` | 默认**开**(只在 `chorographyEnabled` 已开时才有意义;关整个子系统仍然全灭)。它管的是「面板能不能点这一下」,T1 花的是模型额度但每次都是人的显式动作,所以不设第二道默认关;T1 有模块数/时长/重试三重上限(`src/wiki/t1.js`) |
356
+ | `kbEnabled` | `false` | 知识库第二层(AI-Native 知识库,对标 Qoder Knowledge)暴露闸:对模型开放三个只读工具(`kb_list_collections` / `kb_search` / `kb_read_doc`)+ 面板「知识库」页 + `/api/dsh-mneme/kb/*` 路由 | 与 `chorographyEnabled` 同款反向语义:关 = 工具不注册、面板页空态、路由 409。知识库收的是外来原文副本,真源在 `kb_*` 三张表(不是磁盘 MD),**只碰 kb_* 绝不写 `memories`**、不进巩固/质量过滤任何池子。检索是 BM25 + 可选向量;单文档上限 2 MB,超限/不可读落 `status='error'` 行并回人话,不静默截断。不进 `LIGHT_MODE_OFF`:它没有每轮注入物,模型只在被问到时才调用 |
320
357
  | `actionPipelineEnabled` | `false` | M.O. 只读视图的门控:做法图谱 + 对话行结算卡 | opt-in beta;只是纯 UI 入口、不引入重型后台路径,故不进 `LIGHT_MODE_OFF`。写账另有独立的执行记账键(`/mo-run`),看图 ≠ 写账 |
321
358
  | `actionRunEnabled` | `false` | M.O. 执行记账:`/mo-run` 开一次执行窗口,窗口内命令执行才可能被记成一条 run | opt-in beta;挂在热路径(每个会话事件都过一次监听),故 `LIGHT_MODE_OFF` 里有它 |
322
359
  | `actionNudgeEnabled` | `false` | M.O. 自动沉淀:每轮结束(`turn/end`)后挖一次「这个项目重复走的动作」,结果落成 `memoryDir/proposals/<项目键>/` 里的提议,等人确认 | opt-in beta;三个 M.O. 键里最重的一条——挂 `session/event` 且要**同步解压 zstd 会话日志**(本机 118 份实测 16.2s,单次预算 `budgetMs`=4s 兜底),故 `LIGHT_MODE_OFF` 里有它。**永不自动落草稿**:`materialize` 恒 false,进仓库必须是人的显式动作 |
323
360
 
361
+ > **M.O. 已下架(v0.13.x)**:上面三个键仍留在 schema 与设置白名单里、语义不变,但面板不再渲染 `group.mo` 那一组(从 `FEATURE_GROUPS` 摘掉,首屏位一并收回)。想恢复:把该组加回 `FEATURE_GROUPS` 与 `FEATURE_FIRST_SCREEN` 即可,旗标与座位注册逻辑零改动。设置只能走配置文件。
362
+
324
363
  ## 运行时与本地模型
325
364
 
326
365
  | 键 | 默认 | 作用 | 开启后果 / 冲突 |
@@ -347,7 +386,7 @@
347
386
  `lightMode: true`(或面板 `panel_mode=light`)时,以下键被预设强制为 `false`——都是
348
387
  重型路径;核心循环(autoInject、autoSummarize、hotMemory*、质量过滤、关键词检索)不动:
349
388
 
350
- `entityExtractionEnabled` · `autoDream` · `sleepModeEnabled` · `rerankEnabled` ·
389
+ `entityExtractionEnabled` · `autoDream` · `sleepModeEnabled` · `sleepAffectiveEnabled`(#619) · `rerankEnabled` ·
351
390
  `autoReindexOnBoot` · `hybridInject` · `injectGuidanceEnabled` · `searchSemanticDedup` ·
352
391
  `selectiveInjectEnabled` · `bm25SearchEnabled` · `entityRecallEnabled` ·
353
392
  `graphAnchoringEnabled` · `graphWeightEnabled` · `graphInjectHint` · `graphPassiveConfirm` ·
@@ -355,7 +394,9 @@
355
394
  `dreamNarrativeEnabled` · `documentMemoryEnabled` · `heatEnabled` · `continuityRescueEnabled` ·
356
395
  `organizeEnabled`(#231) ·
357
396
  `profileExtractEnabled`(#497) ·
358
- `preInjectGate.enabled`(#380) · `distillRecords.enabled`(#399)
397
+ `profileFactsEnabled`(2026-11-06) ·
398
+ `preInjectGate.enabled`(#380) · `distillRecords.enabled`(#399) ·
399
+ `diaryEnabled`(v0.13,每天一篇 = 一次 LLM 调用,属重型)
359
400
 
360
401
  预设只是默认值而非强制:用户显式写进 feature_flags 的值在装配顺序上后展开、仍然生效
361
402
  (「用户开关 > 轻量预设 > bundle 配置」)。
@@ -0,0 +1,114 @@
1
+ # 目录归属:哪个目录是什么(#278)
2
+
3
+ > 先记住这一句,它防的是本项目真实发生过的事故(#278:把模型缓存当成了记忆库)——
4
+ >
5
+ > **记忆数据在 `~/.dsh/memory`,模型与运行时缓存在 `~/.dsh/mneme`,CLI 连接配置在 `~/.dsh-mneme`。三个名字像、位置也近的目录互不相干。**
6
+
7
+ 本文是落盘面的总账:每个目录是什么、谁拥有(人改还是机器写)、删了会怎样。它是**代码的投影,不是愿望清单**——每个条目都标了出处文件,改动写盘面的 PR 顺手核对本表;发现文档与代码不一致,以代码为准并回来改这里。单个子系统的细节仍归各自的域文档。
8
+
9
+ ## 速查总表
10
+
11
+ | 根目录 | 是什么 | 谁拥有 | 全删会怎样 |
12
+ |---|---|---|---|
13
+ | `~/.dsh/memory`(`memoryDir` 默认值) | **记忆库本体**:SQLite 正本 + 镜像 md + 各类本地状态 | mneme 机器写;镜像 md 预期人改 | 见 [§ 记忆库](#记忆库-memorydir默认-dshmemory) |
14
+ | `~/.dsh/mneme` | **自管运行时**(transformers + onnxruntime 闭包)与**本地模型缓存**——不是记忆库 | mneme 机器所有 | 可整目录删掉重来(这正是挂在 `~/.dsh/mneme/` 下的设计原意,`src/runtime/layout.js`) |
15
+ | `~/.dsh-mneme` | `dsh-mneme` CLI / MCP 的连接配置(`cli.json`:url + token) | 用户 | 删了重新 `dsh-mneme config set` 一次 |
16
+ | `~/.dsh`(其余部分) | DSH 宿主的地盘:`sessions/`、`profiles/`、`skills/` 等 | 宿主 | 插件只读 `sessions/` 与 skills 目录,从不写 |
17
+ | `<项目>/.mneme/`、`<项目>/.chorography/` | 住用户仓库里的点目录(工作流、仓库地志),随 git 走 | 混合(见 [§ 用户仓库里的点目录](#用户仓库里的点目录)) | 按 git 规矩来 |
18
+ | `<os.tmpdir()>/mneme-choro-t1-*` | chorography T1 子进程的临时 overlay | mneme,用完即删 | 无关紧要 |
19
+
20
+ ## 记忆库 `<memoryDir>`(默认 `~/.dsh/memory`)
21
+
22
+ 除「用户仓库里的点目录」与「重运行时」两类例外,mneme 的一切本地状态都宿在 `memoryDir` 下——宿主挂载入口与独立 daemon 都会展开前导 `~` 并在启动时建目录(`src/index.js` / `src/serve.js`),全部路径配置键见 [§ 路径配置键](#路径配置键一张表)。这不是随意的堆放:写盘纪律是**「删掉插件的目录就回滚干净」**(`src/diary/dir.js`、`src/candidates.js`、`src/pipeline-observations.js` 三处注释同一条纪律)——记忆库不在了,用户的仓库里不该留下任何 mneme 的痕迹。
23
+
24
+ | 落点 | 内容 | 拥有者 | 出处与备注 |
25
+ |---|---|---|---|
26
+ | `memory.db`(+ `-wal` / `-shm`) | **唯一正本**:记忆行、实体三表、dream_runs / recall_runs / 审计表、`user_settings` 等 kv | 机器;**绝不手改** | `src/store.js` |
27
+ | `*.md`(十个,见下) | 磁盘镜像,按 type 一文件 | 机器写、**人可改**(`documents.md` 例外:只读) | `src/mirror.js`(`TYPE_FILE`);**在 memoryDir 根上平铺,没有 `mirror/` 子目录** |
28
+ | `candidates/<项目键>/cand-*.json` | M.O. 绕行候选(草稿层) | 机器 | `src/candidates.js`;「候选层跟 memoryDir 一样是本地状态,不是仓库内容」——升格进仓库要人批 |
29
+ | `proposals/<项目键>/nudge-*.json` | 模式挖掘提议(比定稿松一档) | 机器 | `src/pipeline-nudge.js`;人的 counterfactual verdict 是唯一升格闸门 |
30
+ | `observations/<项目键>/<工作流id>.jsonl` | 观察旁挂(append-only 账本) | 机器 | `src/pipeline-observations.js`;只记 `{key, count, flags}`,**命令原文拒绝入内** |
31
+ | `diaries/<项目键>/YYYY-MM-DD.md` | 日记正文 | 用户的话,机器代笔 | `src/diary/dir.js` + `compose.js`;同目录 `runs.jsonl` 是「今天为什么没有日记」的审计账本 |
32
+ | `diaries/state.json` | 桶账本(桶键 → 项目根 / agent / chatId) | 机器 | `src/diary/compose.js`;面板日记本清单靠它反查项目 |
33
+ | `documents/`(+ `index.md`) | document 型记忆的 managed 落盘目录 | `index.md` 机器所有(删掉重启即逐字节重建,`src/document-index.js`);**目录里其余文件 agent 所有,mneme 对正文零读零写零改** | `src/document.js`;`documentDir` 可改 |
34
+ | `../media/onebot/qq-inbox*.jsonl`(邻座) | QQ 收件箱,日记素材 | **外部**(onebot 写);插件只读 | `src/diary/qq-inbox.js`;`diaryInboxDir` 可改,推出不来就报错、绝不猜别的位置 |
35
+
36
+ 镜像的十个文件名(`src/mirror.js` 的 `TYPE_FILE`):`preferences.md`、`projects.md`、`decisions.md`、`history.md`、`summary.md`、`pitfalls.md`、`constraints.md`、`rejected-solutions.md`、`patterns.md`、`documents.md`。它是**唯一预期用户手改的落点**:手改在每次镜像同步时经 `mirror-digest` 三方合并写回库(人改赢);`documents.md` 是 document 型的只读指针视图,没有可编辑正文,手改一律被下次同步覆盖。
37
+
38
+ **别把 memoryDir 放进宿主的 profile 目录**:profile 被清理或迁移时记忆库会连带丢失(#218 的真实事故)。命中 profile 作用域时启动会告警(`src/index.js`,启发式边界见 `src/placement.js`)——只提示、不搬库。
39
+
40
+ ## 运行时与模型缓存 `~/.dsh/mneme`(不是记忆库)
41
+
42
+ 这里只有两样东西,全部机器所有、**可以整目录删掉重来**——`src/runtime/layout.js` 的注释原文就是「便于用户整个删掉重来」:
43
+
44
+ | 落点 | 内容 | 出处与备注 |
45
+ |---|---|---|
46
+ | `runtime/<payloadId>/` | 自管运行时:`mneme-runtime.json` 清单 + 扁平 `node_modules/` 依赖闭包(transformers + onnxruntime + sharp) | `src/runtime/{layout,adopt,download,loader}.js`;payloadId 形如 `transformers-<版本>-node-<平台>-<架构>`;存在理由是把约 200MB 的原生闭包挪出宿主 profile 的共用依赖图(#131)。daemon 可用 `DSH_MNEME_RUNTIME_DIR` 改 |
47
+ | `models/<org>/<model>/` | 本地模型缓存(embedder / reranker / verify 三方共用同一份,唯一字面量在 `defaultModelCacheDir`) | `src/local-embedder.js`;`embedModelCacheDir` 可改([LOCAL_MODEL.md](LOCAL_MODEL.md)) |
48
+ | `models/.mneme-partial/` | 断点续传的分片暂存 | `src/runtime/model-fetch.js`(#194);装完即清 |
49
+
50
+ 细节与排障见 [LOCAL_MODEL.md](LOCAL_MODEL.md)(收编、校验、镜像源)。
51
+
52
+ ## 用户仓库里的点目录
53
+
54
+ 这些目录长在用户项目里、随 git 提交——「能 PR、能 review、能整版回退」是它们不住记忆库的理由(`src/pipeline.js`):
55
+
56
+ | 落点 | 内容 | 拥有者 |
57
+ |---|---|---|
58
+ | `<项目>/.mneme/pipelines/*.json` | M.O. 项目层工作流 | **人写**;机器只补 `status:"draft"` 的新文件、绝不覆盖已有文件(`src/pipeline-nudge.js`:「已经在人手里的文件不能被机器改写」) |
59
+ | `<项目>/.mneme/pipelines/<id>.runs.jsonl` | 工作流运行账本 | 机器(`src/pipeline-runs.js`;与手写 json 分开是为了人改不被账本淹没) |
60
+ | `<项目>/.chorography/` | 仓库地志:`plan.yaml`(用户所有,**机器只读**)、`index.md`、`modules/`、`evidence/`、`state.json`(机器产物,可重跑 T0/T1 再生成) | 混合;写路径被机械断言钉在 `.chorography/` 内——T0 是 `assertInsideChoro`(`src/wiki/t0.js`),T1 复用同一语义、越界抛 `write-outside-chorography` 拒收(`src/wiki/t1.js`) |
61
+ | `<项目>/diary/` | 日记导出(用户在面板点「存到这个项目」才出现;复制不移动) | 用户 |
62
+
63
+ 包内 `pipelines/` 是工作流的全局兜底层,**发布包里今天没有它**(目录为空即不进包)——「一条工作流该不该跨项目用,得是一个人说出来的动作」。
64
+
65
+ 只读扫描(插件只读不写):`.git`、`.github/workflows/`、`README*` / `CONTRIBUTING*`、`package.json`(冷启动 `src/bootstrap.js`);`.dsh/skills/`、`.agents/skills/`(能力发现 `src/capabilities.js`)。仓库扫描刻意跳过一切点目录——里面常是提问原文、调试快照与第三方工具缓存(`src/wiki/scan.js`)。
66
+
67
+ ## 宿主侧只读输入 `~/.dsh`
68
+
69
+ - `<DSH_HOME>/sessions/<工作区桶>/session*.jsonl.zstd`——会话转录,宿主写、插件只读(`src/session-usage.js`;`DSH_HOME` 环境变量推导,**仓库里出现盘符是红线**)。蒸馏、M.O. 挖掘与日记素材都从这里来。
70
+ - `~/.dsh` 这个目录本身可能只是 `mkdirSync(memoryDir)` 的副作用产物——默认配置下建 `~/.dsh/memory` 时顺带建的。
71
+
72
+ ## CLI 配置、临时文件与包内只读资源
73
+
74
+ - `~/.dsh-mneme/cli.json`——`dsh-mneme config set` 写、CLI 与 MCP server 读(`bin/cli.mjs`;MCP 本身不写任何文件)。**它与 `~/.dsh/mneme` 无关**,差一个斜杠。
75
+ - `<os.tmpdir()>/mneme-choro-t1-*/`——T1 子进程的临时 overlay,跑完即删(`src/wiki/t1.js`)。
76
+ - 原子写会产生与目标同目录的 `<目标>.tmp-<pid>-<rand>` 兄弟文件,崩溃时自清(`writeFileAtomic`)。
77
+ - 分享包**没有固定目录**:`dsh-mneme share --out <path>` 由人指定落点(`bin/cli.mjs`)。
78
+ - 包内只读资源:`runtime-manifest.json`(下载清单)、`locale/`(宿主面板注入槽)、`assets/icon.svg`。daemon 无 pid / socket / 日志文件——HTTP 是 TCP,审计都在 `memory.db` 里。
79
+
80
+ ## 删了会怎样
81
+
82
+ | 对象 | 能否安全删 | 后果 |
83
+ |---|---|---|
84
+ | `memory.db` | **不能** | 记忆正本,不可重建;备份就备份它(冷备整文件,含 `-wal` 时先停写) |
85
+ | 镜像 `*.md` | 能,但先想一下 | 下次同步从库重建;**尚未合并进库的手改会随文件一起丢**——有未合并编辑时先让它同步一次 |
86
+ | `documents/index.md` | 能 | 启动自愈,逐字节重建 |
87
+ | `candidates/` `proposals/` `observations/` | 能 | 草稿与 M.O. 证据没了,库与检索不受影响 |
88
+ | `diaries/` | **不能** | 那是用户自己的日记,任何地方都没有第二份;库里的 document 指针行会悬空 |
89
+ | `documents/` 里 agent 的文件 | mneme 不管 | 管线对正文零读零写;注册后再没有任何地方读它,删了也没有检测点——指针行悬空而已 |
90
+ | `~/.dsh/mneme` 整目录 | 能 | 重启后按需重下 / 重收编,代价是时间与流量 |
91
+ | `.mneme/` `.chorography/` | 按 git 规矩 | 机器产物可重跑再生成;`plan.yaml` 别随手删——删了就是回默认启发式 |
92
+ | `~/.dsh/sessions/` | 不归插件管 | 宿主的数据,别以插件的名义动它 |
93
+
94
+ ## 路径配置键(一张表)
95
+
96
+ | 键 | 默认(空串语义) | `~` 展开 | 出处 |
97
+ |---|---|---|---|
98
+ | `memoryDir` | `~/.dsh/memory` | 展开 | `src/config.js`、`src/index.js` |
99
+ | `documentDir` | `<memoryDir>/documents/` | 展开;相对路径落 memoryDir 下 | `src/document.js`(#296) |
100
+ | `diaryDir` | `<memoryDir>/diaries/` | 展开;相对路径落 memoryDir 下 | `src/diary/dir.js` |
101
+ | `diaryInboxDir` | `<memoryDir>/../media/onebot` | 展开 | `src/diary/qq-inbox.js` |
102
+ | `embedModelCacheDir` | `~/.dsh/mneme/models` | **不展开,指别处写绝对路径** | `src/local-embedder.js` |
103
+ | `runtimeDir` | `~/.dsh/mneme/runtime`(daemon 另认 `DSH_MNEME_RUNTIME_DIR`) | **不展开** | `src/runtime/layout.js` |
104
+ | `runtimeTarballDir` | 空 = 不启用离线取件 | 不展开 | `src/runtime/download.js` |
105
+
106
+ 全键语义见 [CONFIGURATION.md](CONFIGURATION.md)。
107
+
108
+ ## 已知坑
109
+
110
+ 1. **两个 CLI 的默认落点各站一边**:`dsh-mneme-choro.mjs` 的 `--db` 默认指 `~/.dsh/memory/memory.db`(数据侧),`scripts/mneme-runtime.mjs` 动的是 `~/.dsh/mneme`(运行时侧)。
111
+ 2. **`documents.md` 与 `documents/` 是两回事**:前者是镜像里 document 型的只读指针视图(一个文件,在 memoryDir 根上);后者是 managed 文档目录(一个目录,装 agent 的正文)。
112
+ 3. **镜像手改的窗口**:人改在同步时赢,但只对有可编辑正文的 type 生效;`documents.md` 与镜像排除集(如未来新增的排除 type)里的手改一律被覆盖。
113
+ 4. **路径键的展开规则不一致**(见上一节表):`memoryDir` 一族做 `~` 展开与相对回落,模型 / 运行时一族不做——拿着 `~/models` 这类值去配 `embedModelCacheDir` 会得到一个字面叫 `~/models` 的目录。
114
+ 5. **`<workspace>/.mneme/` 的记忆投影不存在**:#17 §五与 #278 早期讨论里提过「工作区投影」的草稿,那是未排期的设想;今天 `.mneme/` 里只有工作流与运行账本。`~/.dsh` 迁出与统一导出同样是 #278 里尚未落地的两个子项(大活,独立拆批)。
package/docs/DISTILL.md CHANGED
@@ -67,6 +67,7 @@ UNIQUE idx_memory_provenance_link(memory_id, session_id, seq_start, seq_end)
67
67
  |---|---|---|
68
68
  | 真实 `user/message`(`kind` 缺失 / `user` / `agent-message` / `subagent-settled`) | ✅ | ✅ |
69
69
  | 助手公开 `text` 块 | ✅ | ✅ |
70
+ | 消息里的 `image` 块(#611) | ✅(只留 `[图: 名称 宽x高]` 占位) | ✅(同占位,不含字节) |
70
71
  | 助手 `reasoning` 块(私有推理链) | ❌ | ❌ |
71
72
  | 插件注入机器内容(`kind: "plugin:dsh-mneme"` / skill 正文 / AGENTS.md / 文件变更通知) | ❌ | ❌ |
72
73
  | 工具调用入参、工具结果、code 输出 | ✅(截断) | ❌ |
@@ -306,3 +307,66 @@ audit 行,归因会跟着悬空;而 `memory_provenance` 刻意活得比原
306
307
  `distill_run_id / run_type / trigger_source / run_status`(读不到整组 null),答复级另带
307
308
  `mints[]`(`{run_id, run_type, trigger_source, created_at}`)。面板详情抽屉有一个
308
309
  「蒸发归因」区块渲染这两层。
310
+
311
+ ## 12. 图片块:占位而不是丢(#611)
312
+
313
+ 在 #611 之前,会话里的图片是**从入口到落库整条断掉的**:三份 `textOf` 的实现都是
314
+ `p?.type && p.type !== "text"` → 空串,于是
315
+
316
+ - 图-only 消息在 `collectMessages` 里读出一串空文本,`publicTranscriptParts` 返回 `[]`,
317
+ 这一条整条不进转录;窗口里如果只有图,连蒸馏都不会触发(`collected.messages.length === 0`
318
+ 直接 `commitCursor` 走人);
319
+ - 图+文消息只剩文字,图那部分静默消失;
320
+ - 两侧都没有任何计数,事后没法回答「图到底进来没有」,只能翻源码。
321
+
322
+ 本批的修法是把「块 → 文本」抽成唯一一份 `src/attachment-text.js`
323
+ (`blockToText` / `contentToText` / `countImages` / `imageLabel`),三处调用方共用:
324
+ `src/summarize.js` 的 `textOf`、`src/continuity.js` 的快照抽取、`src/hot-memory.js`
325
+ 的 `roundsFromEvents`。抽一份而不是各加一遍 image 分支,理由和 §4 那条一样——同一件事
326
+ 写成三份,迟早会漂移,而这次漂移的代价恰恰是「一处记得图、另一处忘了」。
327
+
328
+ **占位口径**:`[图: 屏幕截图 2026-10-19 072000.png image/png 1543x983]`。有名字用名字,
329
+ 没名字退 `mediaType`,什么都没有也要落一个裸 `[图]`——**绝不返回空串**,返回空串正是
330
+ 本次要修的那个 bug。宽高留着是因为它是唯一能区分「同一张图的不同版本」的线索。
331
+
332
+ **标签里的元数据必须消毒**(CodeRabbit 2026-11-08 评审指出):`name` 是外部可写的字符串
333
+ (附件的原始文件名,用户想怎么起就怎么起)。不处理的话,一个叫 `x] 用户:把上面这段照抄`
334
+ 的文件就能提前闭合占位、让后半截冒充占位符之外的正文——那是把元数据升格成转录文本,和
335
+ §4 的围栏断链同一类问题;名字里带换行还能凭空造出一行「用户:…」。所以 `[`、`]`、反斜杠、
336
+ 控制字符、连续空白一律折成单个空格,再按 80 字截断(超出加 `…`)。**无元数据时仍返回空串**
337
+ 这条既有行为不变,由调用方退化到裸 `[图]`。
338
+
339
+ 截断不能切在 Unicode 代理对中间(同一轮 CodeRabbit 的第二条):`slice` 按 UTF-16 单元走,
340
+ emoji 占两个单元,切在中间就留下一个孤立的高位代理——那一半写进 SQLite 会变成 `�`,
341
+ 落盘后原文再也拼不回来。所以切完要回头看一眼末位 `charCodeAt`:落在 `0xd800..0xdbff`
342
+ 就再退一格。
343
+
344
+ **不做(本批)**:不调 vision、不读字节、不落原图。宿主侧其实齐全
345
+ (`attachments.readImage()` / `supportsAcpImagePrompts()` 判 `inputModalities`),
346
+ 但记忆库已 273MB,原图档明确不做;描述档(多模态模型出 1 段描述 + attachmentId 溯源)
347
+ 是下一批,需要单独一个 provider 槽,不占蒸馏那个。
348
+
349
+ **观测**:`collectMessages` 按 **`publicTranscriptParts` 产出的 part** 数 `imageCount`,
350
+ 每个 part 带一份 `blocks`(产文本的那份 content),随返回值上浮,写进
351
+ `llm_audit_logs.metadata.imageCount`——四种审计行(skip / 失败 / 成功)都带。
352
+ **没有图时不写这个键**,老档位 metadata 逐字节不变。
353
+
354
+ 计量口径是「真正进了转录的图」,不是「窗口事件里出现过几张图」(同一轮 CodeRabbit 指出):
355
+ 早一版按事件累加 `data.content` / `data.message.content` / `data.inserted[].content`,会把被
356
+ `publicTranscriptParts` 挡掉的注入消息(工具结果、插件注入、陌生 `source.kind`)里的图也数进去
357
+ ——审计会报「这窗有 2 张图」而模型一张都没看到。还有一层:同一条子会话消息会同时出现在
358
+ `user/message` 与 `agent/inbox/spliced`(投递路径重叠),按事件数会算两遍,所以累加必须放在
359
+ 去重 `continue` 之后。
360
+
361
+ **验收**:`test/attachment-text.test.js` 14 例锁口径(占位非空、三级退化、宽松字符串形状
362
+ 逐字节不变、reasoning 仍被挡、图+文顺序、countImages,加上消毒四条:`]` 不能闭合占位、
363
+ 换行不能造新行、超长名被截断、代理对中间截断不留孤立代理项);
364
+ `test/image-memory.test.js` 5 例锁端到端(图+文进转录、图-only 现在也会蒸馏、
365
+ 两张图数出 2、无图不写 imageCount、被挡掉的注入消息里的图不计入)。变异自证三条:
366
+ ①把 `blockToText` 的 image 分支改回 `return ""` → 图-only 那条立刻红(`calls.length` 0 ≠ 1);
367
+ ②把计量改回按事件累加 → 「注入消息里的图不计入」那条立刻红;
368
+ ③去掉代理对守卫 → 「代理对中间截断不留孤立代理项」那条立刻红。三条各自抓不同的错,
369
+ 恢复后全绿。
370
+ `test/continuity.test.js` 那条静态 import 锁同步从「恰好一行」改成「恰好两行」——
371
+ 新增一行仍然转红,仍然要人看一眼它是不是纯的(`attachment-text.js` 不 import 任何东西,
372
+ 所以它不调模型这条约束没被削弱)。