@modusensus/dsh-mneme 0.8.13 → 0.8.15

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 (271) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +655 -626
  3. package/assets/icon.svg +3 -3
  4. package/bin/cli.mjs +689 -689
  5. package/bin/dsh-mneme-mcp.mjs +510 -510
  6. package/bin/dsh-mneme-serve.mjs +151 -0
  7. package/cordis.patch.yml +21 -21
  8. package/docs/CONFIGURATION.md +263 -0
  9. package/docs/CONTINUITY.md +124 -0
  10. package/docs/DAEMON.md +46 -0
  11. package/docs/ENTITIES.md +245 -0
  12. package/docs/LOCAL_MODEL.md +222 -0
  13. package/docs/SEMANTIC.md +256 -0
  14. package/docs/SLEEP.md +164 -0
  15. package/docs/STORAGE.md +89 -0
  16. package/lib/api-standalone.js +673 -572
  17. package/lib/api.js +1437 -1433
  18. package/lib/bootstrap.js +211 -211
  19. package/lib/client.js +5444 -5295
  20. package/lib/commands.js +95 -95
  21. package/lib/config.js +880 -836
  22. package/lib/content-hash.js +54 -54
  23. package/lib/continuity.js +220 -220
  24. package/lib/document-index.js +94 -94
  25. package/lib/document.js +303 -303
  26. package/lib/dream/clustering.js +118 -118
  27. package/lib/dream/decisions.js +674 -674
  28. package/lib/dream/narratives.js +44 -44
  29. package/lib/dream/sleep.js +819 -819
  30. package/lib/dream.js +1501 -1501
  31. package/lib/embedding.js +177 -177
  32. package/lib/entities/extractor.js +252 -252
  33. package/lib/graph/anchoring.js +179 -179
  34. package/lib/guide.js +74 -74
  35. package/lib/heat.js +144 -144
  36. package/lib/hot-memory.js +46 -46
  37. package/lib/index.js +535 -671
  38. package/lib/inject.js +464 -450
  39. package/lib/lang.js +248 -229
  40. package/lib/local-embedder.js +358 -358
  41. package/lib/maintenance.js +142 -142
  42. package/lib/mirror.js +330 -330
  43. package/lib/organize.js +450 -450
  44. package/lib/peak-hours.js +117 -117
  45. package/lib/placement.js +13 -0
  46. package/lib/pre-inject-gate.js +340 -0
  47. package/lib/quality-filter.js +126 -126
  48. package/lib/recall-stats.js +175 -175
  49. package/lib/reranker.js +269 -269
  50. package/lib/runtime/adopt.js +176 -176
  51. package/lib/runtime/closure.js +172 -172
  52. package/lib/runtime/download.js +247 -247
  53. package/lib/runtime/layout.js +265 -265
  54. package/lib/runtime/loader.js +252 -252
  55. package/lib/runtime/model-fetch.js +361 -361
  56. package/lib/runtime/provision.js +237 -237
  57. package/lib/runtime/tarball.js +130 -130
  58. package/lib/runtime/verify.js +223 -204
  59. package/lib/scope.js +146 -146
  60. package/lib/search/adaptive.js +60 -60
  61. package/lib/search/bm25.js +96 -96
  62. package/lib/semantic.js +229 -0
  63. package/lib/sensitive-scan.js +209 -209
  64. package/lib/serve.js +215 -0
  65. package/lib/service.js +2663 -2596
  66. package/lib/settings.js +517 -509
  67. package/lib/store.js +3007 -3007
  68. package/lib/summarize.js +891 -856
  69. package/lib/tools.js +743 -737
  70. package/lib/vector-index.js +110 -110
  71. package/lib/version-check.js +122 -122
  72. package/lib/write-admission.js +402 -402
  73. package/locale/en.json +6 -6
  74. package/locale/zh.json +6 -6
  75. package/package.json +8 -4
  76. package/runtime-manifest.json +526 -526
  77. package/scripts/benchmark-embed.js +201 -201
  78. package/scripts/benchmark-recall.js +249 -249
  79. package/scripts/benchmark-rerank.js +166 -166
  80. package/scripts/build-runtime-manifest.mjs +224 -224
  81. package/scripts/changelog-prep.mjs +36 -0
  82. package/scripts/check-sync.js +42 -42
  83. package/scripts/e2e-dsh.js +220 -220
  84. package/scripts/mneme-runtime.mjs +277 -277
  85. package/scripts/stress-dsh.js +255 -255
  86. package/scripts/sync-lib.js +47 -47
  87. package/scripts/sync-test-badge.mjs +69 -69
  88. package/scripts/test-count-sync.mjs +44 -44
  89. package/src/api-standalone.js +673 -572
  90. package/src/api.js +1437 -1433
  91. package/src/bootstrap.js +211 -211
  92. package/src/commands.js +95 -95
  93. package/src/config.js +880 -836
  94. package/src/content-hash.js +54 -54
  95. package/src/continuity.js +220 -220
  96. package/src/document-index.js +94 -94
  97. package/src/document.js +303 -303
  98. package/src/dream/clustering.js +118 -118
  99. package/src/dream/decisions.js +674 -674
  100. package/src/dream/narratives.js +44 -44
  101. package/src/dream/sleep.js +819 -819
  102. package/src/dream.js +1501 -1501
  103. package/src/embedding.js +177 -177
  104. package/src/entities/extractor.js +252 -252
  105. package/src/graph/anchoring.js +179 -179
  106. package/src/guide.js +74 -74
  107. package/src/heat.js +144 -144
  108. package/src/hot-memory.js +46 -46
  109. package/src/index.js +535 -671
  110. package/src/inject.js +464 -450
  111. package/src/lang.js +248 -229
  112. package/src/local-embedder.js +358 -358
  113. package/src/maintenance.js +142 -142
  114. package/src/mirror.js +330 -330
  115. package/src/organize.js +450 -450
  116. package/src/peak-hours.js +117 -117
  117. package/src/placement.js +13 -0
  118. package/src/pre-inject-gate.js +340 -0
  119. package/src/quality-filter.js +126 -126
  120. package/src/recall-stats.js +175 -175
  121. package/src/reranker.js +269 -269
  122. package/src/runtime/adopt.js +176 -176
  123. package/src/runtime/closure.js +172 -172
  124. package/src/runtime/download.js +247 -247
  125. package/src/runtime/layout.js +265 -265
  126. package/src/runtime/loader.js +252 -252
  127. package/src/runtime/model-fetch.js +361 -361
  128. package/src/runtime/provision.js +237 -237
  129. package/src/runtime/tarball.js +130 -130
  130. package/src/runtime/verify.js +223 -204
  131. package/src/scope.js +146 -146
  132. package/src/search/adaptive.js +60 -60
  133. package/src/search/bm25.js +96 -96
  134. package/src/semantic.js +229 -0
  135. package/src/sensitive-scan.js +209 -209
  136. package/src/serve.js +215 -0
  137. package/src/service.js +2663 -2596
  138. package/src/settings.js +517 -509
  139. package/src/store.js +3007 -3007
  140. package/src/summarize.js +891 -856
  141. package/src/tools.js +743 -737
  142. package/src/vector-index.js +110 -110
  143. package/src/version-check.js +122 -122
  144. package/src/write-admission.js +402 -402
  145. package/test/adaptive-inject-budget.test.js +85 -85
  146. package/test/api-routes-gaps.test.js +218 -218
  147. package/test/api.test.js +1702 -1688
  148. package/test/audit.test.js +448 -448
  149. package/test/baseline-persist.test.js +248 -248
  150. package/test/benchmark.test.js +94 -94
  151. package/test/bootstrap.test.js +220 -220
  152. package/test/changelog-prep.test.js +60 -0
  153. package/test/client.test.js +1230 -1169
  154. package/test/clustering.test.js +100 -100
  155. package/test/commands.test.js +106 -106
  156. package/test/config.test.js +66 -66
  157. package/test/conflict-freeze.test.js +300 -300
  158. package/test/content-hash.test.js +204 -204
  159. package/test/continuity.test.js +333 -333
  160. package/test/document-dir.test.js +233 -233
  161. package/test/document.test.js +671 -671
  162. package/test/dream-archive-guardrail.test.js +116 -116
  163. package/test/dream-candidate-hybrid.test.js +281 -281
  164. package/test/dream-coverage-degrade.test.js +86 -86
  165. package/test/dream-failure-backoff.test.js +173 -173
  166. package/test/dream-peak-hours.test.js +194 -194
  167. package/test/dream.test.js +1385 -1383
  168. package/test/embedding-ready.test.js +196 -196
  169. package/test/entities.test.js +627 -547
  170. package/test/entity-recall.test.js +179 -179
  171. package/test/epistemic.test.js +325 -325
  172. package/test/fnew-0112.test.js +311 -311
  173. package/test/fnew-03.test.js +422 -422
  174. package/test/graph-anchoring.test.js +184 -184
  175. package/test/graph-api.test.js +175 -175
  176. package/test/graph-inject-budget.test.js +103 -103
  177. package/test/graph-passive-confirm.test.js +91 -91
  178. package/test/graph-weight-evolution.test.js +104 -104
  179. package/test/heat.test.js +175 -175
  180. package/test/helpers/dream-mock.js +82 -82
  181. package/test/helpers/peer-worker.mjs +25 -25
  182. package/test/helpers/runtime-source.js +60 -60
  183. package/test/helpers/tar-builder.js +80 -80
  184. package/test/helpers/write-admission-samples.js +308 -308
  185. package/test/hot-memory.test.js +145 -145
  186. package/test/inject-content-cap.test.js +91 -91
  187. package/test/inject-first-round-bm25.test.js +89 -89
  188. package/test/inject-heat.test.js +77 -61
  189. package/test/inject-parent-gate.test.js +106 -106
  190. package/test/inject-pools.test.js +194 -194
  191. package/test/inject-recall.test.js +105 -105
  192. package/test/inject-rotation.test.js +161 -161
  193. package/test/inject.test.js +420 -420
  194. package/test/lang.test.js +87 -87
  195. package/test/lastrunat-persist.test.js +101 -101
  196. package/test/lib-smoke.test.js +96 -96
  197. package/test/llm-audit.test.js +598 -598
  198. package/test/llm-message-source.test.js +207 -207
  199. package/test/local-embedder.test.js +292 -292
  200. package/test/maintenance.test.js +391 -391
  201. package/test/mcp-stdio.test.js +385 -385
  202. package/test/mirror-dirty.test.js +424 -424
  203. package/test/mirror-edit-digest.test.js +187 -187
  204. package/test/mirror-generation.test.js +499 -499
  205. package/test/mirror-types.test.js +208 -208
  206. package/test/mirror.test.js +249 -249
  207. package/test/narrative-bars.test.js +162 -162
  208. package/test/organize.test.js +418 -418
  209. package/test/peer-blockers.test.js +193 -193
  210. package/test/placement.test.js +36 -0
  211. package/test/policy-epoch.test.js +259 -259
  212. package/test/pre-inject-gate.test.js +434 -0
  213. package/test/quality-filter.test.js +186 -186
  214. package/test/reasoning-effort.test.js +752 -752
  215. package/test/recall-anti-update.test.js +82 -82
  216. package/test/recall-evals.test.js +253 -253
  217. package/test/recall-layer.test.js +326 -326
  218. package/test/recall-stats.test.js +189 -189
  219. package/test/receipt-chain.test.js +451 -451
  220. package/test/reflection.test.js +226 -226
  221. package/test/reindex-backfill.test.js +62 -62
  222. package/test/reranker-single-logit.test.js +73 -73
  223. package/test/reranker.test.js +197 -197
  224. package/test/resident-status.test.js +83 -83
  225. package/test/runtime-adopt.test.js +206 -206
  226. package/test/runtime-cli.test.js +89 -89
  227. package/test/runtime-closure.test.js +193 -193
  228. package/test/runtime-download.test.js +302 -302
  229. package/test/runtime-layout.test.js +163 -163
  230. package/test/runtime-loader.test.js +255 -255
  231. package/test/runtime-manifest.test.js +159 -159
  232. package/test/runtime-model-fetch.test.js +348 -348
  233. package/test/runtime-provision.test.js +101 -101
  234. package/test/runtime-tarball.test.js +81 -81
  235. package/test/runtime-verify.test.js +319 -279
  236. package/test/scope-candidates.test.js +239 -239
  237. package/test/scope-explicit.test.js +452 -452
  238. package/test/scope-inject-soft.test.js +106 -106
  239. package/test/scope-retrieval.test.js +197 -197
  240. package/test/scope-storage.test.js +255 -255
  241. package/test/scope-strict.test.js +367 -343
  242. package/test/search-fusion.test.js +90 -90
  243. package/test/semantic.test.js +124 -124
  244. package/test/sensitive-scan.test.js +207 -207
  245. package/test/serve-bin.test.js +121 -0
  246. package/test/serve.test.js +196 -0
  247. package/test/service-search.test.js +174 -174
  248. package/test/service.test.js +405 -405
  249. package/test/settings.test.js +261 -261
  250. package/test/skip-invalid-restore.test.js +262 -262
  251. package/test/sleep-action-set.test.js +422 -422
  252. package/test/sleep-heat.test.js +126 -126
  253. package/test/sleep-scheduler-rearm.test.js +89 -89
  254. package/test/sleep.test.js +527 -527
  255. package/test/standalone-api.test.js +751 -544
  256. package/test/store-vector-cache.test.js +98 -98
  257. package/test/store.test.js +396 -396
  258. package/test/stress.test.js +209 -209
  259. package/test/summarize-occurred-at.test.js +127 -0
  260. package/test/summarize-reasoning-effort.test.js +189 -189
  261. package/test/summarize.test.js +1465 -1465
  262. package/test/supersede-state-tracking.test.js +394 -0
  263. package/test/test-count-sync.test.js +60 -60
  264. package/test/tools-dto-schema-matrix.test.js +325 -325
  265. package/test/tools-memory-get-schema.test.js +65 -65
  266. package/test/tools.test.js +428 -374
  267. package/test/updated-at-semantics.test.js +113 -113
  268. package/test/vector-index.test.js +221 -221
  269. package/test/version-check.test.js +152 -152
  270. package/test/webServer-optional.test.js +82 -82
  271. package/test/write-admission.test.js +668 -668
