dsh-layered-memory 0.7.1 → 0.8.1

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 (53) hide show
  1. package/README.en.md +165 -75
  2. package/README.md +174 -104
  3. package/assets/img/EmbeddingSource.png +0 -0
  4. package/assets/img/MemoryTools.png +0 -0
  5. package/assets/img/ToolTrajectory.png +0 -0
  6. package/assets/readme/bench-dialog.svg +87 -0
  7. package/assets/readme/bench-workflow.svg +79 -0
  8. package/assets/readme/flow.svg +74 -74
  9. package/assets/readme/storage.svg +56 -56
  10. package/dist/client.js +380 -2
  11. package/dist/config.d.ts +41 -1
  12. package/dist/config.js +10 -3
  13. package/dist/hooks/recall.d.ts +11 -8
  14. package/dist/hooks/recall.js +70 -51
  15. package/dist/index.d.ts +20 -0
  16. package/dist/index.js +15 -4
  17. package/dist/llm.d.ts +28 -0
  18. package/dist/llm.js +33 -0
  19. package/dist/pipeline/l1.js +5 -4
  20. package/dist/pipeline/l2.js +7 -2
  21. package/dist/pipeline/l3.js +7 -2
  22. package/dist/pipeline/rebuild.js +2 -2
  23. package/dist/pipeline/runner.d.ts +45 -8
  24. package/dist/pipeline/runner.js +185 -39
  25. package/dist/pipeline/trigger.d.ts +38 -0
  26. package/dist/pipeline/trigger.js +64 -0
  27. package/dist/settings.d.ts +14 -0
  28. package/dist/settings.js +41 -2
  29. package/dist/stats.js +113 -1
  30. package/dist/store/bm25.js +2 -1
  31. package/dist/store/download-queue.d.ts +35 -2
  32. package/dist/store/download-queue.js +102 -5
  33. package/dist/store/embedding.d.ts +12 -6
  34. package/dist/store/embedding.js +10 -7
  35. package/dist/store/l0.d.ts +2 -0
  36. package/dist/store/l0.js +4 -0
  37. package/dist/store/l1.d.ts +2 -0
  38. package/dist/store/l1.js +2 -2
  39. package/dist/store/model-catalog.js +1 -1
  40. package/dist/store/pending.d.ts +27 -6
  41. package/dist/store/pending.js +49 -9
  42. package/dist/store/search-utils.js +3 -2
  43. package/dist/store/session-modes.d.ts +4 -0
  44. package/dist/store/session-modes.js +16 -0
  45. package/dist/store/sqlite.d.ts +12 -0
  46. package/dist/store/sqlite.js +103 -5
  47. package/dist/util/recall-budget.d.ts +32 -0
  48. package/dist/util/recall-budget.js +85 -0
  49. package/dist/util/text.d.ts +8 -3
  50. package/dist/util/text.js +41 -15
  51. package/dist/util/tokenizer.d.ts +14 -0
  52. package/dist/util/tokenizer.js +53 -0
  53. package/package.json +6 -3
package/README.en.md CHANGED
@@ -1,56 +1,19 @@
1
- [简体中文](README.md) | **English**
1
+ <div align="center">
2
2
 
3
- <p align="center">
4
- <img src="./assets/img/Hero.png" width="100%"
5
- alt="DeepSeek Harness hero banner: conversations distilled into layered memories and recalled before every model step — chat bubbles dissolve into three progressively brighter light layers flowing into a frosted-glass capsule with a glowing orb and gradient track (tick labels: 日常·工作·智能·关闭), with light threads looping back to suggest recall">
6
- </p>
3
+ <img src="./assets/img/Hero.png" width="100%"
4
+ alt="DeepSeek Harness hero banner: conversations distilled into layered memories and recalled before every model step — chat bubbles dissolve into three progressively brighter light layers flowing into a frosted-glass capsule with a glowing orb and gradient track (tick labels: 日常·工作·智能·关闭), with light threads looping back to suggest recall">
7
5
 
8
6
  # dsh-layered-memory
