@modusensus/dsh-mneme 0.7.10 → 0.7.12

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 (158) hide show
  1. package/README.en.md +437 -0
  2. package/README.md +364 -166
  3. package/bin/cli.mjs +603 -0
  4. package/{dsh-mneme/cordis.patch.yml → cordis.patch.yml} +1 -0
  5. package/lib/api-standalone.js +264 -0
  6. package/{dsh-mneme/lib → lib}/api.js +133 -289
  7. package/{dsh-mneme/lib → lib}/client.js +1008 -922
  8. package/{dsh-mneme/lib → lib}/config.js +54 -99
  9. package/{dsh-mneme/lib → lib}/dream/decisions.js +94 -143
  10. package/{dsh-mneme/lib → lib}/dream/sleep.js +12 -152
  11. package/{dsh-mneme/lib → lib}/dream.js +22 -168
  12. package/{dsh-mneme/src → lib}/hot-memory.js +0 -7
  13. package/{dsh-mneme/lib → lib}/index.js +45 -42
  14. package/{dsh-mneme/lib → lib}/inject.js +11 -53
  15. package/{dsh-mneme/src → lib}/mirror.js +1 -12
  16. package/{dsh-mneme/lib → lib}/quality-filter.js +1 -3
  17. package/{dsh-mneme/lib → lib}/reranker.js +1 -4
  18. package/{dsh-mneme/lib → lib}/service.js +20 -410
  19. package/{dsh-mneme/src → lib}/settings.js +34 -64
  20. package/{dsh-mneme/lib → lib}/store.js +38 -450
  21. package/{dsh-mneme/lib → lib}/summarize.js +10 -10
  22. package/lib/tools.js +274 -0
  23. package/package.json +45 -19
  24. package/{dsh-mneme/scripts → scripts}/e2e-dsh.js +3 -4
  25. package/{dsh-mneme/scripts → scripts}/sync-lib.js +5 -10
  26. package/src/api-standalone.js +264 -0
  27. package/{dsh-mneme/src → src}/api.js +133 -289
  28. package/{dsh-mneme/src → src}/config.js +54 -99
  29. package/{dsh-mneme/src → src}/dream/decisions.js +94 -143
  30. package/{dsh-mneme/src → src}/dream/sleep.js +12 -152
  31. package/{dsh-mneme/src → src}/dream.js +22 -168
  32. package/{dsh-mneme/lib → src}/hot-memory.js +0 -7
  33. package/{dsh-mneme/src → src}/index.js +45 -42
  34. package/{dsh-mneme/src → src}/inject.js +11 -53
  35. package/{dsh-mneme/lib → src}/mirror.js +1 -12
  36. package/{dsh-mneme/src → src}/quality-filter.js +1 -3
  37. package/{dsh-mneme/src → src}/reranker.js +1 -4
  38. package/{dsh-mneme/src → src}/service.js +20 -410
  39. package/{dsh-mneme/lib → src}/settings.js +34 -64
  40. package/{dsh-mneme/src → src}/store.js +38 -450
  41. package/{dsh-mneme/src → src}/summarize.js +10 -10
  42. package/src/tools.js +274 -0
  43. package/{dsh-mneme/test → test}/api.test.js +45 -156
  44. package/{dsh-mneme/test → test}/client.test.js +10 -206
  45. package/{dsh-mneme/test → test}/config.test.js +0 -21
  46. package/{dsh-mneme/test → test}/dream.test.js +1 -160
  47. package/{dsh-mneme/test → test}/graph-api.test.js +0 -34
  48. package/{dsh-mneme/test → test}/hot-memory.test.js +0 -29
  49. package/test/inject.test.js +120 -0
  50. package/{dsh-mneme/test → test}/llm-audit.test.js +4 -4
  51. package/{dsh-mneme/test → test}/reasoning-effort.test.js +0 -27
  52. package/{dsh-mneme/test → test}/recall-layer.test.js +5 -26
  53. package/{dsh-mneme/test → test}/reranker.test.js +0 -43
  54. package/{dsh-mneme/test → test}/service-search.test.js +0 -25
  55. package/{dsh-mneme/test → test}/service.test.js +0 -106
  56. package/{dsh-mneme/test → test}/settings.test.js +16 -35
  57. package/{dsh-mneme/test → test}/sleep.test.js +4 -171
  58. package/test/standalone-api.test.js +326 -0
  59. package/{dsh-mneme/test → test}/store.test.js +0 -76
  60. package/{dsh-mneme/test → test}/summarize.test.js +16 -15
  61. package/test/tools.test.js +265 -0
  62. package/.github/workflows/publish.yml +0 -58
  63. package/.github/workflows/test.yml +0 -32
  64. package/CHANGELOG.md +0 -91
  65. package/CONTRIBUTING.md +0 -293
  66. package/SECURITY.md +0 -718
  67. package/docs/devlog/2026-08-14-dsh-mneme-dev-log.md +0 -247
  68. package/docs/devlog/2026-08-15-dsh-mneme-audit-stress-dev-log.md +0 -145
  69. package/docs/devlog/2026-08-15-dsh-mneme-pipeline-dev-log.md +0 -56
  70. package/docs/devlog/2026-08-15-dsh-mneme-reflection-dev-log.md +0 -77
  71. package/docs/devlog/2026-08-15-dsh-mneme-review-fixes-dev-log.md +0 -64
  72. package/docs/devlog/2026-08-15-dsh-mneme-semantic-dev-log.md +0 -90
  73. package/dsh-mneme/CHANGELOG.md +0 -419
  74. package/dsh-mneme/LICENSE +0 -21
  75. package/dsh-mneme/README.md +0 -501
  76. package/dsh-mneme/docs/AGENT_MEMORY_RESEARCH.md +0 -183
  77. package/dsh-mneme/docs/ENTITIES.md +0 -245
  78. package/dsh-mneme/docs/LOCAL_MODEL.md +0 -141
  79. package/dsh-mneme/docs/MIGRATION.md +0 -127
  80. package/dsh-mneme/docs/SEMANTIC.md +0 -256
  81. package/dsh-mneme/docs/SLEEP.md +0 -163
  82. package/dsh-mneme/lib/dream/tag-extractor.js +0 -156
  83. package/dsh-mneme/lib/heat.js +0 -136
  84. package/dsh-mneme/lib/parser/tag.js +0 -59
  85. package/dsh-mneme/lib/parser/wiki-link.js +0 -38
  86. package/dsh-mneme/lib/search/tag-boost.js +0 -61
  87. package/dsh-mneme/lib/tools.js +0 -458
  88. package/dsh-mneme/package-lock.json +0 -1936
  89. package/dsh-mneme/package.json +0 -83
  90. package/dsh-mneme/scripts/check-sync.js +0 -42
  91. package/dsh-mneme/src/client.js +0 -2242
  92. package/dsh-mneme/src/dream/tag-extractor.js +0 -156
  93. package/dsh-mneme/src/heat.js +0 -136
  94. package/dsh-mneme/src/parser/tag.js +0 -59
  95. package/dsh-mneme/src/parser/wiki-link.js +0 -38
  96. package/dsh-mneme/src/search/tag-boost.js +0 -61
  97. package/dsh-mneme/src/tools.js +0 -458
  98. package/dsh-mneme/test/boundary-v0625.test.js +0 -82
  99. package/dsh-mneme/test/directory.test.js +0 -134
  100. package/dsh-mneme/test/heat.test.js +0 -148
  101. package/dsh-mneme/test/inject.test.js +0 -206
  102. package/dsh-mneme/test/layered-types-stats.test.js +0 -144
  103. package/dsh-mneme/test/lib-smoke.test.js +0 -109
  104. package/dsh-mneme/test/normalize-decisions.test.js +0 -120
  105. package/dsh-mneme/test/provenance.test.js +0 -103
  106. package/dsh-mneme/test/recall-runs.test.js +0 -93
  107. package/dsh-mneme/test/sleep-heat.test.js +0 -112
  108. package/dsh-mneme/test/tag-boost.test.js +0 -125
  109. package/dsh-mneme/test/tag.test.js +0 -426
  110. package/dsh-mneme/test/tools.test.js +0 -674
  111. package/dsh-mneme/test/updated-at-semantics.test.js +0 -113
  112. package/dsh-mneme/test/wiki-link.test.js +0 -332
  113. package//346/250/252/345/271/205.png +0 -0
  114. /package/{dsh-mneme/lib → lib}/commands.js +0 -0
  115. /package/{dsh-mneme/lib → lib}/dream/clustering.js +0 -0
  116. /package/{dsh-mneme/lib → lib}/embedding.js +0 -0
  117. /package/{dsh-mneme/lib → lib}/entities/extractor.js +0 -0
  118. /package/{dsh-mneme/lib → lib}/local-embedder.js +0 -0
  119. /package/{dsh-mneme/lib → lib}/search/adaptive.js +0 -0
  120. /package/{dsh-mneme/lib → lib}/search/bm25.js +0 -0
  121. /package/{dsh-mneme/lib → lib}/vector-index.js +0 -0
  122. /package/{dsh-mneme/scripts → scripts}/benchmark-embed.js +0 -0
  123. /package/{dsh-mneme/scripts → scripts}/benchmark-recall.js +0 -0
  124. /package/{dsh-mneme/scripts → scripts}/benchmark-rerank.js +0 -0
  125. /package/{dsh-mneme/scripts → scripts}/stress-dsh.js +0 -0
  126. /package/{dsh-mneme/src → src}/commands.js +0 -0
  127. /package/{dsh-mneme/src → src}/dream/clustering.js +0 -0
  128. /package/{dsh-mneme/src → src}/embedding.js +0 -0
  129. /package/{dsh-mneme/src → src}/entities/extractor.js +0 -0
  130. /package/{dsh-mneme/src → src}/local-embedder.js +0 -0
  131. /package/{dsh-mneme/src → src}/search/adaptive.js +0 -0
  132. /package/{dsh-mneme/src → src}/search/bm25.js +0 -0
  133. /package/{dsh-mneme/src → src}/vector-index.js +0 -0
  134. /package/{dsh-mneme/test → test}/audit.test.js +0 -0
  135. /package/{dsh-mneme/test → test}/benchmark.test.js +0 -0
  136. /package/{dsh-mneme/test → test}/clustering.test.js +0 -0
  137. /package/{dsh-mneme/test → test}/commands.test.js +0 -0
  138. /package/{dsh-mneme/test → test}/conflict-freeze.test.js +0 -0
  139. /package/{dsh-mneme/test → test}/entities.test.js +0 -0
  140. /package/{dsh-mneme/test → test}/epistemic.test.js +0 -0
  141. /package/{dsh-mneme/test → test}/fnew-0112.test.js +0 -0
  142. /package/{dsh-mneme/test → test}/fnew-03.test.js +0 -0
  143. /package/{dsh-mneme/test → test}/helpers/dream-mock.js +0 -0
  144. /package/{dsh-mneme/test → test}/local-embedder.test.js +0 -0
  145. /package/{dsh-mneme/test → test}/mirror-dirty.test.js +0 -0
  146. /package/{dsh-mneme/test → test}/mirror-edit-digest.test.js +0 -0
  147. /package/{dsh-mneme/test → test}/mirror-generation.test.js +0 -0
  148. /package/{dsh-mneme/test → test}/mirror.test.js +0 -0
  149. /package/{dsh-mneme/test → test}/peer-blockers.test.js +0 -0
  150. /package/{dsh-mneme/test → test}/policy-epoch.test.js +0 -0
  151. /package/{dsh-mneme/test → test}/quality-filter.test.js +0 -0
  152. /package/{dsh-mneme/test → test}/recall-evals.test.js +0 -0
  153. /package/{dsh-mneme/test → test}/receipt-chain.test.js +0 -0
  154. /package/{dsh-mneme/test → test}/reflection.test.js +0 -0
  155. /package/{dsh-mneme/test → test}/search-fusion.test.js +0 -0
  156. /package/{dsh-mneme/test → test}/semantic.test.js +0 -0
  157. /package/{dsh-mneme/test → test}/stress.test.js +0 -0
  158. /package/{dsh-mneme/test → test}/vector-index.test.js +0 -0