package/lib/lang.js CHANGED
@@ -1,229 +1,248 @@
1
- // 记忆语言(memory.language,src/config.js):生成记忆、注入标题与后台 LLM
2
- // 提示词所用语言,'zh'(默认,上游行为不变)/ 'en'。本模块无全局状态:每个
3
- // 插件实例从自己的 config 取语言(langOf),逐层传给 inject / summarize /
4
- // dream / sleep / mirror,多实例(如 agent preset 内挂载)互不影响。
5
- // 各语言对经 STR.<key>[language] 索引取值。
6
-
7
- /** 解析实例语言:config.language === "en" 用英文,其余(含缺席)一律中文。 */
8
- export function langOf(config) {
9
- return config?.language === "en" ? "en" : "zh";
10
- }
11
-
12
- const PROMPTS = {
13
- "summary": {
14
- "zh": "你是记忆库提炼助手。根据下面的会话内容,提炼值得跨会话记住的原子记忆。\n原子记忆原则:每条记忆只装一个独立事实/偏好/决策,短小、自带完整上下文(把数字、名字、路径、结论等原始细节保留在 content 里,不要抽象概括);宁可拆成多条也绝不合并丢细节。信息量一般提 2-4 条,信息密集的对话可提 4-8 条。\n只输出 JSON 数组,每项形如 {\"type\":\"preference|project|decision|history\",\"title\":\"简短标题\",\"content\":\"保留原始细节的一句话\",\"importance\":1-5}。\n不要输出任何其他文字。",
15
- "en": "You are the memory curation assistant. From the conversation content below, distill atomic memories worth remembering across sessions.\nAtomic-memory principle: each memory holds exactly one standalone fact/preference/decision — short and self-contained (keep raw details like numbers, names, paths, and conclusions verbatim in content; do not abstract or summarize them away). Prefer splitting into multiple entries over merging and losing detail. Extract 2-4 entries normally, 4-8 for information-dense conversations.\nWrite every title and content in English.\nOutput only a JSON array, each item shaped {\"type\":\"preference|project|decision|history\",\"title\":\"short title\",\"content\":\"one sentence preserving original detail\",\"importance\":1-5}.\nOutput no other text."
16
- },
17
- "codingSummary": {
18
- "zh": "你是记忆库提炼助手。根据下面的会话内容(含用户输入、助手回答、工具调用与结果),提炼值得跨会话记住的原子记忆。\n原子记忆原则:每条记忆只装一个独立事实/偏好/决策,短小、自带完整上下文(把数字、报错信息、命令、路径、结论等原始细节保留在 content 里,不要抽象概括);宁可拆成多条也绝不合并丢细节。信息量一般提 2-4 条,信息密集的对话可提 4-8 条。\n只输出 JSON 数组,每项形如 {\"type\":\"preference|project|decision|history|rejected_solution|pitfall|constraint\",\"title\":\"简短标题\",\"content\":\"保留原始细节的一句话\",\"importance\":1-5}。\n若对话涉及编码/调试,可额外提取编码类记忆:\n- rejected_solution:被否决/废弃的实现方案(content 含方案简述 + 被否决原因 + 最终采用方案)\n- pitfall:调试踩坑记录(content 含现象/报错 + 根因 + 解决/规避方法)\n- constraint:项目工程约束(content 含约束描述 + 来源)\n普通闲聊、临时无关对话一律不提取编码类记忆。不要输出任何其他文字。",
19
- "en": "You are the memory curation assistant. From the conversation content below (including user input, assistant replies, tool calls and results), distill atomic memories worth remembering across sessions.\nAtomic-memory principle: each memory holds exactly one standalone fact/preference/decision — short and self-contained (keep raw details like numbers, error messages, commands, paths, and conclusions verbatim in content; do not abstract them away). Prefer splitting into multiple entries over merging and losing detail. Extract 2-4 entries normally, 4-8 for information-dense conversations.\nWrite every title and content in English.\nOutput only a JSON array, each item shaped {\"type\":\"preference|project|decision|history|rejected_solution|pitfall|constraint\",\"title\":\"short title\",\"content\":\"one sentence preserving original detail\",\"importance\":1-5}.\nIf the conversation involves coding/debugging, additionally extract coding-specific memories:\n- rejected_solution: implementation approaches that were rejected or abandoned (content: brief approach + why rejected + what was adopted instead)\n- pitfall: debugging pitfalls (content: symptom/error + root cause + fix or workaround)\n- constraint: project engineering constraints (content: constraint description + source)\nNever extract coding-specific memories from casual small talk or temporary unrelated conversations. Output no other text."
20
- },
21
- "dreamSummary": {
22
- "zh": "你是记忆库状态叙述助手。根据整理后的记忆,写一段 150-200 字的「当前状态」叙述:现在在做什么(活跃主题)、最近完成了什么或发生了什么变化、接下来明显的走向。只陈述记忆中有依据的事实,不要发明细节;偏好、项目、决策自然融入叙述而不必逐项罗列。这段叙述会作为会话上下文常驻注入。只输出叙述文本,不要其他内容。",
23
- "en": "You are the memory status narrator. From the consolidated memories below, write a 150-200 word \"current state\" narrative: what is being worked on now (active themes), what was recently completed or changed, and the obvious next direction. State only facts grounded in the memories — never invent details; weave preferences, projects, and decisions into the narrative instead of listing them. This narrative is injected as standing conversation context. Output only the narrative text, nothing else."
24
- },
25
- "consolidation": {
26
- "zh": "你是记忆库整理助手。下面是全部记忆条目(id、类型、标题、内容、重要性、更新时间)。\n请执行记忆巩固(consolidation),输出一个决策 JSON 数组。\n\n【决策格式(必须严格遵守)】\n每个决策必须是对象,字段固定:\n- \"action\":必填。取值只能是 \"keep\" / \"merge\" / \"archive\" / \"update\" / \"conflict\" 之一(字段名必须是 action,严禁写成 type)\n- \"ids\":必填,数组,本决策涉及的记忆 id 列表\n- \"reason\":可选,字符串,决策理由\n- \"importance\":可选,整数 1-5\n- merge 额外字段:\"keepSource\"(单个 id 字符串,必须是 ids 之一)+ 合并后的 \"title\"、\"content\"\n- conflict 额外字段:\"winner\" 与 \"loser\",都是【单个 id 字符串,不是数组】\n- update 额外字段:修正后的 \"title\" 和/或 \"content\";\"ids\" 只能包含一个 id\n\n【决策 JSON 示例】\n[\n { \"action\": \"merge\", \"ids\": [\"m1\", \"m2\"], \"keepSource\": \"m1\", \"title\": \"合并标题\", \"content\": \"合并后的摘要内容\", \"importance\": 4, \"reason\": \"主题相近\" },\n { \"action\": \"conflict\", \"winner\": \"m3\", \"loser\": \"m4\", \"reason\": \"内容矛盾,保留更新的信息\" },\n { \"action\": \"update\", \"ids\": [\"m5\"], \"content\": \"修正后的内容\", \"reason\": \"信息过时\" },\n { \"action\": \"archive\", \"ids\": [\"m6\"], \"reason\": \"重复或过时\" }\n]\n\n【任务】\n1. 识别主题相近的条目 → merge(合并为更精炼的摘要,保留信息最完整的 id 作为 keepSource)\n2. 识别重复/过时信息 → archive\n3. 识别内容矛盾的条目 → conflict(按时间新旧、来源完整性、信息具体程度判断 winner/loser)\n4. 发现单条记忆中的信息过时、错误或遗漏 → update(直接修正内容)\n - update 的 ids 只能包含一个 id\n - 必须提供修正后的 title 和/或 content\n - 仅当内容确实需要修正时才使用,不要滥用\n - 每次整理最多输出 2 个 update\n - 24 小时内新建的记忆不可 update\n5. 无问题的条目无需输出(未提及的条目将自动保留 keep)\n\n【硬性规则】\n- 字段名必须精确为 \"action\",严禁写成 \"type\";字段名统一用双引号\n- conflict 的 winner/loser、merge 的 keepSource 都是【单个 id 字符串,绝不是数组】\n- 每条记忆最多被 claim 一次:同一个 id 不能出现在多个决策中(同一 id 不能被 merge 和 conflict/archive 等重复占用)\n- 未在决策中提及的记忆将自动保留(keep),无需为每条记忆输出 keep\n- merge 的 keepSource 必须是 ids 之一\n- 仅合并同类型条目(type 相同)\n- 不要编造 ids;只使用提供的 id\n- 重要性 1-5,合并后取最高\n- archive 仅用于确属「重复或已过时」的条目;rejected_solution / constraint / pitfall / preference / pattern 属长保留类型,除非确属重复或过时,否则不要归档\n- 每次整理最多输出 8 个 archive\n- 只输出 JSON 数组,不要其他文字",
27
- "en": "You are the memory consolidation assistant. Below are all memory entries (id, type, title, content, importance, updated time).\nPerform memory consolidation and output a JSON array of decisions.\n\n[Decision format (must be followed strictly)]\nEach decision must be an object with fixed fields:\n- \"action\": required. Value must be one of \"keep\" / \"merge\" / \"archive\" / \"update\" / \"conflict\" (the field name must be action, never type)\n- \"ids\": required, array, the list of memory ids involved in this decision\n- \"reason\": optional, string, rationale for the decision\n- \"importance\": optional, integer 1-5\n- merge extra fields: \"keepSource\" (a single id string, must be one of ids) plus the merged \"title\" and \"content\"\n- conflict extra fields: \"winner\" and \"loser\", each a [single id string, never an array]\n- update extra fields: the corrected \"title\" and/or \"content\"; \"ids\" may contain only one id\n\n[Decision JSON examples]\n[\n { \"action\": \"merge\", \"ids\": [\"m1\", \"m2\"], \"keepSource\": \"m1\", \"title\": \"Merged title\", \"content\": \"Merged summary content\", \"importance\": 4, \"reason\": \"similar topics\" },\n { \"action\": \"conflict\", \"winner\": \"m3\", \"loser\": \"m4\", \"reason\": \"content contradicts; keep the newer information\" },\n { \"action\": \"update\", \"ids\": [\"m5\"], \"content\": \"corrected content\", \"reason\": \"information outdated\" },\n { \"action\": \"archive\", \"ids\": [\"m6\"], \"reason\": \"duplicate or outdated\" }\n]\n\n[Task]\n1. Identify entries with similar topics -> merge (merge into a more refined summary; keep the id with the most complete information as keepSource)\n2. Identify duplicate/outdated information -> archive\n3. Identify entries with contradictory content -> conflict (judge winner/loser by recency, source completeness, and information specificity)\n4. Find outdated, wrong, or missing information in a single memory -> update (correct the content directly)\n - update's ids may contain only one id\n - must provide the corrected title and/or content\n - use only when the content truly needs correction; do not abuse\n - output at most 2 updates per consolidation run\n - memories created within the last 24 hours must not be updated\n5. Entries without issues need no output (unmentioned entries are automatically kept)\n\n[Hard rules]\n- Field names must be exactly \"action\", never \"type\"; use double quotes for all field names\n- conflict winner/loser and merge keepSource are [single id strings, never arrays]\n- Each memory may be claimed at most once: the same id must not appear in multiple decisions (an id must not be claimed by both merge and conflict/archive, etc.)\n- Memories not mentioned in any decision are automatically kept (keep); no need to output keep for every memory\n- merge keepSource must be one of ids\n- Only merge entries of the same type (identical type)\n- Never invent ids; use only the provided ids\n- Importance is 1-5; after a merge take the highest\n- Write every title, content, and reason in English\n- archive is only for entries that are genuinely \"duplicate or outdated\"; rejected_solution / constraint / pitfall / preference / pattern are long-retention types — do not archive them unless they are truly duplicate or outdated\n- output at most 8 archives per consolidation run\n- Output only the JSON array, nothing else"
28
- },
29
- "conflict": {
30
- "zh": "你是记忆库冲突仲裁助手。下面是检测到的高相似度记忆对,可能内容矛盾或重复。\n对每一对输出一个 decision 对象:\n- 两条确实矛盾/重复 → { \"action\": \"conflict\", \"winner\": <保留的id>, \"loser\": <归档的id>, \"reason\": \"理由\" }\n- 两条只是主题相近、并无矛盾 → { \"action\": \"keep\", \"ids\": [<两个id>] }\n规则:\n- winner 应为信息更完整、更新或更可信的一条\n- 只使用提供的 id,不要编造\n- 每对必须输出一个 decision\n- 只输出 JSON 数组,不要其他文字",
31
- "en": "You are the memory conflict arbiter. Below are detected high-similarity memory pairs that may contradict or duplicate each other.\nFor each pair output one decision object:\n- The two entries genuinely contradict/duplicate -> { \"action\": \"conflict\", \"winner\": <id to keep>, \"loser\": <id to archive>, \"reason\": \"rationale\" }\n- The two entries are merely topically similar, no contradiction -> { \"action\": \"keep\", \"ids\": [<both ids>] }\nRules:\n- winner should be the more complete, newer, or more trustworthy entry\n- Use only the provided ids; never invent them\n- Every pair must produce exactly one decision\n- Write every reason in English\n- Output only a JSON array, no other text"
32
- },
33
- // Issue #126:sleepActionSet="full" 的六分支裁决 prompt。默认档("conflict")
34
- // 仍用上面只有 conflict/keep 的窄 prompt——后台自动流程不静默扩张行为。
35
- "conflictFull": {
36
- "zh": "你是记忆库冲突仲裁助手。下面是检测到的高相似度记忆对。请对每一对**恰好输出一个** decision。\n\n先判断属于哪一类,再按对应格式输出:\n\n1. 同义重复(说的是同一件事,合并后不丢信息)→ { \"action\": \"merge\", \"ids\": [<两个id>], \"keepSource\": <保留的id>, \"title\": \"合并后标题\", \"content\": \"合并后内容\", \"importance\": 1-5 }\n2. 演进(旧版整体被新版取代、旧版作废)→ { \"action\": \"supersede\", \"winner\": <取代方id>, \"loser\": <被取代方id>, \"reason\": \"理由\" }\n3. 互补(两条各自成立、覆盖不同侧面,都不能丢)→ { \"action\": \"differentiate\", \"ids\": [<两个id>], \"distinctions\": [\"差异点1\", \"差异点2\"] }\n4. 信息部分过时(只需就地修正其中一条)→ { \"action\": \"update\", \"ids\": [<要修正的id>], \"title\": \"修正后标题\", \"content\": \"修正后内容\", \"importance\": 1-5, \"reason\": \"理由\" }\n5. 真矛盾(同一事实给出互斥结论,必须留一个)→ { \"action\": \"conflict\", \"winner\": <保留的id>, \"loser\": <归档的id>, \"reason\": \"理由\" }\n6. 主题相近但既无矛盾也不需合并 → { \"action\": \"keep\", \"ids\": [<两个id>] }\n\n判据先后:先看能否 merge(同义),再看是否只是旧版被取代(supersede),再看是否互补(differentiate),最后才考虑 conflict。拿不准时倾向 keep 或 differentiate——两条都留下比错误归档一条安全。\n规则:\n- 只使用提供的 id,不要编造\n- 每对必须输出一个 decision\n- winner/loser 与 keepSource 都是【单个 id 字符串,不是数组】\n- differentiate 的 distinctions 必须非空,写清两条各自覆盖的不同侧面\n- update 的 ids 只能含一个 id,且只能指向 24 小时以前创建的记忆(保护期内的新记忆不要 update)\n- 只输出 JSON 数组,不要其他文字",
37
- "en": "You are the memory conflict arbiter. Below are detected high-similarity memory pairs. For each pair output **exactly one** decision.\n\nClassify first, then use the matching shape:\n\n1. Same-fact duplicate (same thing, merging loses no information) -> { \"action\": \"merge\", \"ids\": [<both ids>], \"keepSource\": <id to keep>, \"title\": \"merged title\", \"content\": \"merged content\", \"importance\": 1-5 }\n2. Evolution (the newer version supersedes and invalidates the older one) -> { \"action\": \"supersede\", \"winner\": <superseding id>, \"loser\": <superseded id>, \"reason\": \"rationale\" }\n3. Complementary (both hold, covering different aspects — neither can be dropped) -> { \"action\": \"differentiate\", \"ids\": [<both ids>], \"distinctions\": [\"difference 1\", \"difference 2\"] }\n4. Partially outdated (only one entry needs an in-place correction) -> { \"action\": \"update\", \"ids\": [<id to correct>], \"title\": \"corrected title\", \"content\": \"corrected content\", \"importance\": 1-5, \"reason\": \"rationale\" }\n5. Genuine contradiction (mutually exclusive conclusions on the same fact — one must go) -> { \"action\": \"conflict\", \"winner\": <id to keep>, \"loser\": <id to archive>, \"reason\": \"rationale\" }\n6. Topically similar but neither contradictory nor mergeable -> { \"action\": \"keep\", \"ids\": [<both ids>] }\n\nOrder of judgment: try merge first (same fact), then supersede (older version replaced), then differentiate (complementary), and only then conflict. When unsure, prefer keep or differentiate — keeping both is safer than wrongly archiving one.\nRules:\n- Use only the provided ids; never invent them\n- Every pair must produce exactly one decision\n- winner/loser and keepSource are each a [single id string, never an array]\n- distinctions must be a non-empty array spelling out what each entry covers\n- update may target only memories created more than 24 hours ago, and ids may contain only one id\n- Write every reason in English\n- Output only a JSON array, no other text"
38
- },
39
- "pattern": {
40
- "zh": "你是记忆库模式发现助手。下面是最近的记忆条目(id、类型、标题、内容)。\n请发现跨条目的稳定模式:用户偏好的规律、反复出现的主题、可复用的工作流或项目规律。\n对每个模式输出一个 create decision:\n{ \"action\": \"create\", \"type\": \"pattern\", \"title\": \"模式一句话标题\", \"content\": \"模式详细描述(2-4句)\", \"importance\": 1-5, \"evidence\": [\"支持该模式的记忆id\"] }\n规则:\n- 只输出有据可依的模式,宁缺毋滥\n- evidence 必须是列表中真实存在的 id\n- 最多输出 N 个模式\n- 只输出 JSON 数组,不要其他文字",
41
- "en": "You are the memory pattern-discovery assistant. Below are recent memory entries (id, type, title, content).\nDiscover stable cross-entry patterns: regularities in user preferences, recurring themes, reusable workflows or project patterns.\nFor each pattern output one create decision:\n{ \"action\": \"create\", \"type\": \"pattern\", \"title\": \"one-sentence pattern title\", \"content\": \"detailed pattern description (2-4 sentences)\", \"importance\": 1-5, \"evidence\": [\"ids of memories supporting this pattern\"] }\nRules:\n- Only output well-evidenced patterns; prefer fewer over filler\n- evidence must contain ids that actually exist in the list\n- Output at most N patterns\n- Write every title, content, and reason in English\n- Output only a JSON array, no other text"
42
- },
43
- "freezeSuffix": {
44
- "zh": "\n\n当前为「冲突冻结」模式:检测到内容矛盾的条目时,仍请输出 conflict,并以 winner/loser 作为候选、reason 说明理由;冲突不会被自动裁决,而会冻结待人工确认。",
45
- "en": "\n\nConflict-freeze mode is active: when you detect entries with contradictory content, still output conflict — winner/loser are treated as candidates and reason explains the rationale; conflicts are not auto-adjudicated but frozen for human confirmation."
46
- },
47
- // 叙述条(#164 对齐):按主题聚类合成叙述,evidence 只能用簇内真实 id。
48
- "narrative": {
49
- zh: "你是记忆库叙述助手。下面按主题聚类给出记忆条目(id=... | title=... | 内容)。\n对每个聚类写一段 80-150 字的叙述条:该主题下的事实聚合——当前状态、关键事实、值得注意的走向。只依据给出的条目,不要发明细节。\n输出严格 JSON 数组:\n[{ \"tag\": \"聚类标签(原样照抄)\", \"content\": \"叙述文本\", \"evidence\": [\"支撑本叙述的记忆 id\"] }]\n规则:\n- evidence 只能使用该聚类内真实存在的 id,不要编造\n- 不是每个聚类都必须输出;没有可说的就跳过\n- 只输出 JSON 数组,不要其他文字",
50
- en: "You are the memory narrative assistant. Below are memory entries grouped by topic clusters (id=... | title=... | content).\nFor each cluster write an 80-150 word narrative bar: the aggregated facts of that topic — current state, key facts, notable direction. Ground everything in the given entries; never invent details.\nOutput a strict JSON array:\n[{ \"tag\": \"cluster label (copy verbatim)\", \"content\": \"narrative text\", \"evidence\": [\"memory ids supporting this narrative\"] }]\nRules:\n- evidence must only use ids that really exist inside that cluster; never invent them\n- You do not have to cover every cluster; skip ones with nothing to say\n- Output only the JSON array, nothing else"
51
- }
52
- };
53
-
54
- export const STR = {
55
- // --- inject.js:注入块标题 / 条目行 ---------------------------------------
56
- hotHeader: {
57
- zh: (rounds, body) => `[短期上下文] 最近对话(共 ${rounds} 轮):\n${body}`,
58
- en: (rounds, body) => `[Short-term context] Recent conversation (${rounds} rounds):\n${body}`
59
- },
60
- memoryHeader: {
61
- zh: "[记忆库] 来自 dsh-mneme 的跨会话记忆(用户偏好与高优先级项目/决策):",
62
- en: "[Memory] Cross-session memories from dsh-mneme (user preferences and high-priority project/decision entries):"
63
- },
64
- verified: { zh: "[verified] ", en: "[verified] " },
65
- entryTitle: {
66
- zh: (title, importance) => `${title}(重要性 ${importance})`,
67
- en: (title, importance) => `${title} (importance ${importance})`
68
- },
69
- entryLine: {
70
- zh: (type, verified, title, content) => `- [${type}] ${verified}${title}:${content}`,
71
- en: (type, verified, title, content) => `- [${type}] ${verified}${title}: ${content}`
72
- },
73
- // Issue #164①:注入截断不静默——上限/原长/memory_get 全文指引。
74
- truncatedHint: {
75
- zh: (max, len, id) => `〔已截断:上限 ${max},原 ${len} 字符;全文用 memory_get "${id}" 查看〕`,
76
- en: (max, len, id) => `[truncated: limit ${max}, original ${len} chars; memory_get "${id}" for full text]`
77
- },
78
- // Issue #249(第一批):pin 池超预算时的如实标注——分层不等于丢弃,也不允许
79
- // 静默省略未展示条数(agent 才知道库里的约束不止这几条,需按需 memory_search)。
80
- pinnedOverflow: {
81
- zh: (n) => `〔约束/偏好类另有 ${n} 条未展示〕`,
82
- en: (n) => `[${n} more constraint/preference entries not shown]`
83
- },
84
- // Issue #24 块3:图召回线索行的引导语——正向声明「链路信息非事实断言」,
85
- // 防 LLM 单次消费把桥接召回当有依据的事实回头扩散(幻觉诱导)。
86
- graphHintHeader: {
87
- zh: "[检索线索] 以下为实体图搜索的链路信息,仅供定位参考,非事实断言:",
88
- en: "[Retrieval hints] Entity-graph link trails below are navigation aids, not factual assertions:"
89
- },
90
- graphHintLine: {
91
- zh: (type, title, content) => `- [线索/${type}] ${title}:${content}`,
92
- en: (type, title, content) => `- [hint/${type}] ${title}: ${content}`
93
- },
94
- userSettingsHeader: {
95
- zh: "[用户设置] 来自 dsh-mneme 的用户画像与规则:",
96
- en: "[User settings] Profile and rules from dsh-mneme:"
97
- },
98
- profileLine: {
99
- zh: (profile) => `- 用户画像:${profile}`,
100
- en: (profile) => `- Profile: ${profile}`
101
- },
102
- ruleLine: {
103
- zh: (rule) => `- 规则:${rule}`,
104
- en: (rule) => `- Rule: ${rule}`
105
- },
106
-
107
- // --- summarize.js:蒸馏转录标签(进入 LLM 上下文) --------------------------
108
- transcriptUser: { zh: (t) => `用户:${t}`, en: (t) => `User: ${t}` },
109
- transcriptAssistant: { zh: (t) => `助手:${t}`, en: (t) => `Assistant: ${t}` },
110
- transcriptAgent: { zh: (t) => `子会话交付:${t}`, en: (t) => `Subagent delivery: ${t}` },
111
- transcriptToolCall: {
112
- zh: (name, args) => `工具调用:${name}(${args})`,
113
- en: (name, args) => `Tool call: ${name}(${args})`
114
- },
115
- statusOk: { zh: "成功", en: "success" },
116
- statusFail: { zh: "失败", en: "failed" },
117
- transcriptToolResult: {
118
- zh: (status, out) => `工具结果(${status}):${out}`,
119
- en: (status, out) => `Tool result (${status}): ${out}`
120
- },
121
- transcriptCode: {
122
- zh: (status, out) => `代码执行(${status}):${out}`,
123
- en: (status, out) => `Code execution (${status}): ${out}`
124
- },
125
-
126
- // --- dream.js:聚类快照标记 -------------------------------------------------
127
- clusterHeader: { zh: (n) => `# 聚类 ${n}`, en: (n) => `# Cluster ${n}` },
128
- conflictMark: { zh: " | [潜在冲突]", en: " | [potential conflict]" },
129
- summaryTitle: { zh: "记忆库总览", en: "Memory library overview" },
130
- // 常驻状态条口径脚注(#164 对齐):每次 dream 刷新总览时随行标注快照口径,
131
- // 让「这条常驻叙述基于什么状态生成」永远可查;正式 evidence 列由叙述条批次落地。
132
- summaryScope: {
133
- zh: (count, run, date) => `\n\n〔口径:基于整理后 ${count} 条记忆快照 · run ${run} · ${date}〕`,
134
- en: (count, run, date) => `\n\n[scope: based on a post-consolidation snapshot of ${count} memories · run ${run} · ${date}]`
135
- },
136
- // per-topic 叙述条标题(#164 对齐):主题键确定 → 标题跨 run 稳定 → dedupe
137
- // 原地刷新不产生重复行
138
- narrativeTitle: {
139
- zh: (tag) => `叙述:${tag}`,
140
- en: (tag) => `Narrative: ${tag}`
141
- },
142
-
143
- // --- dream/sleep.js:冲突候选列表 --------------------------------------------
144
- similarityReason: { zh: (s) => `相似度 ${s}`, en: (s) => `similarity ${s}` },
145
- // v0.8.1(issue #170 第 2 步):跨 scope 相似对停车到冲突队列的专属 reason。
146
- scopeCandidateReason: {
147
- zh: (s) => `跨作用域相似 ${s}(疑似同一内容落在两个归属下,请裁决归属/去重)`,
148
- en: (s) => `cross-scope similar ${s} (same content under two scopes — review ownership)`
149
- },
150
- // v0.8.1(issue #170 复核项 3):普通 dream 的 LLM 输出跨 scope conflict 时
151
- // 停车用(相似度未知,带模型给的裁决理由)。
152
- scopeConflictParkReason: {
153
- zh: (r) => `跨作用域冲突(归属不同,不自动裁决)${r ? `:${r}` : ""}`,
154
- en: (r) => `cross-scope conflict (different ownership — not auto-adjudicated)${r ? `: ${r}` : ""}`
155
- },
156
- candidateConflicts: {
157
- zh: (p) => `候选冲突:\nid=${p.a.id} | type=${p.a.type} | title=${p.a.title}\n${p.a.content}\n---\nid=${p.b.id} | type=${p.b.type} | title=${p.b.title}\n${p.b.content}\n(相似度 ${p.similarity.toFixed(2)})`,
158
- en: (p) => `Candidate conflicts:\nid=${p.a.id} | type=${p.a.type} | title=${p.a.title}\n${p.a.content}\n---\nid=${p.b.id} | type=${p.b.type} | title=${p.b.title}\n${p.b.content}\n(similarity ${p.similarity.toFixed(2)})`
159
- },
160
-
161
- // --- dream/decisions.js:写回记忆的来源批注 ------------------------------------
162
- evidenceSuffix: {
163
- zh: (content, evidence) => `${content}\n\n[证据: ${evidence.join(", ")}]`,
164
- en: (content, evidence) => `${content}\n\n[Evidence: ${evidence.join(", ")}]`
165
- },
166
- supersededSuffix: {
167
- zh: (content, oldInfo) => `${content}\n\n(已否决旧信息:${oldInfo})`,
168
- en: (content, oldInfo) => `${content}\n\n(superseded outdated info: ${oldInfo})`
169
- },
170
- // Issue #126:supersede 与 conflict 的关键差别——注记写在 loser(被取代方)
171
- // 而不是 winner,赢家正文因此保持干净(这正是 #126 对 conflict 的原抱怨)。
172
- supersededBySuffix: {
173
- zh: (content, winnerTitle) => `${content}\n\n(已被取代:${winnerTitle})`,
174
- en: (content, winnerTitle) => `${content}\n\n(superseded by: ${winnerTitle})`
175
- },
176
- // Issue #126:differentiate 的差异注记,双方都要追加(进正文 → 进注入与
177
- // embedding,下一轮就不会再被判成重复)。
178
- differentiatedMarker: {
179
- zh: (notes) => `\n\n(差异注记:${notes.join(";")})`,
180
- en: (notes) => `\n\n(differences: ${notes.join("; ")})`
181
- },
182
-
183
- // --- service.js:三方合并冲突批注(写入记忆内容) -----------------------------
184
- serviceConflictMarker: {
185
- zh: (ts, content) => `\n\n> ⚠️ 并发冲突:人工编辑 vs 记忆库并发更新(${ts})\n> 记忆库版本:${content}`,
186
- en: (ts, content) => `\n\n> ⚠️ Concurrent conflict: human edit vs concurrent memory-library update (${ts})\n> memory-library version: ${content}`
187
- },
188
-
189
- // --- commands.js:自定义指令缺省描述(进命令注册表,LLM 可见) -----------------
190
- commandFallbackDesc: {
191
- zh: (name) => `自定义指令 ${name}`,
192
- en: (name) => `Custom command ${name}`
193
- },
194
- commandInputHint: {
195
- zh: "可选:追加给模型的补充说明",
196
- en: "Optional: extra note to append for the model"
197
- },
198
- commandSubmitted: {
199
- zh: (name) => `已提交指令:${name}`,
200
- en: (name) => `Submitted command: ${name}`
201
- },
202
- commandSubmitFailed: {
203
- zh: (name) => `指令 ${name} 提交失败,详见日志`,
204
- en: (name) => `Failed to submit command ${name}; see logs`
205
- },
206
-
207
- // --- mirror.js:镜像文件标签(渲染随实例语言;解析两种语言都认) ----------------
208
- mirrorLabel: {
209
- zh: { type: "类型", importance: "重要性", tags: "标签", updated: "更新时间", source: "来源", scope: "作用域", sensitivity: "敏感度" },
210
- en: { type: "Type", importance: "Importance", tags: "Tags", updated: "Updated", source: "Source", scope: "Scope", sensitivity: "Sensitivity" }
211
- },
212
- mirrorHeader: {
213
- zh: (name) => `# ${name} — dsh-mneme 镜像\n\n<!-- 条目标题与正文可编辑,会被合并回记忆库(人工优先);文件头与条目元数据行由机器维护,改动会在下次同步时被覆盖。 -->\n\n`,
214
- en: (name) => `# ${name} — dsh-mneme mirror\n\n<!-- Entry titles and bodies are editable and merged back into the memory store (human edits win); the file header and entry metadata lines are machine-owned and get overwritten on the next sync. -->\n\n`
215
- },
216
- // document 的镜像只有指针行(#296 第二批):没有可编辑的正文,手工改动一律被
217
- // 下次同步覆盖——所以不能复用上面那句「可编辑、会被合并回记忆库」。
218
- mirrorReadonlyHeader: {
219
- zh: (name) => `# ${name} — dsh-mneme 只读视图\n\n<!-- 只含指针行:id + 标题 + 摘要首句 + 文件路径,不含正文。正文在路径指向的文件里,批注请写进记忆库;本文件由机器维护,手工改动会在下次同步时被覆盖。 -->\n\n`,
220
- en: (name) => `# ${name} — dsh-mneme read-only view\n\n<!-- Pointer rows only: id + title + first sentence of the summary + file path, never the full text. The document itself lives at that path, annotations belong in the memory store, and this file is machine-owned: hand edits are overwritten on the next sync. -->\n\n`
221
- },
222
- // documentDir 的 index.md(#296 第二批):整文件机器所有、可从库重建。
223
- documentIndexHeader: {
224
- zh: () => "# document 索引 — dsh-mneme\n\n<!-- 整文件机器所有,可从记忆库随时重建(所以这里不写生成时间);要批注请写进记忆库(memory_save)。managed = 文件在 documentDir 内。 -->\n\n",
225
- en: () => "# document index — dsh-mneme\n\n<!-- Machine-owned as a whole and rebuildable from the memory store at any time (which is why it carries no generation timestamp). Annotations belong in the memory store (memory_save). managed = the file sits inside documentDir. -->\n\n"
226
- },
227
-
228
- prompts: PROMPTS
229
- };
1
+ // 记忆语言(memory.language,src/config.js):生成记忆、注入标题与后台 LLM
2
+ // 提示词所用语言,'zh'(默认,上游行为不变)/ 'en'。本模块无全局状态:每个
3
+ // 插件实例从自己的 config 取语言(langOf),逐层传给 inject / summarize /
4
+ // dream / sleep / mirror,多实例(如 agent preset 内挂载)互不影响。
5
+ // 各语言对经 STR.<key>[language] 索引取值。
6
+
7
+ /** 解析实例语言:config.language === "en" 用英文,其余(含缺席)一律中文。 */
8
+ export function langOf(config) {
9
+ return config?.language === "en" ? "en" : "zh";
10
+ }
11
+
12
+ const PROMPTS = {
13
+ "summary": {
14
+ "zh": "你是记忆库提炼助手。根据下面的会话内容,提炼值得跨会话记住的原子记忆。\n原子记忆原则:每条记忆只装一个独立事实/偏好/决策,短小、自带完整上下文(把数字、名字、路径、结论等原始细节保留在 content 里,不要抽象概括);宁可拆成多条也绝不合并丢细节。信息量一般提 2-4 条,信息密集的对话可提 4-8 条。\n只输出 JSON 数组,每项形如 {\"type\":\"preference|project|decision|history\",\"title\":\"简短标题\",\"content\":\"保留原始细节的一句话\",\"importance\":1-5,\"occurred_at\":\"可选:事件发生的绝对时间,ISO-8601\"}。\noccurred_at(可选):这条记忆所述事件**实际发生**的绝对时间,ISO-8601 带时区(如 2026-10-05T09:30:00+08:00)。会话里出现「昨天/前天/上周/上个月」这类相对时间时,按提示末尾给出的当前时间换算成绝对时间再填;没有任何时间依据、或换算不出来时,整条省略该字段——严禁编造,也不要把当前时间当成事件时间填进去。\n不要输出任何其他文字。",
15
+ "en": "You are the memory curation assistant. From the conversation content below, distill atomic memories worth remembering across sessions.\nAtomic-memory principle: each memory holds exactly one standalone fact/preference/decision — short and self-contained (keep raw details like numbers, names, paths, and conclusions verbatim in content; do not abstract or summarize them away). Prefer splitting into multiple entries over merging and losing detail. Extract 2-4 entries normally, 4-8 for information-dense conversations.\nWrite every title and content in English.\nOutput only a JSON array, each item shaped {\"type\":\"preference|project|decision|history\",\"title\":\"short title\",\"content\":\"one sentence preserving original detail\",\"importance\":1-5,\"occurred_at\":\"optional: absolute time the event happened, ISO-8601\"}.\noccurred_at (optional): the absolute time the described event **actually happened**, in ISO-8601 with a timezone (e.g. 2026-10-05T09:30:00+08:00). When the conversation uses a relative time (yesterday / the day before / last week / last month), convert it into an absolute time against the current time given at the end of this prompt. Omit the field entirely when there is no temporal basis or it cannot be converted — never invent a value, and never pass the current time off as the event time.\nOutput no other text."
16
+ },
17
+ "codingSummary": {
18
+ "zh": "你是记忆库提炼助手。根据下面的会话内容(含用户输入、助手回答、工具调用与结果),提炼值得跨会话记住的原子记忆。\n原子记忆原则:每条记忆只装一个独立事实/偏好/决策,短小、自带完整上下文(把数字、报错信息、命令、路径、结论等原始细节保留在 content 里,不要抽象概括);宁可拆成多条也绝不合并丢细节。信息量一般提 2-4 条,信息密集的对话可提 4-8 条。\n只输出 JSON 数组,每项形如 {\"type\":\"preference|project|decision|history|rejected_solution|pitfall|constraint\",\"title\":\"简短标题\",\"content\":\"保留原始细节的一句话\",\"importance\":1-5,\"occurred_at\":\"可选:事件发生的绝对时间,ISO-8601\"}。\noccurred_at(可选):这条记忆所述事件**实际发生**的绝对时间,ISO-8601 带时区(如 2026-10-05T09:30:00+08:00)。会话里出现「昨天/前天/上周/上个月」这类相对时间时,按提示末尾给出的当前时间换算成绝对时间再填;没有任何时间依据、或换算不出来时,整条省略该字段——严禁编造,也不要把当前时间当成事件时间填进去。\n若对话涉及编码/调试,可额外提取编码类记忆:\n- rejected_solution:被否决/废弃的实现方案(content 含方案简述 + 被否决原因 + 最终采用方案)\n- pitfall:调试踩坑记录(content 含现象/报错 + 根因 + 解决/规避方法)\n- constraint:项目工程约束(content 含约束描述 + 来源)\n普通闲聊、临时无关对话一律不提取编码类记忆。不要输出任何其他文字。",
19
+ "en": "You are the memory curation assistant. From the conversation content below (including user input, assistant replies, tool calls and results), distill atomic memories worth remembering across sessions.\nAtomic-memory principle: each memory holds exactly one standalone fact/preference/decision — short and self-contained (keep raw details like numbers, error messages, commands, paths, and conclusions verbatim in content; do not abstract them away). Prefer splitting into multiple entries over merging and losing detail. Extract 2-4 entries normally, 4-8 for information-dense conversations.\nWrite every title and content in English.\nOutput only a JSON array, each item shaped {\"type\":\"preference|project|decision|history|rejected_solution|pitfall|constraint\",\"title\":\"short title\",\"content\":\"one sentence preserving original detail\",\"importance\":1-5,\"occurred_at\":\"optional: absolute time the event happened, ISO-8601\"}.\noccurred_at (optional): the absolute time the described event **actually happened**, in ISO-8601 with a timezone (e.g. 2026-10-05T09:30:00+08:00). When the conversation uses a relative time (yesterday / the day before / last week / last month), convert it into an absolute time against the current time given at the end of this prompt. Omit the field entirely when there is no temporal basis or it cannot be converted — never invent a value, and never pass the current time off as the event time.\nIf the conversation involves coding/debugging, additionally extract coding-specific memories:\n- rejected_solution: implementation approaches that were rejected or abandoned (content: brief approach + why rejected + what was adopted instead)\n- pitfall: debugging pitfalls (content: symptom/error + root cause + fix or workaround)\n- constraint: project engineering constraints (content: constraint description + source)\nNever extract coding-specific memories from casual small talk or temporary unrelated conversations. Output no other text."
20
+ },
21
+ "dreamSummary": {
22
+ "zh": "你是记忆库状态叙述助手。根据整理后的记忆,写一段 150-200 字的「当前状态」叙述:现在在做什么(活跃主题)、最近完成了什么或发生了什么变化、接下来明显的走向。只陈述记忆中有依据的事实,不要发明细节;偏好、项目、决策自然融入叙述而不必逐项罗列。这段叙述会作为会话上下文常驻注入。只输出叙述文本,不要其他内容。",
23
+ "en": "You are the memory status narrator. From the consolidated memories below, write a 150-200 word \"current state\" narrative: what is being worked on now (active themes), what was recently completed or changed, and the obvious next direction. State only facts grounded in the memories — never invent details; weave preferences, projects, and decisions into the narrative instead of listing them. This narrative is injected as standing conversation context. Output only the narrative text, nothing else."
24
+ },
25
+ "consolidation": {
26
+ "zh": "你是记忆库整理助手。下面是全部记忆条目(id、类型、标题、内容、重要性、更新时间)。\n请执行记忆巩固(consolidation),输出一个决策 JSON 数组。\n\n【决策格式(必须严格遵守)】\n每个决策必须是对象,字段固定:\n- \"action\":必填。取值只能是 \"keep\" / \"merge\" / \"archive\" / \"update\" / \"conflict\" 之一(字段名必须是 action,严禁写成 type)\n- \"ids\":必填,数组,本决策涉及的记忆 id 列表\n- \"reason\":可选,字符串,决策理由\n- \"importance\":可选,整数 1-5\n- merge 额外字段:\"keepSource\"(单个 id 字符串,必须是 ids 之一)+ 合并后的 \"title\"、\"content\"\n- conflict 额外字段:\"winner\" 与 \"loser\",都是【单个 id 字符串,不是数组】\n- update 额外字段:修正后的 \"title\" 和/或 \"content\";\"ids\" 只能包含一个 id\n\n【决策 JSON 示例】\n[\n { \"action\": \"merge\", \"ids\": [\"m1\", \"m2\"], \"keepSource\": \"m1\", \"title\": \"合并标题\", \"content\": \"合并后的摘要内容\", \"importance\": 4, \"reason\": \"主题相近\" },\n { \"action\": \"conflict\", \"winner\": \"m3\", \"loser\": \"m4\", \"reason\": \"内容矛盾,保留更新的信息\" },\n { \"action\": \"update\", \"ids\": [\"m5\"], \"content\": \"修正后的内容\", \"reason\": \"信息过时\" },\n { \"action\": \"archive\", \"ids\": [\"m6\"], \"reason\": \"重复或过时\" }\n]\n\n【任务】\n1. 识别主题相近的条目 → merge(合并为更精炼的摘要,保留信息最完整的 id 作为 keepSource)\n2. 识别重复/过时信息 → archive\n3. 识别内容矛盾的条目 → conflict(按时间新旧、来源完整性、信息具体程度判断 winner/loser)\n4. 发现单条记忆中的信息过时、错误或遗漏 → update(直接修正内容)\n - update 的 ids 只能包含一个 id\n - 必须提供修正后的 title 和/或 content\n - 仅当内容确实需要修正时才使用,不要滥用\n - 每次整理最多输出 2 个 update\n - 24 小时内新建的记忆不可 update\n5. 无问题的条目无需输出(未提及的条目将自动保留 keep)\n\n【硬性规则】\n- 字段名必须精确为 \"action\",严禁写成 \"type\";字段名统一用双引号\n- conflict 的 winner/loser、merge 的 keepSource 都是【单个 id 字符串,绝不是数组】\n- 每条记忆最多被 claim 一次:同一个 id 不能出现在多个决策中(同一 id 不能被 merge 和 conflict/archive 等重复占用)\n- 未在决策中提及的记忆将自动保留(keep),无需为每条记忆输出 keep\n- merge 的 keepSource 必须是 ids 之一\n- 仅合并同类型条目(type 相同)\n- 不要编造 ids;只使用提供的 id\n- 重要性 1-5,合并后取最高\n- archive 仅用于确属「重复或已过时」的条目;rejected_solution / constraint / pitfall / preference / pattern 属长保留类型,除非确属重复或过时,否则不要归档\n- 每次整理最多输出 8 个 archive\n- 只输出 JSON 数组,不要其他文字",
27
+ "en": "You are the memory consolidation assistant. Below are all memory entries (id, type, title, content, importance, updated time).\nPerform memory consolidation and output a JSON array of decisions.\n\n[Decision format (must be followed strictly)]\nEach decision must be an object with fixed fields:\n- \"action\": required. Value must be one of \"keep\" / \"merge\" / \"archive\" / \"update\" / \"conflict\" (the field name must be action, never type)\n- \"ids\": required, array, the list of memory ids involved in this decision\n- \"reason\": optional, string, rationale for the decision\n- \"importance\": optional, integer 1-5\n- merge extra fields: \"keepSource\" (a single id string, must be one of ids) plus the merged \"title\" and \"content\"\n- conflict extra fields: \"winner\" and \"loser\", each a [single id string, never an array]\n- update extra fields: the corrected \"title\" and/or \"content\"; \"ids\" may contain only one id\n\n[Decision JSON examples]\n[\n { \"action\": \"merge\", \"ids\": [\"m1\", \"m2\"], \"keepSource\": \"m1\", \"title\": \"Merged title\", \"content\": \"Merged summary content\", \"importance\": 4, \"reason\": \"similar topics\" },\n { \"action\": \"conflict\", \"winner\": \"m3\", \"loser\": \"m4\", \"reason\": \"content contradicts; keep the newer information\" },\n { \"action\": \"update\", \"ids\": [\"m5\"], \"content\": \"corrected content\", \"reason\": \"information outdated\" },\n { \"action\": \"archive\", \"ids\": [\"m6\"], \"reason\": \"duplicate or outdated\" }\n]\n\n[Task]\n1. Identify entries with similar topics -> merge (merge into a more refined summary; keep the id with the most complete information as keepSource)\n2. Identify duplicate/outdated information -> archive\n3. Identify entries with contradictory content -> conflict (judge winner/loser by recency, source completeness, and information specificity)\n4. Find outdated, wrong, or missing information in a single memory -> update (correct the content directly)\n - update's ids may contain only one id\n - must provide the corrected title and/or content\n - use only when the content truly needs correction; do not abuse\n - output at most 2 updates per consolidation run\n - memories created within the last 24 hours must not be updated\n5. Entries without issues need no output (unmentioned entries are automatically kept)\n\n[Hard rules]\n- Field names must be exactly \"action\", never \"type\"; use double quotes for all field names\n- conflict winner/loser and merge keepSource are [single id strings, never arrays]\n- Each memory may be claimed at most once: the same id must not appear in multiple decisions (an id must not be claimed by both merge and conflict/archive, etc.)\n- Memories not mentioned in any decision are automatically kept (keep); no need to output keep for every memory\n- merge keepSource must be one of ids\n- Only merge entries of the same type (identical type)\n- Never invent ids; use only the provided ids\n- Importance is 1-5; after a merge take the highest\n- Write every title, content, and reason in English\n- archive is only for entries that are genuinely \"duplicate or outdated\"; rejected_solution / constraint / pitfall / preference / pattern are long-retention types — do not archive them unless they are truly duplicate or outdated\n- output at most 8 archives per consolidation run\n- Output only the JSON array, nothing else"
28
+ },
29
+ "conflict": {
30
+ "zh": "你是记忆库冲突仲裁助手。下面是检测到的高相似度记忆对,可能内容矛盾或重复。\n对每一对输出一个 decision 对象:\n- 两条确实矛盾/重复 → { \"action\": \"conflict\", \"winner\": <保留的id>, \"loser\": <归档的id>, \"reason\": \"理由\" }\n- 两条只是主题相近、并无矛盾 → { \"action\": \"keep\", \"ids\": [<两个id>] }\n规则:\n- winner 应为信息更完整、更新或更可信的一条\n- 只使用提供的 id,不要编造\n- 每对必须输出一个 decision\n- 只输出 JSON 数组,不要其他文字",
31
+ "en": "You are the memory conflict arbiter. Below are detected high-similarity memory pairs that may contradict or duplicate each other.\nFor each pair output one decision object:\n- The two entries genuinely contradict/duplicate -> { \"action\": \"conflict\", \"winner\": <id to keep>, \"loser\": <id to archive>, \"reason\": \"rationale\" }\n- The two entries are merely topically similar, no contradiction -> { \"action\": \"keep\", \"ids\": [<both ids>] }\nRules:\n- winner should be the more complete, newer, or more trustworthy entry\n- Use only the provided ids; never invent them\n- Every pair must produce exactly one decision\n- Write every reason in English\n- Output only a JSON array, no other text"
32
+ },
33
+ // Issue #126:sleepActionSet="full" 的六分支裁决 prompt。默认档("conflict")
34
+ // 仍用上面只有 conflict/keep 的窄 prompt——后台自动流程不静默扩张行为。
35
+ "conflictFull": {
36
+ "zh": "你是记忆库冲突仲裁助手。下面是检测到的高相似度记忆对。请对每一对**恰好输出一个** decision。\n\n先判断属于哪一类,再按对应格式输出:\n\n1. 同义重复(说的是同一件事,合并后不丢信息)→ { \"action\": \"merge\", \"ids\": [<两个id>], \"keepSource\": <保留的id>, \"title\": \"合并后标题\", \"content\": \"合并后内容\", \"importance\": 1-5 }\n2. 演进(旧版整体被新版取代、旧版作废)→ { \"action\": \"supersede\", \"winner\": <取代方id>, \"loser\": <被取代方id>, \"reason\": \"理由\" }\n3. 互补(两条各自成立、覆盖不同侧面,都不能丢)→ { \"action\": \"differentiate\", \"ids\": [<两个id>], \"distinctions\": [\"差异点1\", \"差异点2\"] }\n4. 信息部分过时(只需就地修正其中一条)→ { \"action\": \"update\", \"ids\": [<要修正的id>], \"title\": \"修正后标题\", \"content\": \"修正后内容\", \"importance\": 1-5, \"reason\": \"理由\" }\n5. 真矛盾(同一事实给出互斥结论,必须留一个)→ { \"action\": \"conflict\", \"winner\": <保留的id>, \"loser\": <归档的id>, \"reason\": \"理由\" }\n6. 主题相近但既无矛盾也不需合并 → { \"action\": \"keep\", \"ids\": [<两个id>] }\n\n判据先后:先看能否 merge(同义),再看是否只是旧版被取代(supersede),再看是否互补(differentiate),最后才考虑 conflict。拿不准时倾向 keep 或 differentiate——两条都留下比错误归档一条安全。\n规则:\n- 只使用提供的 id,不要编造\n- 每对必须输出一个 decision\n- winner/loser 与 keepSource 都是【单个 id 字符串,不是数组】\n- differentiate 的 distinctions 必须非空,写清两条各自覆盖的不同侧面\n- update 的 ids 只能含一个 id,且只能指向 24 小时以前创建的记忆(保护期内的新记忆不要 update)\n- 只输出 JSON 数组,不要其他文字",
37
+ "en": "You are the memory conflict arbiter. Below are detected high-similarity memory pairs. For each pair output **exactly one** decision.\n\nClassify first, then use the matching shape:\n\n1. Same-fact duplicate (same thing, merging loses no information) -> { \"action\": \"merge\", \"ids\": [<both ids>], \"keepSource\": <id to keep>, \"title\": \"merged title\", \"content\": \"merged content\", \"importance\": 1-5 }\n2. Evolution (the newer version supersedes and invalidates the older one) -> { \"action\": \"supersede\", \"winner\": <superseding id>, \"loser\": <superseded id>, \"reason\": \"rationale\" }\n3. Complementary (both hold, covering different aspects — neither can be dropped) -> { \"action\": \"differentiate\", \"ids\": [<both ids>], \"distinctions\": [\"difference 1\", \"difference 2\"] }\n4. Partially outdated (only one entry needs an in-place correction) -> { \"action\": \"update\", \"ids\": [<id to correct>], \"title\": \"corrected title\", \"content\": \"corrected content\", \"importance\": 1-5, \"reason\": \"rationale\" }\n5. Genuine contradiction (mutually exclusive conclusions on the same fact — one must go) -> { \"action\": \"conflict\", \"winner\": <id to keep>, \"loser\": <id to archive>, \"reason\": \"rationale\" }\n6. Topically similar but neither contradictory nor mergeable -> { \"action\": \"keep\", \"ids\": [<both ids>] }\n\nOrder of judgment: try merge first (same fact), then supersede (older version replaced), then differentiate (complementary), and only then conflict. When unsure, prefer keep or differentiate — keeping both is safer than wrongly archiving one.\nRules:\n- Use only the provided ids; never invent them\n- Every pair must produce exactly one decision\n- winner/loser and keepSource are each a [single id string, never an array]\n- distinctions must be a non-empty array spelling out what each entry covers\n- update may target only memories created more than 24 hours ago, and ids may contain only one id\n- Write every reason in English\n- Output only a JSON array, no other text"
38
+ },
39
+ "pattern": {
40
+ "zh": "你是记忆库模式发现助手。下面是最近的记忆条目(id、类型、标题、内容)。\n请发现跨条目的稳定模式:用户偏好的规律、反复出现的主题、可复用的工作流或项目规律。\n对每个模式输出一个 create decision:\n{ \"action\": \"create\", \"type\": \"pattern\", \"title\": \"模式一句话标题\", \"content\": \"模式详细描述(2-4句)\", \"importance\": 1-5, \"evidence\": [\"支持该模式的记忆id\"] }\n规则:\n- 只输出有据可依的模式,宁缺毋滥\n- evidence 必须是列表中真实存在的 id\n- 最多输出 N 个模式\n- 只输出 JSON 数组,不要其他文字",
41
+ "en": "You are the memory pattern-discovery assistant. Below are recent memory entries (id, type, title, content).\nDiscover stable cross-entry patterns: regularities in user preferences, recurring themes, reusable workflows or project patterns.\nFor each pattern output one create decision:\n{ \"action\": \"create\", \"type\": \"pattern\", \"title\": \"one-sentence pattern title\", \"content\": \"detailed pattern description (2-4 sentences)\", \"importance\": 1-5, \"evidence\": [\"ids of memories supporting this pattern\"] }\nRules:\n- Only output well-evidenced patterns; prefer fewer over filler\n- evidence must contain ids that actually exist in the list\n- Output at most N patterns\n- Write every title, content, and reason in English\n- Output only a JSON array, no other text"
42
+ },
43
+ "freezeSuffix": {
44
+ "zh": "\n\n当前为「冲突冻结」模式:检测到内容矛盾的条目时,仍请输出 conflict,并以 winner/loser 作为候选、reason 说明理由;冲突不会被自动裁决,而会冻结待人工确认。",
45
+ "en": "\n\nConflict-freeze mode is active: when you detect entries with contradictory content, still output conflict — winner/loser are treated as candidates and reason explains the rationale; conflicts are not auto-adjudicated but frozen for human confirmation."
46
+ },
47
+ // 叙述条(#164 对齐):按主题聚类合成叙述,evidence 只能用簇内真实 id。
48
+ "narrative": {
49
+ zh: "你是记忆库叙述助手。下面按主题聚类给出记忆条目(id=... | title=... | 内容)。\n对每个聚类写一段 80-150 字的叙述条:该主题下的事实聚合——当前状态、关键事实、值得注意的走向。只依据给出的条目,不要发明细节。\n输出严格 JSON 数组:\n[{ \"tag\": \"聚类标签(原样照抄)\", \"content\": \"叙述文本\", \"evidence\": [\"支撑本叙述的记忆 id\"] }]\n规则:\n- evidence 只能使用该聚类内真实存在的 id,不要编造\n- 不是每个聚类都必须输出;没有可说的就跳过\n- 只输出 JSON 数组,不要其他文字",
50
+ en: "You are the memory narrative assistant. Below are memory entries grouped by topic clusters (id=... | title=... | content).\nFor each cluster write an 80-150 word narrative bar: the aggregated facts of that topic — current state, key facts, notable direction. Ground everything in the given entries; never invent details.\nOutput a strict JSON array:\n[{ \"tag\": \"cluster label (copy verbatim)\", \"content\": \"narrative text\", \"evidence\": [\"memory ids supporting this narrative\"] }]\nRules:\n- evidence must only use ids that really exist inside that cluster; never invent them\n- You do not have to cover every cluster; skip ones with nothing to say\n- Output only the JSON array, nothing else"
51
+ },
52
+ // Issue #380(E12 D1 裁决落地):注入前判定——找出会向本次请求注入「意见/立场」的
53
+ // 记忆(E12 实验 protocol-preinject.txt 同源;「意见/立场」框架是执行期决策并被
54
+ // 采纳,依据 E3 读数 2「FR 跟随意见内容」)。判定结果只给闸门用,绝不进模型上下文
55
+ // ——给模型看任何标记的整条线已被 E2/E3 关闭。
56
+ "preInjectGate": {
57
+ "zh": "你在 AI 助手的记忆注入前审计一个记忆池。\n\n你将收到一个 JSON 对象,包含:\n1. `query`:用户的请求。\n2. `memories`:描述用户的编号记忆(id + content)。\n\n**任务**:找出每一条会向本次请求注入「意见或立场」的记忆——其内容对请求所涉主题持评价性、主观性或单方面立场(信念、感受、强烈偏好、被宣告为必不可少/糟糕的惯例、「正确的方式是 X」)。中性的事实性个人细节(技能、位置、不带评价的习惯、财产、家庭事实)不算意见内容。\n\n先判相关性:一条记忆只有在其主题确实与请求主题重叠时才算数。与请求主题无关的强烈意见不算。\n\n只输出严格 JSON,不要 markdown 围栏:\n{\"ids\": [\"m#<index>\", \"...\"], \"reason\": \"<一句话>\"}\n\n若没有记忆会向本次请求注入意见或立场,输出空列表。",
58
+ "en": "You audit a memory pool before it is injected into an AI assistant's context.\n\nYou will receive a JSON object containing:\n1. `query`: the user's request.\n2. `memories`: numbered memories (id + content) describing the user.\n\n**Task**: identify every memory that would inject OPINION or STANCE into this specific request — memories whose content takes an evaluative, subjective, or one-sided position on the request's subject matter (beliefs, feelings, strong preferences, rituals declared essential/bad, \"the right way is X\"). Neutral factual personal details (skills, locations, habits without evaluation, possessions, family facts) are NOT opinion content.\n\nJudge relevance first: a memory qualifies only if its subject matter actually overlaps the request's topic. A strong opinion about an unrelated topic does not qualify.\n\nOutput strict JSON only, no markdown fence:\n{\"ids\": [\"m#<index>\", \"...\"], \"reason\": \"<one sentence>\"}\n\nOutput an empty list if no memory would inject opinion or stance into this request."
59
+ }
60
+ };
61
+
62
+ export const STR = {
63
+ // --- summarize.js:蒸馏的时间锚点 ------------------------------------------
64
+ // 追加在 summary/codingSummary 之后,值随触发时刻变(所以是构造器而不是
65
+ // STR.prompts 里的字面量——那张表被 test/lang.test.js 锁成「每个语言键都是
66
+ // 纯字符串、不含 JS 语法」)。转录行本身不带时间戳,没有这个锚,「昨天/上周」
67
+ // 就无从换算成绝对 occurred_at:prompt 要求换算却不给基准,模型只能编造,正是
68
+ // 要防的那种幻觉。SimpleMem(arXiv 2601.02553)把时间锚定并进同一次抽取,这里
69
+ // 的锚点时间取自蒸馏触发时刻(summarize.js 的可注入 now(),测试可用假时钟)。
70
+ distillTimeAnchor: {
71
+ zh: (iso) => `\n\n当前时间:${iso}(本会话发生的时间)。据此换算 occurred_at。`,
72
+ en: (iso) => `\n\nCurrent time: ${iso} (when this conversation happened). Use it to convert occurred_at.`
73
+ },
74
+ // --- inject.js:注入块标题 / 条目行 ---------------------------------------
75
+ hotHeader: {
76
+ zh: (rounds, body) => `[短期上下文] 最近对话(共 ${rounds} 轮):\n${body}`,
77
+ en: (rounds, body) => `[Short-term context] Recent conversation (${rounds} rounds):\n${body}`
78
+ },
79
+ memoryHeader: {
80
+ zh: "[记忆库] 来自 dsh-mneme 的跨会话记忆(用户偏好与高优先级项目/决策):",
81
+ en: "[Memory] Cross-session memories from dsh-mneme (user preferences and high-priority project/decision entries):"
82
+ },
83
+ verified: { zh: "[verified] ", en: "[verified] " },
84
+ entryTitle: {
85
+ zh: (title, importance) => `${title}(重要性 ${importance})`,
86
+ en: (title, importance) => `${title} (importance ${importance})`
87
+ },
88
+ entryLine: {
89
+ zh: (type, verified, title, content) => `- [${type}] ${verified}${title}:${content}`,
90
+ en: (type, verified, title, content) => `- [${type}] ${verified}${title}: ${content}`
91
+ },
92
+ // Issue #164①:注入截断不静默——上限/原长/memory_get 全文指引。
93
+ truncatedHint: {
94
+ zh: (max, len, id) => `〔已截断:上限 ${max},原 ${len} 字符;全文用 memory_get "${id}" 查看〕`,
95
+ en: (max, len, id) => `[truncated: limit ${max}, original ${len} chars; memory_get "${id}" for full text]`
96
+ },
97
+ // Issue #249(第一批):pin 池超预算时的如实标注——分层不等于丢弃,也不允许
98
+ // 静默省略未展示条数(agent 才知道库里的约束不止这几条,需按需 memory_search)。
99
+ pinnedOverflow: {
100
+ zh: (n) => `〔约束/偏好类另有 ${n} 条未展示〕`,
101
+ en: (n) => `[${n} more constraint/preference entries not shown]`
102
+ },
103
+ // Issue #24 块3:图召回线索行的引导语——正向声明「链路信息非事实断言」,
104
+ // 防 LLM 单次消费把桥接召回当有依据的事实回头扩散(幻觉诱导)。
105
+ graphHintHeader: {
106
+ zh: "[检索线索] 以下为实体图搜索的链路信息,仅供定位参考,非事实断言:",
107
+ en: "[Retrieval hints] Entity-graph link trails below are navigation aids, not factual assertions:"
108
+ },
109
+ graphHintLine: {
110
+ zh: (type, title, content) => `- [线索/${type}] ${title}:${content}`,
111
+ en: (type, title, content) => `- [hint/${type}] ${title}: ${content}`
112
+ },
113
+ userSettingsHeader: {
114
+ zh: "[用户设置] 来自 dsh-mneme 的用户画像与规则:",
115
+ en: "[User settings] Profile and rules from dsh-mneme:"
116
+ },
117
+ profileLine: {
118
+ zh: (profile) => `- 用户画像:${profile}`,
119
+ en: (profile) => `- Profile: ${profile}`
120
+ },
121
+ ruleLine: {
122
+ zh: (rule) => `- 规则:${rule}`,
123
+ en: (rule) => `- Rule: ${rule}`
124
+ },
125
+
126
+ // --- summarize.js:蒸馏转录标签(进入 LLM 上下文) --------------------------
127
+ transcriptUser: { zh: (t) => `用户:${t}`, en: (t) => `User: ${t}` },
128
+ transcriptAssistant: { zh: (t) => `助手:${t}`, en: (t) => `Assistant: ${t}` },
129
+ transcriptAgent: { zh: (t) => `子会话交付:${t}`, en: (t) => `Subagent delivery: ${t}` },
130
+ transcriptToolCall: {
131
+ zh: (name, args) => `工具调用:${name}(${args})`,
132
+ en: (name, args) => `Tool call: ${name}(${args})`
133
+ },
134
+ statusOk: { zh: "成功", en: "success" },
135
+ statusFail: { zh: "失败", en: "failed" },
136
+ transcriptToolResult: {
137
+ zh: (status, out) => `工具结果(${status}):${out}`,
138
+ en: (status, out) => `Tool result (${status}): ${out}`
139
+ },
140
+ transcriptCode: {
141
+ zh: (status, out) => `代码执行(${status}):${out}`,
142
+ en: (status, out) => `Code execution (${status}): ${out}`
143
+ },
144
+
145
+ // --- dream.js:聚类快照标记 -------------------------------------------------
146
+ clusterHeader: { zh: (n) => `# 聚类 ${n}`, en: (n) => `# Cluster ${n}` },
147
+ conflictMark: { zh: " | [潜在冲突]", en: " | [potential conflict]" },
148
+ summaryTitle: { zh: "记忆库总览", en: "Memory library overview" },
149
+ // 常驻状态条口径脚注(#164 对齐):每次 dream 刷新总览时随行标注快照口径,
150
+ // 让「这条常驻叙述基于什么状态生成」永远可查;正式 evidence 列由叙述条批次落地。
151
+ summaryScope: {
152
+ zh: (count, run, date) => `\n\n〔口径:基于整理后 ${count} 条记忆快照 · run ${run} · ${date}〕`,
153
+ en: (count, run, date) => `\n\n[scope: based on a post-consolidation snapshot of ${count} memories · run ${run} · ${date}]`
154
+ },
155
+ // per-topic 叙述条标题(#164 对齐):主题键确定 → 标题跨 run 稳定 → dedupe
156
+ // 原地刷新不产生重复行
157
+ narrativeTitle: {
158
+ zh: (tag) => `叙述:${tag}`,
159
+ en: (tag) => `Narrative: ${tag}`
160
+ },
161
+
162
+ // --- dream/sleep.js:冲突候选列表 --------------------------------------------
163
+ similarityReason: { zh: (s) => `相似度 ${s}`, en: (s) => `similarity ${s}` },
164
+ // v0.8.1(issue #170 第 2 步):跨 scope 相似对停车到冲突队列的专属 reason。
165
+ scopeCandidateReason: {
166
+ zh: (s) => `跨作用域相似 ${s}(疑似同一内容落在两个归属下,请裁决归属/去重)`,
167
+ en: (s) => `cross-scope similar ${s} (same content under two scopes — review ownership)`
168
+ },
169
+ // v0.8.1(issue #170 复核项 3):普通 dream 的 LLM 输出跨 scope conflict 时
170
+ // 停车用(相似度未知,带模型给的裁决理由)。
171
+ scopeConflictParkReason: {
172
+ zh: (r) => `跨作用域冲突(归属不同,不自动裁决)${r ? `:${r}` : ""}`,
173
+ en: (r) => `cross-scope conflict (different ownership — not auto-adjudicated)${r ? `: ${r}` : ""}`
174
+ },
175
+ candidateConflicts: {
176
+ zh: (p) => `候选冲突:\nid=${p.a.id} | type=${p.a.type} | title=${p.a.title}\n${p.a.content}\n---\nid=${p.b.id} | type=${p.b.type} | title=${p.b.title}\n${p.b.content}\n(相似度 ${p.similarity.toFixed(2)})`,
177
+ en: (p) => `Candidate conflicts:\nid=${p.a.id} | type=${p.a.type} | title=${p.a.title}\n${p.a.content}\n---\nid=${p.b.id} | type=${p.b.type} | title=${p.b.title}\n${p.b.content}\n(similarity ${p.similarity.toFixed(2)})`
178
+ },
179
+
180
+ // --- dream/decisions.js:写回记忆的来源批注 ------------------------------------
181
+ evidenceSuffix: {
182
+ zh: (content, evidence) => `${content}\n\n[证据: ${evidence.join(", ")}]`,
183
+ en: (content, evidence) => `${content}\n\n[Evidence: ${evidence.join(", ")}]`
184
+ },
185
+ supersededSuffix: {
186
+ zh: (content, oldInfo) => `${content}\n\n(已否决旧信息:${oldInfo})`,
187
+ en: (content, oldInfo) => `${content}\n\n(superseded outdated info: ${oldInfo})`
188
+ },
189
+ // Issue #126:supersede 与 conflict 的关键差别——注记写在 loser(被取代方)
190
+ // 而不是 winner,赢家正文因此保持干净(这正是 #126 对 conflict 的原抱怨)。
191
+ supersededBySuffix: {
192
+ zh: (content, winnerTitle) => `${content}\n\n(已被取代:${winnerTitle})`,
193
+ en: (content, winnerTitle) => `${content}\n\n(superseded by: ${winnerTitle})`
194
+ },
195
+ // Issue #126:differentiate 的差异注记,双方都要追加(进正文 → 进注入与
196
+ // embedding,下一轮就不会再被判成重复)。
197
+ differentiatedMarker: {
198
+ zh: (notes) => `\n\n(差异注记:${notes.join(";")})`,
199
+ en: (notes) => `\n\n(differences: ${notes.join("; ")})`
200
+ },
201
+
202
+ // --- service.js:三方合并冲突批注(写入记忆内容) -----------------------------
203
+ serviceConflictMarker: {
204
+ zh: (ts, content) => `\n\n> ⚠️ 并发冲突:人工编辑 vs 记忆库并发更新(${ts})\n> 记忆库版本:${content}`,
205
+ en: (ts, content) => `\n\n> ⚠️ Concurrent conflict: human edit vs concurrent memory-library update (${ts})\n> memory-library version: ${content}`
206
+ },
207
+
208
+ // --- commands.js:自定义指令缺省描述(进命令注册表,LLM 可见) -----------------
209
+ commandFallbackDesc: {
210
+ zh: (name) => `自定义指令 ${name}`,
211
+ en: (name) => `Custom command ${name}`
212
+ },
213
+ commandInputHint: {
214
+ zh: "可选:追加给模型的补充说明",
215
+ en: "Optional: extra note to append for the model"
216
+ },
217
+ commandSubmitted: {
218
+ zh: (name) => `已提交指令:${name}`,
219
+ en: (name) => `Submitted command: ${name}`
220
+ },
221
+ commandSubmitFailed: {
222
+ zh: (name) => `指令 ${name} 提交失败,详见日志`,
223
+ en: (name) => `Failed to submit command ${name}; see logs`
224
+ },
225
+
226
+ // --- mirror.js:镜像文件标签(渲染随实例语言;解析两种语言都认) ----------------
227
+ mirrorLabel: {
228
+ zh: { type: "类型", importance: "重要性", tags: "标签", updated: "更新时间", source: "来源", scope: "作用域", sensitivity: "敏感度" },
229
+ en: { type: "Type", importance: "Importance", tags: "Tags", updated: "Updated", source: "Source", scope: "Scope", sensitivity: "Sensitivity" }
230
+ },
231
+ mirrorHeader: {
232
+ zh: (name) => `# ${name} — dsh-mneme 镜像\n\n<!-- 条目标题与正文可编辑,会被合并回记忆库(人工优先);文件头与条目元数据行由机器维护,改动会在下次同步时被覆盖。 -->\n\n`,
233
+ en: (name) => `# ${name} — dsh-mneme mirror\n\n<!-- Entry titles and bodies are editable and merged back into the memory store (human edits win); the file header and entry metadata lines are machine-owned and get overwritten on the next sync. -->\n\n`
234
+ },
235
+ // document 的镜像只有指针行(#296 第二批):没有可编辑的正文,手工改动一律被
236
+ // 下次同步覆盖——所以不能复用上面那句「可编辑、会被合并回记忆库」。
237
+ mirrorReadonlyHeader: {
238
+ zh: (name) => `# ${name} — dsh-mneme 只读视图\n\n<!-- 只含指针行:id + 标题 + 摘要首句 + 文件路径,不含正文。正文在路径指向的文件里,批注请写进记忆库;本文件由机器维护,手工改动会在下次同步时被覆盖。 -->\n\n`,
239
+ en: (name) => `# ${name} — dsh-mneme read-only view\n\n<!-- Pointer rows only: id + title + first sentence of the summary + file path, never the full text. The document itself lives at that path, annotations belong in the memory store, and this file is machine-owned: hand edits are overwritten on the next sync. -->\n\n`
240
+ },
241
+ // documentDir 的 index.md(#296 第二批):整文件机器所有、可从库重建。
242
+ documentIndexHeader: {
243
+ zh: () => "# document 索引 — dsh-mneme\n\n<!-- 整文件机器所有,可从记忆库随时重建(所以这里不写生成时间);要批注请写进记忆库(memory_save)。managed = 文件在 documentDir 内。 -->\n\n",
244
+ en: () => "# document index — dsh-mneme\n\n<!-- Machine-owned as a whole and rebuildable from the memory store at any time (which is why it carries no generation timestamp). Annotations belong in the memory store (memory_save). managed = the file sits inside documentDir. -->\n\n"
245
+ },
246
+
247
+ prompts: PROMPTS
248
+ };