9
7
 
10
- A **layered distillation memory plugin** for DeepSeek Harness (persistent composition
11
- plugin): conversations are processed in the background through L0 capture → L1 atomic
12
- memories → L2 scene consolidation → L3 persona distillation, and relevant memories are
13
- automatically injected into context before every model step — neither the user nor the
14
- model needs to do anything.
8
+ **A layered distillation memory plugin for DeepSeek Harness: conversations are processed in the background through L0 capture → L1 atomic memories → L2 scene consolidation → L3 persona distillation, and relevant memories are automatically injected into context before every model step — neither the user nor the model needs to do anything.**
15
9
 
16
- > The core memory capabilities of this plugin (the L0–L3 layered distillation pipeline,
17
- > prompts, and dual-write storage design) are modeled after **MemoryCore** from
18
- > [TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory):
19
- > prompts are kept as-is; only the L2/L3 "LLM manipulates files" flow is adapted to
20
- > "LLM outputs, engineering side executes".
10
+ [简体中文](README.md) · [Latest release](https://github.com/JunNanLYS/dsh-layered-memory/releases/latest) · [Report issues](https://github.com/JunNanLYS/dsh-layered-memory/issues)
21
11
 
22
- ## Runtime Data Flow
12
+ [![npm version](https://img.shields.io/npm/v/dsh-layered-memory?color=6f83ff&style=flat-square&label=npm)](https://www.npmjs.com/package/dsh-layered-memory)
13
+ [![DSH 0.1.0-rc.6](https://img.shields.io/badge/DSH-0.1.0--rc.6-8b5cf6?style=flat-square)](https://github.com/deepseek-ai/deepseek-harness)
14
+ [![MIT License](https://img.shields.io/badge/license-MIT-536990?style=flat-square)](LICENSE)
23
15
 
24
- <p align="center">
25
- <img src="./assets/readme/flow.svg" width="100%"
26
- alt="Runtime data flow: session events from User and Assistant (left) flow into the plugin (L0 capture, L1–L3 distillation, retrieval, memory tools), which injects relevant memories into the DeepSeek Harness core (right) at agent/pre-step; distillation reuses the core's ctx.llm and data is dual-written to ~/.dsh/memory/">
27
- </p>
28
-
29
- The plugin attaches to DSH-native event seams (`session/event` for capture,
30
- `agent/pre-step` for injection), reuses the host's `ctx.llm` for distillation, and stays
31
- fully transparent to both user and model. It also registers three model-callable memory
32
- tools: `memory_search` / `conversation_search` / `memory_read_scene`.
33
-
34
- ## Layered Memory (L0–L3)
35
-
36
- <p align="center">
37
- <img src="./assets/img/Layers.png" width="100%"
38
- alt="Four memory layers refining from top-left to bottom-right: L0 raw conversation (chat bubbles) → L1 atomic memories (glowing fact particles) → L2 scene blocks (glass document slabs) → L3 core persona (radiant crystal core); stages connected by LLM extract/consolidate/distill light beams, shrinking width shows progressive refinement">
39
- </p>
40
-
41
- ## Per-Session Memory Modes
42
-
43
- <p align="center">
44
- <img src="./assets/img/Modes.png" width="100%"
45
- alt="Per-session memory modes: a glass capsule track with four stops (日常 · 工作 · 智能 · 关闭), the glowing orb resting on 智能 (default); a vignette above each — personal chat bubbles for 日常, code/document panes for 工作, two streams merging brightest for 智能, a dim dashed ghost bubble for 关闭">
46
- </p>
47
-
48
- - **Control**: the pill next to the mode selector in the input bar (`Memory · Auto`);
49
- clicking opens a macOS-style sliding picker above — release to snap to the nearest
50
- mode; adapts to light/dark themes;
51
- - Each session's choice is persisted by sessionId to `session-modes.json`, surviving
52
- restarts/session restore; stacks with the global switches (global is the master gate);
53
- L2/L3 are fully family-isolated — content never leaks across families.
16
+ </div>
54
17
 
55
18
  ## Getting Started
56
19
 
@@ -58,7 +21,7 @@ Requires Node ≥ 22.16. Two invocation styles — the `npx` prefix can replace
58
21
  any command below:
59
22
 
60
23
  ```bash
61
- # Option 1: run the official CLI directly via npx (no pre-installed dsh; version can be pinned, e.g. dsh-layered-memory@0.6.1)
24
+ # Option 1: run the official CLI directly via npx (no pre-installed dsh; version can be pinned, e.g. dsh-layered-memory@0.8.0)
62
25
  npx -y @deepseek-ai/dsh plugin --profile web add dsh-layered-memory
63
26
 
64
27
  # Option 2: with the dsh CLI installed (dsh is a pnpm forwarder; npm i -g pnpm first if missing)
@@ -69,6 +32,24 @@ dsh plugin --profile web add https://github.com/JunNanLYS/dsh-layered-memory
69
32
  dsh plugin --profile web add /path/to/dsh-layered-memory
70
33
  ```
71
34
 
35
+ ### Install via an AI Agent (Recommended)
36
+
37
+ If your current agent can run terminal commands, send it this message as-is:
38
+
39
+ ```text
40
+ Please install the dsh-layered-memory plugin for the web profile of DeepSeek Harness.
41
+
42
+ Run only the two commands below and do not modify any other profile:
43
+ dsh plugin --profile web add dsh-layered-memory
44
+ dsh --profile web --dump-config
45
+
46
+ Confirm that dsh-layered-memory appears in the output, then report the result to me.
47
+ Do not close or restart my running DSH yourself; after installation, remind me to manually restart the DSH Web Host.
48
+ ```
49
+
50
+ The agent should report the installation result and explicitly tell you whether
51
+ `dsh-layered-memory` has appeared in the configuration.
52
+
72
53
  This package declares a `dsh.bundle` composition layer (`cordis.patch.yml`); after
73
54
  installation the **plugin entry is mounted automatically** — no need to hand-edit
74
55
  `$DSH_HOME/profiles/web/cordis.patch.yml`. Then restart DeepSeek Harness and verify:
@@ -95,6 +76,63 @@ npm run smoke # smoke test (rebuild first: see command b
95
76
  npx tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution nodenext --target es2022 --strict --skipLibCheck --esModuleInterop
96
77
  ```
97
78
 
79
+ ## Runtime Data Flow
80
+
81
+ <p align="center">
82
+ <img src="./assets/readme/flow.svg" width="100%"
83
+ alt="Runtime data flow: session events from User and Assistant (left) flow into the plugin (L0 capture, L1–L3 distillation, retrieval, memory tools), which injects relevant memories into the DeepSeek Harness core (right) at agent/pre-step; distillation reuses the core's ctx.llm and data is dual-written to ~/.dsh/memory/">
84
+ </p>
85
+
86
+ The plugin attaches to DSH-native event seams (`session/event` for capture,
87
+ `agent/pre-step` for injection) and reuses the host's `ctx.llm` for distillation. Recall
88
+ is presented as **message-side injection**: relevant memories enter the conversation as a
89
+ synthetic message placed right before the user's new message, rendered as a
90
+ **"Context injection · memory"** row in the chat flow (expand to see the hits) — so you
91
+ can see "memory at work" directly. Injected content is bounded by length and
92
+ time budgets — oversized lines are truncated (pointing the model at the memory tools for
93
+ the full text) and a timed-out recall silently skips that turn, never slowing the chat. It
94
+ also registers three model-callable memory tools: `memory_search` /
95
+ `conversation_search` / `memory_read_scene`.
96
+
97
+ In action: the "Context injection · memory" row surfaces relevant memories first, and
98
+ the model then calls `memory_read_scene` directly to read scene blocks before answering
99
+ from memory:
100
+
101
+ <p align="center">
102
+ <img src="./assets/img/MemoryTools.png" width="60%"
103
+ alt="Real conversation UI (light theme): a "Context injection · memory" row sits above the user's message asking about recent plans; the assistant lists 4 memory_read_scene tool calls (with scene-block .md filenames as arguments) before answering from memory">
104
+ </p>
105
+
106
+ In restricted sessions where only the code-execution entry point is available, the model
107
+ reaches the memory tools indirectly through `run_code` (nested as SUBTOOL calls in the
108
+ trajectory view):
109
+
110
+ <p align="center">
111
+ <img src="./assets/img/ToolTrajectory.png" width="80%"
112
+ alt="Tool-call trajectory view: a colored timeline on top and a step list on the left (SYSTEM/CONTEXT/USER/ASSISTANT/TOOL/SUBTOOL tags); a run_code tool step nests 5 memory_read_scene sub-tool calls (SUBTOOL tags), with a detail panel for the selected step on the right">
113
+ </p>
114
+
115
+ ## Layered Memory (L0–L3)
116
+
117
+ <p align="center">
118
+ <img src="./assets/img/Layers.png" width="100%"
119
+ alt="Four memory layers refining from top-left to bottom-right: L0 raw conversation (chat bubbles) → L1 atomic memories (glowing fact particles) → L2 scene blocks (glass document slabs) → L3 core persona (radiant crystal core); stages connected by LLM extract/consolidate/distill light beams, shrinking width shows progressive refinement">
120
+ </p>
121
+
122
+ ## Per-Session Memory Modes
123
+
124
+ <p align="center">
125
+ <img src="./assets/img/Modes.png" width="100%"
126
+ alt="Per-session memory modes: a glass capsule track with four stops (日常 · 工作 · 智能 · 关闭), the glowing orb resting on 智能 (default); a vignette above each — personal chat bubbles for 日常, code/document panes for 工作, two streams merging brightest for 智能, a dim dashed ghost bubble for 关闭">
127
+ </p>
128
+
129
+ - **Control**: the pill next to the mode selector in the input bar (`Memory · Auto`);
130
+ clicking opens a macOS-style sliding picker above — release to snap to the nearest
131
+ mode; adapts to light/dark themes;
132
+ - Each session's choice is persisted by sessionId to `session-modes.json`, surviving
133
+ restarts/session restore; stacks with the global switches (global is the master gate);
134
+ L2/L3 are fully family-isolated — content never leaks across families.
135
+
98
136
  ## UI Preview
99
137
 
100
138
  <p align="center">
@@ -104,6 +142,44 @@ npx tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution no
104
142
  alt="The same settings memory browser overview in light theme: identical layout and information on light card backgrounds with the same accent family, theme switch without reload">
105
143
  </p>
106
144
 
145
+ ## Measured Comparison (DSH-MemBench: Automated Benchmark)
146
+
147
+ Screenshots show what the plugin looks like — this section answers "**what does enabling it actually buy you?**" with measured numbers from an **automated benchmark** ([`bench/`](./bench/), one command to reproduce). Method: the same scenario bank with verbatim-identical inputs runs in **Group A (memory on)** and **Group B (memory off)**, 3 repetitions each, merged; environment DeepSeek official `deepseek-v4-flash`, plugin 0.8.0, Windows; taxonomy adapted from [LongMemEval](https://github.com/xiaowu0162/longmemeval) / [LoCoMo](https://snap-research.github.io/locomo/) / [AMB](https://github.com/vectorize-io/agent-memory-benchmark).
148
+
149
+ ### Dialog track (15 scenarios × 6 probe types × 3 reps = 270 questions/group): does it remember correctly
150
+
151
+ <p align="center">
152
+ <img src="./assets/readme/bench-dialog.svg" width="100%"
153
+ alt="DSH-MemBench dialog track, Group A vs Group B bar chart: overall accuracy A (memory on) 92.6% (250/270) vs B (memory off) 17.8% (48/270); per probe type, 45 each — extraction A 45/45 vs B 3/45, multi-hop A 45/45 vs B 0/45, temporal A 43/45 vs B 0/45, knowledge updates A 31/45 vs B 0/45, scene recall A 41/45 vs B 0/45, abstention both 45/45 with 0 fabricated">
154
+ </p>
155
+
156
+ **Dual-channel recall** (Group A): passive injection hit rate **75.1%** (the answer's key points appear in the recall injection, 169/225); most of the rest the model recovered by **actively calling the memory tools** — 84 questions with active queries, **60 rescued by tools**. The end-to-end 92.6% is the composite of both channels plus model utilization. With the memory store accumulating across scenarios for the whole run, 144 probe injections carried other scenarios' memories (honestly counted) — yet overall accuracy held at 92.6%: interference resistance under a growing store, measured.
157
+
158
+ ### Workflow track (4 scenarios × 3 reps, real tool sandbox): does it do it right, and cheaper
159
+
160
+ <p align="center">
161
+ <img src="./assets/readme/bench-workflow.svg" width="100%"
162
+ alt="DSH-MemBench workflow track, Group A vs Group B: task completion A 24/33 (72.7%) vs B 11/33 (33.3%); cost comparison (Group B as the full-bar baseline) — steps 125 vs 186 (B +49%), tool calls 184 vs 296 (B +61%), input tokens 1.30M vs 1.86M (B +43%); asks-user-for-help A 0 vs B 3; login scenario input tokens A 241k vs B 453k (+88%)">
163
+ </p>
164
+
165
+ **Login scenario close-up** (credentials exist only in memory; the site is a local service with unforgeable tokens): Group A completed all three runs **in a single turn each** (6/6, 241k input); Group B had to **ask the user for credentials every time** (3 asks, double the turns) and still finished only 5/6, at 453k input — **+88%**. This is one of memory's core values: **what it saves is not task difficulty, but pointless round-trips and re-teaching**.
166
+
167
+ ### Methodology & reproduction
168
+
169
+ ```bash
170
+ node bench/harness/run.mjs --arm A --repeats 3 --provider deepseek-official --model deepseek-v4-flash
171
+ node bench/harness/run.mjs --arm B --repeats 3 --provider deepseek-official --model deepseek-v4-flash # dialog track
172
+ node bench/harness/run.mjs --track workflow --arm A/B --repeats 3 ... # workflow track
173
+ node bench/harness/report.mjs --latest [dialog|workflow] # aggregate report
174
+ ```
175
+
176
+ - Scoring: programmatic `contains-all` plus an LLM judge against key points (every answer and verdict is preserved in `result.json` for human audit); workflow completion is verified programmatically from produced files and their contents;
177
+ - Metrics come from provider-reported usage (input with cache-hit split) and session-event folding; the **steady-state cache rate** excludes each session's first request (A 88.7% vs B 85.4% — memory injection does not hurt caching);
178
+ - Regression use: run before/after a plugin change and diff with `compare.mjs` (environment header check + Group-B control-drift warning);
179
+ - Limitations (stated honestly): single machine, 3 merged runs; the judge model is the same as the tested model; the scenario bank is author-built (biased toward memory-advantage scenarios — reproduce it yourself); the tool audit flags out-of-sandbox access (agents occasionally probed the user home dir in tests; this benchmark's answers never exist in the real memory store, so the numbers are unaffected).
180
+
181
+ Full reports and per-question data: [`bench/baseline/`](./bench/baseline/).
182
+
107
183
  ## Configuration
108
184
 
109
185
  Override configs go into the profile's own `cordis.patch.yml` as a **top-level bare
@@ -115,9 +191,9 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
115
191
  name: dsh-layered-memory
116
192
  config: # keys replace whole lines (no deep merge); write out all keys you want to keep
117
193
  family: auto # default mode for new sessions: auto | chat | work
118
- llm: # distillation model route (falls back to the current default model if empty)
119
- provider: ''
120
- model: ''
194
+ llm: # static distillation route (both fields set = deployment pin,
195
+ provider: '' # which outranks the settings-page selection; when empty the route
196
+ model: '' # follows the settings-page "distillation model" picker or the default model)
121
197
  ```
122
198
 
123
199
  | Field | Default | Description |
@@ -128,8 +204,9 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
128
204
  | `capture.stripCodeBlocks` | `true` | Strip code blocks from assistant messages |
129
205
  | `capture.maxMessageChars` | `4000` | Max characters per message |
130
206
  | `extract.enabled` | `true` | L1 extraction |
131
- | `extract.minMessages` | `1` | Run L1 extraction after N new messages accumulate |
132
- | `extract.backgroundMessages` | `10` | Background messages attached to extraction |
207
+ | `extract.minMessages` | `6` | Steady-state trigger threshold: run L1 extraction once a session accumulates N new messages. The effective threshold ramps up 1→2→4→…→N (first turn yields memories immediately, then batches to save calls) |
208
+ | `extract.idleSeconds` | `300` | Idle flush: distill a session's pending slice after N seconds of silence (catches "user left before reaching the threshold"); `0` disables |
209
+ | `extract.backgroundMessages` | `10` | Background messages attached to extraction (fetched per session from L0 — no cross-session contamination) |
133
210
  | `extract.candidatePool` | `5` | Dedup candidate pool size |
134
211
  | `l2.enabled` | `true` | L2 scene consolidation |
135
212
  | `l2.minNewMemories` | `5` | New-memory threshold since last L2 consolidation |
@@ -138,9 +215,12 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
138
215
  | `l3.enabled` | `true` | L3 persona distillation |
139
216
  | `l3.interval` | `20` | L3 distillation interval (new-memory count) |
140
217
  | `recall.enabled` | `true` | Auto recall |
141
- | `recall.maxResults` | `5` | L1 records injected per step |
142
- | `recall.includePersona` | `true` | Inject persona context on recall (`<user-persona>`) |
143
- | `recall.includeSceneNav` | `true` | Inject scene navigation on recall (`<scene-navigation>`) |
218
+ | `recall.maxResults` | `5` | Max L1 records injected before each new user message |
219
+ | `recall.maxCharsPerMemory` | `500` | Per-memory character cap for injected recall (overlong lines truncated with a hint to use the memory tools for the full text); `0` disables |
220
+ | `recall.maxTotalRecallChars` | `2000` | Total character cap per injected recall batch (lowest-ranked tail dropped first); `0` disables |
221
+ | `recall.timeoutMs` | `5000` | Overall recall budget (ms): a timed-out recall skips that turn without blocking the chat; `0` disables |
222
+ | `recall.includePersona` | `true` | Inject persona context into the system prompt (`<user-persona>`, stable zone) |
223
+ | `recall.includeSceneNav` | `true` | Inject scene navigation into the system prompt (`<scene-navigation>`, stable zone) |
144
224
  | `recall.strategy` | `hybrid` | Retrieval strategy: `keyword` / `embedding` / `hybrid` |
145
225
  | `recall.scoreThreshold` | `0.3` | Recall score threshold (below is not injected; applies to keyword/embedding only, not pre-fusion hybrid; tool path unfiltered) |
146
226
  | `embedding.enabled` | `false` | Vector retrieval switch; off = pure FTS |
@@ -152,11 +232,12 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
152
232
  | `embedding.timeoutMs` | `10000` | Per-call embedding timeout (ms) |
153
233
  | `embedding.allowLocalModels` | `true` | Allow the local embedding tier (deployment ceiling; when off, no model downloads and no local tier in settings) |
154
234
  | `embedding.mirror` | `https://hf-mirror.com` | Download mirror root for local models (can be changed back to `https://huggingface.co`) |
155
- | `llm.provider/model` | empty | Distillation model override (defaults to current selection) |
156
- | `llm.maxTokens` | `256000` | Output token cap per distillation call (unified across stages; a reasoning model's reasoning shares this budget too low gets fully consumed by thinking, leaving 0 chars of text) |
235
+ | `embedding.proxy` | `''` | Three-state download proxy: `''` (default) = auto-detect proxy env vars (`HTTPS_PROXY`/`ALL_PROXY` etc., honoring `NO_PROXY`); `none` = disable, always direct; any other value = proxy URL (e.g. `http://127.0.0.1:7890`). Direct connections to the mirror are intermittently unreachable on some networks (connect timeouts and poisoned bytes have both been observed) — keep the default auto-detection on machines with a proxy |
236
+ | `llm.provider/model` | empty | Static distillation route (deployment pin): when **both** fields are set the route is locked, outranking the settings-page selection and the default model (deployments can force distillation onto a specific route); when empty the route follows "settings-page selection default model". At runtime, switch among **configured providers** (including custom ones added in dsh Settings → Models) via the "distillation model" picker in Settings → Memory → Overview — effective immediately, no restart needed |
237
+ | `llm.maxTokens` | `65536` | Fallback output cap for non-layered calls. Each distillation stage has its own budget (extraction 16k / dedup 8k / L2 32k / L3 16k; auto ×4 when reasoning effort is high/max, so thinking can't starve the text budget); the per-layer budgets are runtime-adjustable in Settings → Memory → Overview → distillation parameters (empty/0 = built-in defaults) |
157
238
  | `llm.reasoningEffort` | `off` | Distillation reasoning-effort tier (deployment default): `off` / `high` / `max`; empty string = don't send (follow model default). Distillation is structured extraction, so thinking is off by default — a reasoning model (e.g. v4-flash) at its default `high` tier can consume the entire output budget on thinking, leaving 0 chars of text; set to empty string for models that don't recognize the effort parameter. Switchable at runtime in Settings → Memory → Overview ("follow config" falls back to this value) |
158
239
  | `llm.temperature` | `0.3` | Distillation temperature |
159
- | `llm.maxInputChars` | `700000` | Input character budget per distillation call (over-budget L1 inputs are chunked automatically) |
240
+ | `llm.maxInputChars` | `700000` | Input character budget per distillation call (over-budget L1 inputs are chunked automatically); runtime-adjustable in Settings → distillation parameters → input budget (empty/0 = follow this value) |
160
241
  | `llm.timeoutMs` | `120000` | Per-call distillation timeout (ms) |
161
242
  | `tools` | `true` | Whether to register model-callable memory tools |
162
243
 
@@ -176,24 +257,25 @@ local), switchable at runtime in the settings page — see the next section.
176
257
  Pick the embedding source in Settings → Memory → Overview → Semantic Retrieval;
177
258
  it takes effect immediately, no config edit or restart:
178
259
 
179
- | Source | Description |
180
- | --- | --- |
181
- | **Off** (default) | No vector embedding at all; pure BM25 keyword retrieval |
182
- | **Remote** | Bring any OpenAI-compatible `/embeddings` service (selectable only when the `embedding.*` quartet is configured) |
183
- | **Local** | Pick from a built-in model catalog, ONNX-quantized **CPU inference** — no API key, data never leaves the machine |
184
-
185
- The local catalog is a built-in allowlist (each model pinned to a revision with
186
- per-file sha256; arbitrary repos cannot be downloaded):
260
+ <p align="center">
261
+ <img src="./assets/img/EmbeddingSource.png" width="70%"
262
+ alt="Semantic retrieval (embedding source) panel in the settings page (light theme): a three-state selector (Off/Local/Remote, Local selected) showing the current source and the first-run runtime install hint; below it the local model catalog lists BGE small Chinese (in use/ready), EmbeddingGemma 300M (download 316MB) and BGE-M3 (download 560MB) with dims/context/size/notes and download buttons">
263
+ </p>
187
264
 
188
- | Model | Dims | Context | Size | Notes |
189
- | --- | --- | --- | --- | --- |
190
- | BGE small Chinese | 512 | 512 | ~25MB | Fastest on CPU; good first taste of semantic retrieval |
191
- | EmbeddingGemma 300M | 768 | 2048 | ~330MB | 100+ languages incl. Chinese; balanced quality/cost (same as upstream MemoryCore) |
192
- | BGE-M3 | 1024 | 8192 | ~590MB | Best Chinese quality; a single embedding can take seconds |
265
+ Three sources: **Off** (default; no vector embedding at all, pure BM25 keyword
266
+ retrieval), **Remote** (bring any OpenAI-compatible `/embeddings` service, selectable
267
+ only when the `embedding.*` quartet is configured), **Local** (pick from a built-in
268
+ model catalog, ONNX-quantized **CPU inference** no API key, data never leaves the
269
+ machine). The local catalog is a built-in allowlist (each model pinned to a revision
270
+ with per-file sha256; arbitrary repos cannot be downloaded).
193
271
 
194
272
  - **Download**: one click on the model card (default mirror `hf-mirror.com`, resumable
195
- downloads + sha256 integrity checks); stored under `models/<id>/` in the data
196
- directory, deletable from the settings page at any time;
273
+ downloads + sha256 integrity checks; a proxy is used when direct access is
274
+ unreachable proxy env vars like `HTTPS_PROXY`/`ALL_PROXY` are auto-detected by
275
+ default, see `embedding.proxy`). Per-file failures auto-retry with a rotated cache
276
+ key (`?dshmem-retry=N`, sidestepping occasionally bad CDN cache objects); hash
277
+ mismatches restart from zero, network errors resume from the checkpoint; stored
278
+ under `models/<id>/` in the data directory, deletable from the settings page at any time;
197
279
  - **On-demand runtime**: the inference runtime (transformers.js, ~100–200MB) is
198
280
  installed only on first switch to the local tier, into `runtime/` in the data
199
281
  directory — never in the plugin's dependency tree or install directory;
@@ -221,8 +303,8 @@ of the raw model output; all failure warns carry the first stack frame.
221
303
 
222
304
  - The full pipeline is embedded (no external Gateway); distillation reuses DSH's own LLM;
223
305
  - L2/L3 changed from "LLM manipulates file tools" to "LLM outputs operation JSON / full documents, engineering side executes";
224
- - Recall injection happens at `agent/pre-step` + agent-scoped `systemPrompt.context` (DSH-native events/services);
225
- - Storage/retrieval is a single-machine slimmed version of the official sqlite backend (drops multi-tenant isolation columns, TCVDB cloud backend, audit tables; tokenization uses a bundled CJK bigram instead of jieba, keeping zero native dependencies sqlite-vec is the only native extension, auto-degrading on load failure).
306
+ - Recall injection happens at `agent/pre-step` (message-side synthetic message, the official pre-step replacement semantics) plus agent-scoped `systemPrompt.context` (persona/navigation stable zone — DSH-native events/services);
307
+ - Storage/retrieval is a single-machine slimmed version of the official sqlite backend (drops multi-tenant isolation columns, TCVDB cloud backend, audit tables; tokenization uses jieba like the official one @node-rs/jieba prebuilt binaries union CJK character bigrams: word tokens give BM25 exact-word hits while bigrams keep sub-word recall; on load failure it falls back to pure bigrams, and FTS indexes are rebuilt automatically via a tokenizer version stamp).
226
308
 
227
309
  ## Credits
228
310
 
@@ -231,6 +313,14 @@ dual-write storage architecture) are modeled after **MemoryCore** from
231
313
  [TencentCloud/TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory).
232
314
  Thanks to the original project for open-sourcing its design and implementation.
233
315
 
316
+ ## Roadmap
317
+
318
+ Features under planning — feedback and priorities welcome in the
319
+ [issue tracker](https://github.com/JunNanLYS/dsh-layered-memory/issues):
320
+
321
+ - [ ] **Git branch awareness**: associate memories with the current git branch; recall can filter/boost by branch (orthogonal to the existing memory modes)
322
+ - [ ] **Claude Code / Codex memory import**: one-click migration of existing memory assets (`CLAUDE.md`, Claude Code memory files, Codex `AGENTS.md`, etc.), fed into the layered distillation pipeline
323
+
234
324
  ## License
235
325
 
236
326
  [MIT](LICENSE)