package/README.en.md ADDED
@@ -0,0 +1,437 @@
1
+ <p align="center"><strong>中文 | <a href="docs/SEMANTIC.md">English (Semantic)</a></strong></p>
2
+
3
+ # dsh-mneme
4
+
5
+ English | [简体中文](README.md)
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@modusensus/dsh-mneme?color=blue&label=npm)](https://www.npmjs.com/package/@modusensus/dsh-mneme)
8
+ [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE)
9
+ [![Awesome](https://awesome-dsh-plugin.com/badge.svg)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
10
+ [![tests](https://img.shields.io/badge/tests-450%20passed-success)](https://github.com/modusensus/dsh-mneme)
11
+
12
+ > A cross-session memory plugin for DeepSeek Harness: it lets the Agent remember you, remember your projects, and organize memories automatically. **Mneme** (Μνήμη) — named after Mnemosyne, the Greek goddess of memory who presides over memory and dreams, just as autoDream consolidates memories in the background.
13
+
14
+ `dsh-mneme` is a [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) plugin that gives Agents persistent cross-session memory. It draws on Claude's **Dream mechanism** and the **autoDream** implementation ideas from cc-haha / Claude Code — it does not just **store** memories, it also **consolidates them automatically** (deduplication, merging, conflict adjudication, summary generation), so the memory store keeps getting more refined with use.
15
+
16
+ ## ✨ Features
17
+
18
+ ### Memory Storage (SQLite + Markdown Mirror)
19
+
20
+ - **SQLite primary storage**: `~/.dsh/memory/memory.db`, built-in `node:sqlite`, zero native dependencies
21
+ - **Markdown mirror**: `preferences.md` / `projects.md` / `decisions.md` / `history.md` / `summary.md` — human-readable and hand-editable (**manual edits take priority** when merged back into the store)
22
+ - **4+1 memory types**: `preference` / `project` / `decision` / `history` / `summary`
23
+ - **Mirror sync state machine (v0.3.6+)**: the mirror is strongly consistent with the primary store, with sync debt modeled via `generation` (desired round) / `applied_generation` (applied round)
24
+ - Business write operations **atomically increment** the desired generation **within their own transaction** — even a crash after COMMIT but before rendering recovers on restart from the durable debt, never silently skipped (v0.3.8)
25
+ - `generation` is incremented with atomic SQLite statements — zero loss under multi-process concurrency; a `CHECK` upper bound rejects negative values/overflow
26
+ - Per-type `committed / failed / pending` receipts; the health endpoint distinguishes `ok / degraded / unknown`
27
+ - State write failures are never silent: sync failures are logged and leave debt behind, converging automatically on restart
28
+
29
+ ### Model Tools (7)
30
+
31
+ | Tool | Function |
32
+ |------|------|
33
+ | `memory_save` | Save a memory (automatic dedup and merge by title) |
34
+ | `memory_search` | Full-text search (Chinese-substring friendly; vector semantic search can be enabled) |
35
+ | `memory_list` | Paginated listing by type (`include_archived=true` to view archived items) |
36
+ | `memory_update` | Modify an existing memory |
37
+ | `memory_delete` | Delete a memory |
38
+ | `memory_forget` | Suppress injection (down-weighted rather than deleted; recoverable) |
39
+ | `memory_archive` | Archive/restore memories (v0.2.5; archived items are hidden from listing/search/injection/consolidation, `archived=false` restores them) |
40
+
41
+ ### Auto Injection + Session Summary
42
+
43
+ - **Auto injection**: at the start of a new session, a memory digest is injected (the `summary` first, plus a few high-importance items)
44
+ - **Session summary**: at `turn/end`, an LLM distills the preferences/decisions/lessons of the session and stores them automatically (plugin-injected context is filtered out to avoid contamination)
45
+
46
+ ### autoDream Automatic Memory Consolidation 🧠
47
+
48
+ - **Trigger**: fires asynchronously and automatically once memory count > 10 or total characters > 5000 (never blocks writes)
49
+ - **Decision-list consolidation**: the LLM outputs a decision list of `keep` / `merge` / `archive` / `conflict` / `update` decisions, which the server validates and then applies item by item
50
+ - `merge`: merges entries with similar topics, keeping the most information-complete one
51
+ - `archive`: archives outdated/redundant entries (recoverable, never physically deleted)
52
+ - `conflict`: adjudicates contradictory information — the winner is kept, the loser is archived with a provenance note appended
53
+ - `update` (v0.2.1): directly corrects outdated/incorrect content of a single memory (single id / must actually change / not `summary` / 24h protection / ≤2 per run)
54
+ - **Failure tracking (v0.2.1)**: when the user corrects a memory, it is written to the `failure_memories` table (old value/new value), accumulating data for future self-evolution
55
+ - **Summary generation**: after consolidation, a "memory store overview" (single instance) is generated, injected with priority in the next session
56
+ - **Fail-safe**: illegal LLM output (unknown id / invalid action / cross-type merge / out-of-range importance) rejects the entire decision list — the memory store is never corrupted
57
+ - **Adjudication audit**: every run writes to the `dream_runs` audit table (input snapshot sha256 digest + full input snapshot + decision list + per-id disposition + receipt), replayable offline; merge / conflict / update are applied idempotently — replays and concurrent duplicate runs have no cumulative side effects; `update` records a `_before` snapshot
58
+
59
+ #### dreamMaxTokens Tuning Guide
60
+
61
+ The default `4096` covers ordinary memory stores. When the **memory volume is large** (tens of thousands of characters or more), the decision list and summary may exceed the default budget; scale it up by size:
62
+
63
+ | Memory store size | Recommended `dreamMaxTokens` |
64
+ |-----------|----------------------|
65
+ | Ordinary (<10k chars) | `4096` (default) |
66
+ | Medium (10k–50k chars) | `65536` |
67
+ | Large (>50k chars) | `131072` (cap) |
68
+
69
+ > With **reasoning models** (e.g. DeepSeek-R1-like), the model may spend the entire budget on reasoning and return an empty body (the log shows `no json array in llm output`). In that case, setting `dreamReasoningEffort` to `low` suppresses reasoning overhead and leaves the budget for the body output; the sleep side has the corresponding `sleepReasoningEffort`. The default `none` omits the field entirely, fully honoring the model's own default — behavior identical to earlier versions.
70
+
71
+ ### Sleep Mode: System-Level Sleep 💤 (v0.4.0, opt-in)
72
+
73
+ Upgrades autoDream's "passive threshold triggering" into "proactive scheduled maintenance + tiered compression". Once the system has been idle for `sleepIdleMinutes` minutes, deep maintenance runs automatically. **Off by default** (`sleepModeEnabled: false`); once enabled:
74
+
75
+ - **Interruptible**: implemented with AbortController — user activity aborts the current cycle (`noteWrite` resets the idle timer + the abort signal)
76
+ - **Serially safe**: sleep cycles go through the `service.enqueue` serial queue, strictly non-overlapping with autoDream; `minRefTimeMs` prevents memories recalled after the snapshot from being mistakenly demoted
77
+ - **Four-phase deep maintenance**:
78
+ 1. `conflict_resolution`: store-wide conflict resolution, strictness configurable in three levels (gentle 0.92 / normal 0.85 / aggressive 0.75)
79
+ 2. `archival_demotion`: tiered by `last_accessed_at` — not recalled for 30 days → compressed into a summary (original text kept in `_full_content`, losslessly restorable); 90 days → fully archived
80
+ 3. `pattern_discovery`: the LLM scans recent memories to distill patterns, producing `type=pattern` memories, with strict evidence validation to prevent fabrication
81
+ 4. `relation_completion`: detects orphaned entities and completes implicit relations (co-occurrence `related_to` / project `part_of` / technology `depends_on`)
82
+ - **Fail-safe**: each phase has its own try/catch — an LLM failure only skips the corresponding phase; without an LLM route, the pure-rule fallbacks (demotion/relations) still run
83
+ - **Audit continuity**: sleep cycles write to `dream_runs` with `run_type='sleep'`, sharing the audit table with autoDream for traceability
84
+
85
+ > See `docs/SLEEP.md` for configuration; see `docs/MIGRATION.md` for migration notes.
86
+
87
+ ### Web Memory Panel
88
+
89
+ Official settings panel → "Memory Store Settings" → "Memory" tab: browse by type, full-text search; once vector search is enabled, the "Semantic" toggle becomes available for vector recall.
90
+
91
+ ### User Settings (Profile / Rules) and Custom Commands ⚙️
92
+
93
+ Official settings panel → "Memory Store Settings" tab:
94
+
95
+ - **User profile**: a free-text passage describing the user (role, background, preferences), **injected into the system prompt every turn** so the Agent always follows it
96
+ - **Rules**: a list of behavioral rules the Agent must obey (e.g. "give the conclusion first"), likewise injected every turn
97
+ - **Custom commands**: register slash commands (`/name`); when triggered, the user-defined instruction content is handed to the Agent. Commands persist to SQLite, are automatically registered into the DSH command table at startup, and adding/removing takes effect in real time
98
+
99
+ > Profile and rules are injected through a separate `[用户设置]` (User Settings) block (higher priority than the memory store), and are injected even when memories are empty.
100
+
101
+ ### Vector Search (Semantic Search) 🔎
102
+
103
+ An optional capability: connect an OpenAI-compatible embeddings API so search can hit memories that are **literally different but semantically close**.
104
+
105
+ **Configuration**: official settings → "Memory Store Settings" → scroll to the bottom "Vector Search" section:
106
+
107
+ | Field | Description |
108
+ |------|------|
109
+ | `启用向量搜索` (Enable vector search) | Master switch; once on, a "Semantic" toggle appears in the memory panel |
110
+ | `API 地址 (Base URL)` | OpenAI-compatible endpoint, e.g. `https://api.openai.com/v1`; also supports SiliconFlow, Zhipu, local Ollama, etc. |
111
+ | `API Key` | The key for the corresponding service |
112
+ | `模型名` (Model name) | Embedding model, e.g. `text-embedding-3-small`, `text-embedding-v3`, `bge-m3`, etc. |
113
+
114
+ After saving, click "重建索引" (Rebuild Index) to backfill vectors for existing memories in bulk (newly written memories are embedded automatically). Then enter a query in the memory panel and click "语义" (Semantic) to recall semantically related results via vectors; if the vector service is unavailable, it automatically falls back to full-text search.
115
+
116
+ > ⚠️ The key is stored only in the local `user_settings` table of `~/.dsh/memory/memory.db`; it is never uploaded and never written into the code repository.
117
+ > You need an embedding model, not a rerank model: e.g. Alibaba Cloud's `text-embedding-v3` works, while `qwen3-vl-rerank` is a rerank model (it does not go through `/embeddings`).
118
+
119
+ ### Semantic Enhancement 🧠
120
+
121
+ Since v0.2, a **fully offline semantic memory engine** (local models + reranking + clustering):
122
+
123
+ - **Local embedding**: three selectable backends — ONNX (`Xenova/bge-small-zh-v1.5`, offline) / Ollama / OpenAI-compatible; failures fall back level by level automatically, with keyword search as the last resort
124
+ - **Rerank fine-ranking**: `Xenova/bge-reranker-base` cross-encodes recalled candidates for reranking, improving Top-K accuracy
125
+ - **autoDream semantic enhancement**: clusters memory vectors (`clusterMemories`) to automatically discover topically similar / potentially contradictory memories, making consolidation more precise
126
+ - **Search pipeline**: hybrid recall (keywords + vectors) → Rerank → Top-K
127
+
128
+ Configuration only requires setting `embedProvider` in `cordis.patch.yml` (default `openai`, preserving v0.1 behavior; switch it to `local` for offline). Upgrading requires no data migration.
129
+
130
+ ### Entity-Structured Memory (Entity Gene) 🧬
131
+
132
+ Since v0.3.0, a new **memory gene** layer: extracts **named entities**, **attributes with a timeline**, and **relations between entities** from memories, upgrading search from "literal keywords" to "precise recall by entity/attribute".
133
+
134
+ - **Three tables**: `entities` / `entity_attrs` (`valid_until` snapshot-style timeline) / `entity_relations`; opening an older database creates the tables automatically — idempotent, zero migration cost
135
+ - **Automatic extraction**: once `entityExtractionEnabled=true`, newly written memories fire-and-forget trigger LLM extraction (same-name entity dedup, attributes stored on the timeline, relations appended; failures never block writes)
136
+ - **Entity search** (`searchMemories` prefix routing, `entitySearchEnabled` on by default):
137
+ - `entity:阿尔托` → memories precisely associated via attributes (`_score 1.0`) rank ahead of keyword mentions (`_score 0.7`)
138
+ - `attr:国籍=芬兰` → memories exactly matching that attribute value
139
+ - `attr:国籍` → **all** currently valid memories for that attribute key (empty-value contract)
140
+ - **autoDream integration**: `update` decisions write a `supersedes` self-reference (the attribute version is superseded); `merge` decisions migrate the loser's attribute ownership to the keeper (void if the keeper already has a current value for the same key)
141
+
142
+ > 📖 See also: [Entity-Structured Memory Design](docs/ENTITIES.md) · [Semantic Enhancement Architecture](docs/SEMANTIC.md) · [Local Model Deployment Guide](docs/LOCAL_MODEL.md) · [Upgrading from v0.1](docs/MIGRATION.md)
143
+
144
+ ### Memory Quality Filter 🧼 (v0.4.6, on by default)
145
+
146
+ Before writing to the store, every memory receives a **heuristic quality score** (a pure function — no I/O, no shared state): meta-memory vocabulary (talking about the memory system itself), self-referential type tags, overly short content, high repetition, and near-duplication of recent memories all deduct points (0-100):
147
+
148
+ - `score ≥ 60`: stored normally
149
+ - `30 ≤ score < 60`: `quality_score` is persisted and injection ranking is down-weighted by `importance × quality/100` (degraded)
150
+ - `score < 30`: archived and flagged `low_quality` — still recallable via explicit search, but **never auto-injected**
151
+
152
+ `memoryQualityFilter.enabled` turns the feature off entirely; `archiveThreshold` / `degradeThreshold` / `minContentLength` are tunable.
153
+
154
+ ### LLM Usage Audit 📊 (v0.4.6, on by default)
155
+
156
+ Every **background LLM call** (autoDream consolidation + summary, autoSummarize compression) is written to the `llm_audit_logs` table: `tokens` / `duration` / `status` / `source` (which trigger produced it). Failed calls are recorded as `status=error` and never block the feature itself; `retentionDays` (default 90) purges expired rows at startup. Two new read-only APIs:
157
+
158
+ - `GET /api/dsh-mneme/semantic/llm-audit?page=&pageSize=&source=` — paginated query + filter by source
159
+ - `GET /api/dsh-mneme/semantic/llm-audit/stats?days=` — budget aggregated by source over the last N days (tokens / calls / failures)
160
+
161
+ > Read-only endpoints; like list/search/semantic, they remain open even after `apiToken` is set.
162
+
163
+ ## 🆕 Recent Release Highlights
164
+
165
+ | Version | Highlights |
166
+ |------|------|
167
+ | **v0.4.2** | autoSummarize custom model: the `summarizeProvider`/`summarizeModel` config options let you independently designate a lightweight model (e.g. qwen3.6-plus) for session summaries, saving main-model tokens; 473 tests green |
168
+ | **v0.4.0** | System-level Sleep Mode: idle-triggered four-phase deep maintenance (conflict resolution / archival demotion / pattern discovery / relation completion), interruptible, serially safe, fail-safe; tiered compression releases cold memories; 471 tests green |
169
+ | **v0.3.9** | Fixed 4 FAILs from the third-party audit: CAS made atomic within the same transaction, mirror degraded-receipt passthrough, per-type physical terminal-state convergence, strict integer validation for generation and stabilized concurrent initialization |
170
+ | **v0.3.8** | All 6 runtime-blocking findings from the audit peer re-review fixed: desired generation atomically incremented within the same transaction (the crash window no longer skips silently), sync failures not silent, atomic generation increments (zero loss across processes), per-type committed/failed/pending receipts, explicit unknown on read failure, generation upper-bound/negative CHECK |
171
+ | **v0.3.7** | Startup race fix: vector rebuild failing after a restart following manual edits to the md mirror (backfill moved to after init readiness + scheduleEmbed readiness gate) |
172
+ | **v0.3.6** | Mirror sync state machine: generation/applied_generation debt modeling, F-NEW-03 mirror health status, persistent dirty + recoverMirror at startup |
173
+ | **v0.3.0** | Memory gene: entity/attribute/relation three tables + timeline + entity search + autoDream supersedes |
174
+
175
+ ## 🗺️ Evolution Roadmap
176
+
177
+ | Version | Status | Theme | Description |
178
+ |------|------|------|------|
179
+ | v0.2.x | ✅ Done | Semantic enhancement + reflection updates | Local embedding/rerank/clustering, `failure_memories` failure tracking |
180
+ | v0.3.0 | ✅ Done | Memory gene | entities/attrs/relations three tables + timeline + entity search |
181
+ | v0.3.6–0.3.8 | ✅ Done | Mirror consistency + audit hardening | generation sync state machine, 6 audit-peer runtime-blocking fixes, 450 tests green |
182
+ | v0.3.9 | ✅ Done | Audit hardening A/B/D/F | compareAndUpdate same-transaction atomicity, degraded receipts, per-type physical terminal state, integer fail-closed, stable concurrent initialization |
183
+ | **v0.4.0** | ✅ Done | System-level Sleep Mode | Idle-triggered four-phase deep maintenance (conflict resolution / archival demotion / pattern discovery / relation completion), tiered compression, interruptible serial fail-safe; 471 tests green |
184
+ | **v0.4.2** | ✅ Done | autoSummarize custom model | `summarizeProvider`/`summarizeModel` config options, letting you independently designate a lightweight model (e.g. qwen3.6-plus) for session summaries and save main-model tokens; 473 tests green |
185
+ | **v0.4.3** | ✅ Done | autoDream large-memory fix | issue#9 B+A: `dreamMaxTokens` cap raised 32768→131072 + `dreamReasoningEffort`/`sleepReasoningEffort` reasoning toggles (`none` by default, main conversation unaffected); 478 tests green |
186
+ | **v0.4.4** | ✅ Done | autoDream decision coverage fix | issue#9 plan C: sliding window `dreamMaxSnapshotSize` (default 200, truncated by updated_at descending) + implicit keep `dreamImplicitKeep` (default true) + coverage floor `dreamMinExplicitCoverage` (default 50%) + fixed decision schema; 487 tests green |
187
+ | **v0.4.5** | ✅ Done | Epistemic trust + recall eval | Memory credibility grading `trustEpistemicWeighting` (observation>inferred>subjective: retrieval ranking favors high-credibility memories, injection tags `[verified]`, dream merge/conflict favors the more credible side; opt-in, off by default) + retrieval evaluation `evaluateRetrieval` persisted to `recall_evals` (`evalPersistTestResults` opt-in, off by default; production retrieval always goes through `recall_runs`, unconditionally isolated); 518 tests green |
188
+ | **v0.4.6** | ✅ Done | 8 fixes (vector pipeline + injection/quality/audit) | Vector pipeline fixes (embedSingle adaptation / `autoReindexOnBoot` backfill of existing data / `vector_meta` metadata) + injection semantic recall `hybridInject` + same-title append `content_history` + injection length caps (300 per item / 1500 per block) + memory quality filter `memoryQualityFilter` + LLM usage audit `llmAudit` (table + instrumentation + read-only APIs); 553 tests green |
189
+ | **v0.4.7** | ✅ Done | Idempotent schema migrations | When the same db is opened concurrently, the `PRAGMA table_info` check and ALTER are non-atomic and may repeat `ADD COLUMN`, failing with a duplicate column name; switched to an `addColumn` helper that swallows the race (try/catch), unifying all 12 migration sites |
190
+ | **v0.5.0+** | 🚀 Long-term | Self-evolving memory | Interest drift tracking + cross-workspace memory sharing (pending DSH support) |
191
+
192
+ > All new capabilities ship as **toggleable features** (enabled/disabled via configuration), conservatively on by default and never breaking existing behavior. The `failure_memories` table and the autoDream decision engine have already paved the way for future reflective growth.
193
+
194
+ ## 📦 Installation
195
+
196
+ ### Prerequisites
197
+
198
+ - [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH)
199
+ - Node 24+ (`node:sqlite`)
200
+
201
+ ### Installation Steps
202
+
203
+ #### Option 1: npm install (recommended)
204
+
205
+ dsh-mneme is a **bundle** (it declares a `dsh.bundle` manifest); installation activates it automatically, no manual configuration required:
206
+
207
+ ```bash
208
+ # 1. 安装插件(自动注册 bundle 层)
209
+ dsh plugin --profile web add @modusensus/dsh-mneme
210
+
211
+ # 2. 重启
212
+ dsh web
213
+ ```
214
+
215
+ > For custom configuration (thresholds, delays, etc.), override the defaults under `id: dsh-mneme` in `~/.dsh/profiles/web/cordis.patch.yml` (see the configuration table below).
216
+
217
+ #### Option 2: Install from source
218
+
219
+ ```bash
220
+ git clone https://github.com/modusensus/dsh-mneme.git
221
+ cd dsh-mneme
222
+ dsh plugin --profile web add .
223
+ dsh web
224
+ ```
225
+
226
+ #### Custom configuration (optional)
227
+
228
+ It works out of the box with the defaults. To adjust, override in `~/.dsh/profiles/web/cordis.patch.yml`:
229
+
230
+ ```yaml
231
+ - id: dsh-mneme
232
+ name: '@modusensus/dsh-mneme'
233
+ config:
234
+ memoryDir: ~/.dsh/memory
235
+ autoInject: true
236
+ autoSummarize: true
237
+ maxInjectedItems: 5
238
+ importanceThreshold: 3
239
+ autoDream: true
240
+ dreamThresholdCount: 10
241
+ dreamThresholdChars: 5000
242
+ dreamDelayMs: 2000
243
+ ```
244
+
245
+ ## ⚙️ Configuration
246
+
247
+ | Key | Default | Description |
248
+ |----|--------|------|
249
+ | `memoryDir` | `~/.dsh/memory` | Memory storage directory (SQLite + Markdown) |
250
+ | `autoInject` | `true` | Automatically inject memories at session start |
251
+ | `autoSummarize` | `true` | Automatically distill a summary at session end |
252
+ | `summarizeProvider` / `summarizeModel` | empty | LLM route override for summaries (empty = use the current session model); a lightweight model is recommended to save main-model tokens |
253
+ | `maxInjectedItems` | `5` | Maximum number of memories to inject |
254
+ | `importanceThreshold` | `3` | Minimum importance for injection (1-5) |
255
+ | `autoDream` | `true` | Automatic memory consolidation switch |
256
+ | `dreamThresholdCount` | `10` | Memory count threshold that triggers consolidation |
257
+ | `dreamThresholdChars` | `5000` | Total character threshold that triggers consolidation |
258
+ | `dreamDelayMs` | `2000` | Asynchronous consolidation delay (debounce) |
259
+ | `dreamProvider` / `dreamModel` | empty | LLM route fallback for dream (defaults to the agent's default model) |
260
+ | `dreamMaxTokens` | `4096` | Maximum tokens per dream LLM call (cap 131072; increase for large memory stores — see the tuning guide below) |
261
+ | `dreamReasoningEffort` | `none` | Reasoning-effort passthrough for the dream LLM: `low` / `medium` / `high` / `none` (`none` = omit the field and use the model default; set `low` when a reasoning model exhausts its budget on reasoning and produces an empty body) |
262
+ | `apiToken` | empty | Optional API auth token; once set, write operations and key endpoints require `Authorization: Bearer <apiToken>` |
263
+ | `embedProvider` | `openai` | Semantic backend: `openai` (default, v0.1-compatible) / `local` (ONNX offline) / `ollama` |
264
+ | `localEmbedModel` | `Xenova/bge-small-zh-v1.5` | Local ONNX embedding model |
265
+ | `localEmbedDimension` | `512` | Local embedding vector dimension |
266
+ | `localEmbedDevice` | `cpu` | Local inference device: `cpu` / `gpu` |
267
+ | `localEmbedBatchSize` | `8` | Local embedding batch size (1-64) |
268
+ | `ollamaBaseUrl` | `http://localhost:11434` | Ollama service address |
269
+ | `ollamaModel` | `nomic-embed-text` | Ollama embedding model |
270
+ | `embedModelCacheDir` | empty | Model cache directory (empty = user-level `~/.dsh/mneme/models`) |
271
+ | `embedModelMirror` | `https://hf-mirror.com` | Mirror source for model downloads |
272
+ | `vectorSearchTopK` | `20` | Top-K returned by vector search |
273
+ | `vectorSearchThreshold` | `0.65` | Vector search similarity threshold |
274
+ | `hybridSearchVectorWeight` | `0.6` | Vector weight in hybrid search |
275
+ | `hybridSearchKeywordWeight` | `0.4` | Keyword weight in hybrid search |
276
+ | `rerankEnabled` | `false` | Whether to enable rerank fine-ranking (the local onnxruntime model loads only when explicitly enabled) |
277
+ | `rerankProvider` | `none` | Rerank backend: `local` / `none` (default `none`) |
278
+ | `rerankModel` | `Xenova/bge-reranker-base` | Rerank cross-encoding model |
279
+ | `rerankBatchSize` | `8` | Rerank batch size |
280
+ | `rerankMaxCandidates` | `30` | Maximum number of rerank candidates |
281
+ | `rerankScoreThreshold` | `0.1` | Rerank score threshold (candidates below it are dropped) |
282
+ | `reflectionUpdateEnabled` | `true` | Master switch for `update` decisions |
283
+ | `reflectionFailureTracking` | `true` | Master switch for failure tracking |
284
+ | `reflectionUpdateMaxPerRun` | `2` | Maximum `update` decisions per consolidation run |
285
+ | `reflectionUpdateMinAgeHours` | `24` | Protection period for newly created memories (hours) |
286
+ | `entityExtractionEnabled` | `false` | Master switch for entity extraction (v0.3.0; always available at the storage layer) |
287
+ | `entityExtractionModel` | empty | Dedicated extraction model (empty = use the agent's default model) |
288
+ | `entityExtractionMaxEntities` | `10` | Maximum entities per extraction |
289
+ | `entityExtractionMaxAttrs` | `20` | Maximum attributes per entity |
290
+ | `entitySearchEnabled` | `true` | Switch for `entity:` / `attr:` prefix search |
291
+ | `trustEpistemicWeighting` | `false` | Memory credibility weighting (v0.4.5, opt-in, off by default): memories are graded by source as `observation` > `inferred` > `subjective`; when enabled, retrieval ranking favors high-credibility memories, injection tags observation entries `[verified]`, and dream merge/conflict favors the more credible side; when off, `epistemic_status` is only persisted on save and does not participate in behavior |
292
+ | `evalPersistTestResults` | `false` | Retrieval evaluation persistence (v0.4.5, opt-in, off by default): when enabled, `evaluateRetrieval` writes precision/recall/mrr snapshots into `recall_evals`; when off it only returns them to the caller without persisting. Production `searchMemories` auditing always goes through `recall_runs`, unconditionally never touching `recall_evals` |
293
+ | `autoReindexOnBoot` | `true` | When existing memories lack embeddings and vectors are configured, a delayed, rate-limited background backfill rebuild starts after boot (set to `false` for manual rebuild only) |
294
+ | `hybridInject` | `true` | Semantic-recall-first injection (v0.4.6, Bug4): when `injectCandidates` receives a non-empty query, it first recalls candidates via the vector index semantically, then fills in/deduplicates with rule-based filtering; empty query / no vectors falls back to the old logic |
295
+ | `memoryQualityFilter` | `{enabled:true, archiveThreshold:30, degradeThreshold:60, minContentLength:10}` | Memory quality filter (v0.4.6, on by default): heuristic 0-100 scoring before write; meta-memory vocabulary/self-reference/overly short/duplicate/near-duplicate content deduct points; ≥60 stored normally, 30-60 down-weighted (injection ranked by importance×quality/100), <30 archived and flagged `low_quality` (still recallable via explicit search, never auto-injected) |
296
+ | `llmAudit` | `{enabled:true, retentionDays:90}` | LLM usage audit (v0.4.6, on by default): every background LLM call (autoDream/autoSummarize) writes `llm_audit_logs` (tokens/duration/status/source); failures are recorded as error without blocking; read-only APIs `/api/dsh-mneme/semantic/llm-audit` + `/llm-audit/stats` |
297
+
298
+ > 🔐 **API security**: DSH has no built-in authentication and by default listens only on `127.0.0.1`. The plugin API is open by default (so the web panel works out of the box). For protection (e.g. when exposed to a LAN), set `apiToken` in the configuration: write operations (profile/rules/commands) and key endpoints (`vector-config`, `vector-reindex`) require `Authorization: Bearer <token>` (the frontend settings panel accepts the same token), while the read-only `list` / `search` / `semantic` endpoints remain open. The `apiKey` returned by `/api/dsh-mneme/vector-config` is masked (`sk-***…`), while the stored plaintext is kept for actual calls; the frontend sending back an empty or masked value means "do not change the key".
299
+
300
+ ## External API & CLI
301
+
302
+ Besides DSH's internal port, the plugin can also run a **standalone HTTP external API** (default `http://127.0.0.1:8790`, Bearer token auth) so other plugins, CLI scripts, or desktop tools can read and write memories without depending on DSH's internal port.
303
+
304
+ ### Enabling & Authentication
305
+
306
+ - Enable the external API in the plugin settings (it listens on `127.0.0.1:8790` by default, local machine only);
307
+ - The access token can be found in the plugin settings / the panel under "Settings → External Access";
308
+ - Except for `GET /health` (no auth), all routes require an `Authorization: Bearer <token>` header; an invalid token returns `401 {"error":"unauthorized"}`.
309
+
310
+ Main routes:
311
+
312
+ | Method | Route | Description |
313
+ |--------|-------|-------------|
314
+ | `GET` | `/health` | Health check (no auth), returns `{ok:true}` |
315
+ | `GET` | `/status` | Version, memory stats, entity count, uptime |
316
+ | `GET` | `/memories?limit&offset&type&minImportance&source&order=chrono` | List memories with pagination |
317
+ | `GET` | `/memories/:id` | A single memory |
318
+ | `POST` | `/memories` | Create a memory `{type,title,content,importance?,tags?,source?}` |
319
+ | `DELETE` | `/memories/:id` | Delete a memory |
320
+ | `GET` | `/search?q&mode=keyword\|vector\|auto&topK` | Search (keyword / vector / auto) |
321
+
322
+ ### curl Examples
323
+
324
+ ```bash
325
+ # Service status
326
+ curl -s -H "Authorization: Bearer $DSH_MNEME_TOKEN" http://127.0.0.1:8790/status
327
+
328
+ # List the 5 most recent memories
329
+ curl -s -H "Authorization: Bearer $DSH_MNEME_TOKEN" \
330
+ "http://127.0.0.1:8790/memories?limit=5"
331
+
332
+ # Add a decision memory
333
+ curl -s -X POST http://127.0.0.1:8790/memories \
334
+ -H "Authorization: Bearer $DSH_MNEME_TOKEN" \
335
+ -H "Content-Type: application/json" \
336
+ -d '{"type":"decision","title":"Adopt SQLite","content":"Storage layer uses node:sqlite","importance":4,"tags":["storage"]}'
337
+ ```
338
+
339
+ ### Installing the CLI
340
+
341
+ The plugin ships a zero-dependency CLI (published with the npm package):
342
+
343
+ ```bash
344
+ npm i -g @modusensus/dsh-mneme
345
+ dsh-mneme --help
346
+ ```
347
+
348
+ On first use, configure the server URL and token (you can also use the `DSH_MNEME_URL` / `DSH_MNEME_TOKEN` environment variables, or override temporarily with `--url` / `--token`):
349
+
350
+ ```bash
351
+ dsh-mneme config set http://127.0.0.1:8790 <your-token>
352
+ ```
353
+
354
+ ### Common CLI Commands
355
+
356
+ ```bash
357
+ dsh-mneme status # Service status
358
+ dsh-mneme list --type project --limit 10 # List memories
359
+ dsh-mneme search "deploy pipeline" --mode vector --topk 5 # Semantic search
360
+ dsh-mneme add --type decision --title "Adopt SQLite" \
361
+ --content "Storage layer uses node:sqlite" --importance 4 --tags storage
362
+ dsh-mneme get 42 # Show one memory
363
+ dsh-mneme delete 42 # Delete
364
+ dsh-mneme config show # Show current config (token masked)
365
+ ```
366
+
367
+ > All read/write commands support `--json` for raw JSON output; `config path` prints the config file location (`~/.dsh-mneme/cli.json`).
368
+
369
+ ## 🏗️ Architecture
370
+
371
+ ```
372
+ ┌─────────────────────────────────────────────────┐
373
+ │ 存储层:SQLite (archived/forgotten 状态) │
374
+ │ + Markdown 镜像(人工可编辑,双向同步) │
375
+ ├─────────────────────────────────────────────────┤
376
+ │ 服务层:saveWithDedupe / injectCandidates │
377
+ │ / mergeHumanEdits / onWrite 钩子 │
378
+ ├─────────────────────────────────────────────────┤
379
+ │ 模型接口:7 个工具 + 自动注入 + 会话摘要 │
380
+ ├─────────────────────────────────────────────────┤
381
+ │ autoDream:阈值调度 → LLM 决策清单 │
382
+ │ → 校验(fail-safe)→ 应用 → 摘要 │
383
+ ├─────────────────────────────────────────────────┤
384
+ │ Web 面板:设置面板内嵌 + 浏览/搜索(含向量) │
385
+ └─────────────────────────────────────────────────┘
386
+ ```
387
+
388
+ **Source layout**:
389
+
390
+ ```
391
+ src/
392
+ ├── store.js # SQLite 存储(CRUD、搜索、归档/遗忘、schema 迁移)
393
+ ├── mirror.js # Markdown 镜像(渲染/解析,人工优先)
394
+ ├── service.js # 领域逻辑(去重合并、注入筛选、写入钩子)
395
+ ├── config.js # schemastery 配置 schema
396
+ ├── tools.js # 7 个模型工具(defineTool)
397
+ ├── inject.js # systemPrompt.context 动态注入
398
+ ├── summarize.js # 会话结束 LLM 摘要
399
+ ├── dream.js # autoDream 调度 + runDream(LLM 决策 + 摘要)
400
+ ├── dream/decisions.js# 决策校验(fail-safe)+ 决策应用
401
+ ├── entities/extractor.js # 实体抽取器(v0.3.0:LLM JSON 抽取 + 去重 + fail-safe)
402
+ ├── embedding.js # OpenAI 兼容 embeddings 客户端 + 向量检索
403
+ ├── api.js # HTTP 路由(Web 面板数据通道)
404
+ └── index.js # 插件接线
405
+ lib/
406
+ ├── client.js # Web 面板(手写 ModuleLoader bundle)
407
+ └── *.js # src 的同步分发产物
408
+ test/ # 450 个 node:test 测试(含审计与三轴线压测不变量)
409
+ scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压测 · sync-lib.js 同步
410
+ ```
411
+
412
+ ## 🧪 Development
413
+
414
+ ```bash
415
+ cd dsh-mneme
416
+ npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试)
417
+ npm test # 运行 450 个测试
418
+ npm run stress # 三轴线压测:长会话检索 / 冲突仲裁 / 多 Agent 并发(离线 mock LLM)
419
+ npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行)
420
+ ```
421
+
422
+ > The stress test (`npm run stress`) covers three axes: **long-session retrieval** (Recall@k, stale-residual rate), **conflict adjudication** (a replayable adjudication set: audit snapshot hash + receipt + idempotent replay), and **multi-Agent concurrency** (lost updates, duplicate merges, transaction/crash recovery). Every autoDream run writes to the `dream_runs` audit table (input snapshot digest + decision list + per-id disposition + receipt), so silent errors can be pinpointed even when the pass rate is high.
423
+
424
+ > `lib/` is the synced distribution artifact of `src/` (`npm run sync`); `lib/client.js` is the hand-written web panel source and is unaffected by the sync.
425
+
426
+ ## 📄 Design Documents
427
+
428
+ > Design documents live in `docs/` at the repository root; the links point there via the `../docs/` relative path (they resolve correctly on GitHub when opened from this directory).
429
+
430
+ - [Entity-Structured Memory Design](docs/ENTITIES.md)
431
+ - [Semantic Enhancement Architecture](docs/SEMANTIC.md)
432
+ - [Local Model Deployment Guide](docs/LOCAL_MODEL.md)
433
+ - [Upgrading from v0.1](docs/MIGRATION.md)
434
+
435
+ ## 📜 License
436
+
437
+ MIT