dsh-engram 0.3.5 → 0.3.7
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.
- package/README.md +198 -6
- package/README.zh.md +137 -4
- package/docs/RELEASE-NOTES.TEMPLATE.md +68 -0
- package/lib/api.js +127 -4
- package/lib/client.js +47 -26
- package/lib/index-block.js +92 -1
- package/lib/index.js +38 -2
- package/lib/settings.js +49 -23
- package/lib/store.js +287 -0
- package/lib/tools.js +48 -14
- package/lib/usage.js +146 -5
- package/package.json +17 -13
- package/scripts/dsh-engram.mjs +0 -0
- package/scripts/gen-release-notes.mjs +64 -0
- package/scripts/prepublish-check.mjs +4 -4
- package/scripts/setup-links.mjs +21 -11
package/README.md
CHANGED
|
@@ -40,6 +40,17 @@ with one goal: **save tokens**.
|
|
|
40
40
|
tokenization), recall runs a bounded substring scan over the newest session
|
|
41
41
|
logs (zstd-decompressed, LRU-cached) and appends matched past sessions —
|
|
42
42
|
deterministic `# past sessions` lines, capped files and bytes.
|
|
43
|
+
- **Session-start auto-recall (`autoRecallOnStart`, default on)** — the pull-only
|
|
44
|
+
design had a gap proven in real use: recall tools relied on model initiative,
|
|
45
|
+
and in practice the model almost never calls `engram_recall` on its own (19
|
|
46
|
+
real sessions, thousands of tool calls, ~5 recall calls total). So at the FIRST
|
|
47
|
+
assembly of a new session the host runs one deterministic BM25 recall keyed on
|
|
48
|
+
the session's first user message and injects the top ≤ `autoRecallLimit` hits
|
|
49
|
+
as a `[RECALL]` block (default 700-char budget, "few and precise" — agentmemory
|
|
50
|
+
candidate ①). Relevant memory is in context even without a single recall call.
|
|
51
|
+
Pure rule, zero LLM, pure read (never bumps `hits`); superseded stale truth does
|
|
52
|
+
not occupy an injection slot (still fetchable via `engram_detail`); frozen per
|
|
53
|
+
session together with `[ENGRAM]` for prefix stability.
|
|
43
54
|
- **Memory-to-memory semantics** — `engram_store` accepts optional
|
|
44
55
|
`supersedes` / `contradicts` memory ids (validated against the same
|
|
45
56
|
workspace). A superseded ("stale truth") memory is demoted to the tail of
|
|
@@ -128,14 +139,175 @@ LLM-distillation and vector/graph niches are already crowded. dsh-engram fills t
|
|
|
128
139
|
three gaps that matter for token discipline:
|
|
129
140
|
|
|
130
141
|
1. **No model in the write path** — capture is deterministic pattern matching.
|
|
131
|
-
2. **No raw text in the prompt** — a bounded symbolic index is injected,
|
|
132
|
-
|
|
142
|
+
2. **No raw text in the prompt** — a bounded symbolic index is injected, plus the
|
|
143
|
+
top ≤ N memories auto-recalled for the session's first message (within budget);
|
|
144
|
+
deeper retrieval stays on demand ("retrieved ≠ full-text injected").
|
|
133
145
|
3. **Honest task closure** — STABLE cannot be declared without evidence.
|
|
134
146
|
|
|
135
147
|
DSH already provides cross-session FTS (`ctx.sessionQuery`), storage
|
|
136
148
|
(`ctx.storageDomain`), prompt-injection hooks and settings slots; dsh-engram is a
|
|
137
149
|
thin composition layer over them, not a re-implementation.
|
|
138
150
|
|
|
151
|
+
## Security model
|
|
152
|
+
|
|
153
|
+
The DSH plugin ecosystem is young and unvetted — there is no official directory,
|
|
154
|
+
no signature check, and the harness's read/write permission tiers do **not**
|
|
155
|
+
constrain what a plugin's own code can do. So the trust bar for a memory plugin
|
|
156
|
+
is higher than for a tool: a memory plugin sits on your prompt prefix and writes
|
|
157
|
+
to disk on your behalf. dsh-engram's model, stated plainly:
|
|
158
|
+
|
|
159
|
+
- **No network, ever.** The plugin has no outbound socket, no telemetry, no
|
|
160
|
+
update pinger. Nothing here can leak your sessions off the machine.
|
|
161
|
+
- **No shell, no filesystem access beyond one storage file.** It never executes
|
|
162
|
+
commands and never touches arbitrary paths. All data lives in the single
|
|
163
|
+
storage-domain unit `~/.dsh/storages/dsh_engram.json` (plus the session-log
|
|
164
|
+
reads it makes through DSH's own `ctx.sessionQuery`).
|
|
165
|
+
- **Secrets are redacted before they can land on disk.** Every write path —
|
|
166
|
+
`engram_store`, auto-capture, and the import/restore route — runs text through
|
|
167
|
+
the deterministic `redactText` rule engine (API keys, JWTs, `Bearer` tokens,
|
|
168
|
+
private keys, `key=value` secret shapes) *before* the dedup hash, the char
|
|
169
|
+
cap, and storage. Nothing sensitive is ever persisted, so nothing sensitive
|
|
170
|
+
can be recalled later.
|
|
171
|
+
- **Doing harm requires your harness's own permissions.** The plugin only *asks*
|
|
172
|
+
the host to do things via the same seams the tools you already trust use. It
|
|
173
|
+
does not widen sandbox policy or escalate to `danger-full-access` — if you
|
|
174
|
+
keep your profile's defaults, engram inherits exactly those bounds.
|
|
175
|
+
- **Read-mostly web API, loopback-fenced.** The GUI routes under
|
|
176
|
+
`/api/dsh-engram` reject any non-loopback caller unless you explicitly
|
|
177
|
+
whitelist a hostname via `trustedHosts`; even then the same-origin fence holds.
|
|
178
|
+
- **Verifiable.** `npm run dsh-engram -- doctor` (`/scripts/dsh-engram.mjs
|
|
179
|
+
doctor`) reports what the plugin is configured to touch; the whole store is
|
|
180
|
+
inspectable, nothing is opaque. If you ever doubt a claim in this section, the
|
|
181
|
+
code to check is `lib/redact.js`, `lib/store.js` and this plugin's
|
|
182
|
+
`cordis.patch.yml`.
|
|
183
|
+
|
|
184
|
+
What this deliberately does **not** claim: it is not an air-gap. If your profile
|
|
185
|
+
grants the agent `danger-full-access` or a shell, the agent can use those —
|
|
186
|
+
engram is a memory layer, not a sandbox. The point above is that *dsh-engram
|
|
187
|
+
itself* adds no surface of its own.
|
|
188
|
+
|
|
189
|
+
## Data contract: export / import
|
|
190
|
+
|
|
191
|
+
Memory should not be a hostage. The four tables (memories / tasks / links /
|
|
192
|
+
entities) can be dumped and restored through a stable, versioned, machine-readable
|
|
193
|
+
shape — so the corpus survives a reinstall, a machine move, or (when a thin
|
|
194
|
+
adapter is written) a move to another harness, MCP end-point or Claude Code
|
|
195
|
+
skill. The contract is plain JSON with a self-describing header:
|
|
196
|
+
|
|
197
|
+
```http
|
|
198
|
+
GET /api/dsh-engram/export?workspace=/path/to/project # one workspace
|
|
199
|
+
GET /api/dsh-engram/export # every workspace
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```jsonc
|
|
203
|
+
{
|
|
204
|
+
"meta": { "format": "dsh-engram/export", "version": 1, "exportedAt": 1724…, "workspace": "/path/to/project" },
|
|
205
|
+
"memories": [ /* all rows, archived included — a backup never loses provenance */ ],
|
|
206
|
+
"tasks": [ /* draft/active/stable + archived */ ],
|
|
207
|
+
"links": [ /* typed edges */ ],
|
|
208
|
+
"entities": [ /* graph nodes */ ]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Restore is idempotent by default and gated when destructive:
|
|
213
|
+
|
|
214
|
+
```http
|
|
215
|
+
POST /api/dsh-engram/import # body: { payload, mode, dryRun?, workspace?, confirm? }
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **`mode: "merge"`** (default) — writes only rows whose `id` is absent, so a
|
|
219
|
+
backup can be re-applied any number of times without duplicating anything. Each
|
|
220
|
+
row keeps its own `workspace`.
|
|
221
|
+
- **`mode: "replace"`** — wipes the target `workspace` from all four tables, then
|
|
222
|
+
restores only its rows. Destructive, so it additionally requires
|
|
223
|
+
`confirm: "restore"` in the body.
|
|
224
|
+
- **`dryRun: true`** — computes the identical plan (what would be written /
|
|
225
|
+
skipped) without writing a single row.
|
|
226
|
+
- **Import honors the same invariants as `storeMemory`**: memory text passes the
|
|
227
|
+
secret redactor before touching disk, and rows that would break the contract
|
|
228
|
+
(missing id/workspace, empty text, over `maxMemoryChars`, id already present
|
|
229
|
+
in merge, over the workspace memory cap) are skipped and listed in the
|
|
230
|
+
response report instead of aborting the batch.
|
|
231
|
+
|
|
232
|
+
A restore round-trip for one workspace is then:
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
curl -s "http://127.0.0.1:3080/api/dsh-engram/export?workspace=$PWD" -o engram-backup.json
|
|
236
|
+
curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
|
|
237
|
+
-H "content-type: application/json" \
|
|
238
|
+
-d "{\"payload\": $(cat engram-backup.json), \"mode\": \"merge\"}"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## DSH compatibility
|
|
242
|
+
|
|
243
|
+
> **Short version.** Every **published** release (`0.2.0` … `0.3.6`) supports
|
|
244
|
+
> the **old** DSH API graph (`0.1.0-rc.7` / `0.1.1`, peers `^0.1.0-rc.7`)
|
|
245
|
+
> **only**. DSH `0.1.2-alpha.2` and newer are **not** compatible with those
|
|
246
|
+
> releases — the settings/client API family broke there. New-DSH support
|
|
247
|
+
> **starts with `0.3.7`**, the first release of the new API generation
|
|
248
|
+
> (described below).
|
|
249
|
+
|
|
250
|
+
| dsh-engram release | Status | DSH support | Declared via |
|
|
251
|
+
| --- | --- | --- | --- |
|
|
252
|
+
| `0.2.0` … `0.3.6` (npm, GitHub v0.3.3–v0.3.6) | published | **old DSH only** — `0.1.0-rc.7` / `0.1.0-rc.8` / `0.1.1-rc.1` / `0.1.1-rc.2` | peers `@deepseek-ai/dsh-*@^0.1.0-rc.7` |
|
|
253
|
+
| `0.3.7` (npm, GitHub v0.3.7) | published | **new DSH only** — `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.2.0-0` |
|
|
254
|
+
|
|
255
|
+
### Where the API family broke — `0.1.2-alpha.2`
|
|
256
|
+
|
|
257
|
+
`0.1.2-alpha.2` is the first version of the new API family and the first that
|
|
258
|
+
**breaks** the released plugin versions:
|
|
259
|
+
|
|
260
|
+
- `@deepseek-ai/dsh-settings` **removed** `settingsNamespace()` and
|
|
261
|
+
`installSettingsSection()` — the exact imports every release ≤ `0.3.6` uses —
|
|
262
|
+
and replaced them with `settings.installSection`. The old host half fails to
|
|
263
|
+
load on `0.1.2-alpha.2+`.
|
|
264
|
+
- The web client half swapped its inject set: `@deepseek-ai/dsh-client-runtime`
|
|
265
|
+
no longer exists in the new family (replaced by `dsh-api-remotes` plus
|
|
266
|
+
`dsh-client-locale` / `-conversation` / `-renderer` / `-ui-settings-plugins`),
|
|
267
|
+
and the settings card used to talk through `ctx.remote.settings`.
|
|
268
|
+
- Supporting bumps at the same boundary: `@deepseek-ai/cordis` `^4.0.1 → ^4.0.2`,
|
|
269
|
+
`@deepseek-ai/schemastery` `^3.18.1 → ^3.18.2`, and `@deepseek-ai/dsh` stopped
|
|
270
|
+
shipping `config/agent-presets` in the package (moved to `dsh.configTrees`).
|
|
271
|
+
|
|
272
|
+
The web config card no longer depends on any of this: since `0.3.7` it reads
|
|
273
|
+
and writes the `dsh-engram` settings namespace through the plugin's **own**
|
|
274
|
+
HTTP route (`/api/dsh-engram/settings`, same-origin fetch like the memory
|
|
275
|
+
viewer), so the card is generation-agnostic on both sides of the
|
|
276
|
+
`0.1.2-alpha.2` boundary.
|
|
277
|
+
|
|
278
|
+
So: keep DSH on `0.1.1-rc.2` or older while running a release ≤ `0.3.6`, and
|
|
279
|
+
upgrade the plugin to `>=0.3.7` before moving DSH to `0.1.2-alpha.2`.
|
|
280
|
+
|
|
281
|
+
### Current release — `0.3.7`
|
|
282
|
+
|
|
283
|
+
This release declares support for DSH `>=0.1.2-alpha.2 <0.2.0-0` and was fully
|
|
284
|
+
validated against the `dsh-v0.1.5-alpha.1` source and web runtime from the local
|
|
285
|
+
`deepseek-harness` checkout.
|
|
286
|
+
|
|
287
|
+
| Component | Supported range |
|
|
288
|
+
| --- | --- |
|
|
289
|
+
| DSH runtime / host / client package family | `>=0.1.2-alpha.2 <0.2.0-0` |
|
|
290
|
+
| `@deepseek-ai/cordis` | `^4.0.2` |
|
|
291
|
+
| `@deepseek-ai/schemastery` | `^3.18.2` |
|
|
292
|
+
| Node.js | `>=22.19.0` |
|
|
293
|
+
|
|
294
|
+
`0.1.2-alpha.2` is the minimum version for this API family: host settings use
|
|
295
|
+
`settings.installSection`, and the browser settings card reads and writes
|
|
296
|
+
through the plugin's own HTTP route (`/api/dsh-engram/settings`, see Route B
|
|
297
|
+
above). Host import and settings-registration smoke checks pass on
|
|
298
|
+
`0.1.2-alpha.2` and `0.1.3-alpha.2`; the full regression suite is validated
|
|
299
|
+
on `0.1.5-alpha.1` (real-machine GET/PUT/conflict/reset/persist-across-restart
|
|
300
|
+
all verified on `dsh 0.1.5-rc.1` with an isolated profile). The older
|
|
301
|
+
`0.1.0-rc.7` and `0.1.1` API graphs are outside this release's compatibility
|
|
302
|
+
range.
|
|
303
|
+
|
|
304
|
+
To install the DSH version used for validation:
|
|
305
|
+
|
|
306
|
+
```sh
|
|
307
|
+
npm install --global @deepseek-ai/dsh@0.1.5-alpha.1
|
|
308
|
+
dsh --version
|
|
309
|
+
```
|
|
310
|
+
|
|
139
311
|
## Install
|
|
140
312
|
|
|
141
313
|
```sh
|
|
@@ -317,8 +489,8 @@ npm run build:client
|
|
|
317
489
|
|
|
318
490
|
| Tool | Purpose | Kind |
|
|
319
491
|
|---|---|---|
|
|
320
|
-
| `engram_store` | Explicitly store one memory (kind, tags, optional entity anchor, optional supersedes/contradicts
|
|
321
|
-
| `engram_recall` | Deterministic keyword recall over workspace memories; optional `search_sessions` FTS over past sessions | read |
|
|
492
|
+
| `engram_store` | Explicitly store one memory (kind, tags, optional entity anchor, optional supersedes/contradicts ids, optional `source`/`conditions`) | write |
|
|
493
|
+
| `engram_recall` | Deterministic keyword recall over workspace memories; optional `search_sessions` FTS over past sessions; opt-in `scope=global`/`recallScope` cross-workspace recall with `[W:<ws>]` origin markers | read |
|
|
322
494
|
| `engram_detail` | Full record of one memory id (provenance, tags, hits) | read |
|
|
323
495
|
| `esr_task` | Create a task entity (draft → active) | write |
|
|
324
496
|
| `esr_close` | Close a task via the evidence protocol (artifact + evaluation + memory_ref) | write |
|
|
@@ -542,7 +714,11 @@ ESR operating protocol (static, byte-identical every turn)
|
|
|
542
714
|
[ENGRAM] workspace: symbolic-index · 2 memories · 1 task(s) active · 0 links
|
|
543
715
|
[D] 06-18 Decided: use sqlite-vec for retrieval #a2331d87
|
|
544
716
|
[T] 06-18 Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref #tsk_8b26
|
|
545
|
-
drill:
|
|
717
|
+
drill: use [RECALL] below · engram_detail <id> (full record) · engram_recall <query> (more) · esr_task/esr_node/esr_link (work)
|
|
718
|
+
|
|
719
|
+
[RECALL] recall · 1 hit(s) · first msg: retrieval
|
|
720
|
+
- [D] 06-18 Decided: use sqlite-vec for retrieval upgrade #a2331d87 ×2
|
|
721
|
+
context: use above · engram_detail <id> · engram_recall <query>
|
|
546
722
|
|
|
547
723
|
[ESR] tasks: 1 active / 1 stable
|
|
548
724
|
- tsk_0d: Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref
|
|
@@ -564,6 +740,14 @@ records via `engram_detail`. When a workspace has no tasks, `[ESR]` still render
|
|
|
564
740
|
one line naming `esr_task`/`esr_close` so the mechanism stays visible to the
|
|
565
741
|
model instead of vanishing.
|
|
566
742
|
|
|
743
|
+
`[RECALL]` (session-start auto recall, `autoRecallOnStart`) runs one deterministic
|
|
744
|
+
BM25 pass over the workspace keyed on the session's first user message and injects
|
|
745
|
+
the top `autoRecallLimit` hits, bounded by the `autoRecallMaxChars` character
|
|
746
|
+
budget; nothing renders on zero hits or an empty workspace. It is a *pointer* — the
|
|
747
|
+
model should treat it as "this is relevant; `engram_detail` for the full record" —
|
|
748
|
+
rather than full-text dumping. It is a pure read (never bumps `hits`), superseded
|
|
749
|
+
stale truth never occupies a slot, and it freezes per session with `[ENGRAM]`.
|
|
750
|
+
|
|
567
751
|
## Config
|
|
568
752
|
|
|
569
753
|
Defaults are token-conscious; override any key via the profile patch
|
|
@@ -574,6 +758,10 @@ Defaults are token-conscious; override any key via the profile patch
|
|
|
574
758
|
config:
|
|
575
759
|
autoCapture: true # zero-LLM tool-result capture
|
|
576
760
|
sessionSearch: true # engram_recall may also FTS past sessions
|
|
761
|
+
recallScope: workspace # recall scope: "workspace" (strict isolation, default) | "global" (opt-in cross-workspace recall with [W:<ws>] origin markers)
|
|
762
|
+
autoRecallOnStart: true # session-start auto recall (false = back to pure pull)
|
|
763
|
+
autoRecallLimit: 3 # [RECALL] max injected hits (few and precise)
|
|
764
|
+
autoRecallMaxChars: 700 # [RECALL] char budget
|
|
577
765
|
autoCapturePerSession: 40
|
|
578
766
|
indexMaxLines: 12 # [ENGRAM] line cap
|
|
579
767
|
indexMaxChars: 700 # [ENGRAM] char cap (token budget)
|
|
@@ -616,7 +804,11 @@ dedup 1.0.
|
|
|
616
804
|
**/api/dsh-engram/stats + the observability panel** is the real-session layer —
|
|
617
805
|
it answers how the model actually uses the memory in production (ESR
|
|
618
806
|
proactivity ratio, recall hit rate, detail conversion), while the eval
|
|
619
|
-
answers how good the retrieval layer itself is.
|
|
807
|
+
answers how good the retrieval layer itself is. The stats read the
|
|
808
|
+
concatenated multi-frame `session.jsonl.zstd` logs with Node's built-in
|
|
809
|
+
`node:zlib` (frame scan + one reused decoder handle) — **no system `zstd` CLI
|
|
810
|
+
is required**, so the telemetry works out-of-the-box on Windows and other
|
|
811
|
+
hosts without an external codec.
|
|
620
812
|
|
|
621
813
|
Repo layout: `lib/` (host half: store / capture / index-block / tools / api /
|
|
622
814
|
settings), `client/` (browser half, TSX + `build.mjs`), `test/` (node:test).
|
package/README.zh.md
CHANGED
|
@@ -28,6 +28,13 @@
|
|
|
28
28
|
且查询含 **CJK 中文**(FTS5 中文分词整段只算一个 token)时,召回会对最近若干会话日志做
|
|
29
29
|
有界的**子串扫描**(zstd 解压 + LRU 缓存,限文件数与字节),追加命中会话的确定性
|
|
30
30
|
`# past sessions` 行。
|
|
31
|
+
- **会话启动自动召回(`autoRecallOnStart`,默认开)** — 纯拉取式设计有个被实证戳穿的盲区:
|
|
32
|
+
召回工具只交模型自觉,而真实会话里模型几乎从不主动调 `engram_recall`(实测 19 个会话数千次
|
|
33
|
+
工具调用只出现 ~5 次召回)。因此宿主在新会话**首次组装**时,用会话**首条用户消息**对工作区做
|
|
34
|
+
一次确定性 BM25 召回,把命中前 ≤3 条直接注入 `[RECALL]` 块(默认 700 字符预算,"少而准",
|
|
35
|
+
agentmemory 候选①)——相关记忆不依赖模型想起来就已在上下文里。纯规则、零 LLM、
|
|
36
|
+
纯读不 bump 命中;superseded 过期真相不占注入槽(仍可 `engram_detail` 重取);
|
|
37
|
+
与 `[ENGRAM]` 一起按会话冻结保持前缀稳定。
|
|
31
38
|
- **记忆间语义(supersede/contradict)** — `engram_store` 可选收 `supersedes` / `contradicts`
|
|
32
39
|
记忆 id(同工作区校验)。被 supersede 的「过期真相」在召回里**降级到尾部**、并从 `[ENGRAM]`
|
|
33
40
|
块中**剔除**(新陈述占行);被 contradicts 的记忆保留排序但标注 `· contradicted by <id>`。
|
|
@@ -83,12 +90,125 @@ MIT · node >= 22.19 · host 半边 + 浏览器半边合在一包
|
|
|
83
90
|
dsh-engram 补的是对 token 纪律真正重要的三个空白:
|
|
84
91
|
|
|
85
92
|
1. **写入路径没有模型** — 捕获是确定性的模式匹配。
|
|
86
|
-
2.
|
|
93
|
+
2. **提示词里不灌全文** — 只注入有界的符号索引 + 会话启动时自动召回的最相关 ≤3 条(预算内),
|
|
94
|
+
更深的检索按需进行("检索到≠灌全文")。
|
|
87
95
|
3. **诚实的任务闭环** — 没有证据就不能宣布 STABLE。
|
|
88
96
|
|
|
89
97
|
DSH 已经提供跨会话 FTS(`ctx.sessionQuery`)、存储(`ctx.storageDomain`)、提示词注入钩子和设置槽位;
|
|
90
98
|
dsh-engram 只是这些能力之上的一层薄组合层,而非重新实现。
|
|
91
99
|
|
|
100
|
+
## 安全模型
|
|
101
|
+
|
|
102
|
+
DSH 插件生态还很年轻、缺乏背书:没有官方目录、没有签名校验,且 harness 的读写权限三档**约束不了插件自身代码**。因此记忆插件面对的信任何题比工具更高——它趴在你的提示词前缀上,还替你在磁盘上写东西。dsh-engram 的模型,直说如下:
|
|
103
|
+
|
|
104
|
+
- **永远不联网。** 本插件没有出口 socket、没有遥测、没有更新探针。任何东西都不可能把会话带出本机。
|
|
105
|
+
- **无 shell、除一个存储文件外不碰文件系统。** 不执行命令、不碰任意路径。全部数据只落在一个 storage-domain 单元 `~/.dsh/storages/dsh_engram.json`(外加经 DSH 自身 `ctx.sessionQuery` 做的会话日志读取)。
|
|
106
|
+
- **密钥在落盘之前就被脱敏。** 每条写入路径——`engram_store`、自动捕获,以及本节的**导入/恢复**——都先把文本过一遍确定性的 `redactText` 规则引擎(API Key、JWT、`Bearer` token、私钥、`key=value` 密钥形态),*之后*才做去重哈希、字符上限与落盘。敏感文本永不落盘,召回时自然也不会有。
|
|
107
|
+
- **要做什么坏事,必须借你 harness 自己的权限。** 插件只通过你已信任的工具同款的 seam 向宿主"请求"做事;不扩沙箱策略、不拉高到 `danger-full-access`——你保持 profile 默认,engram 就只继承那些边界。
|
|
108
|
+
- **Web API 只读为主、loopback 栅栏。** `/api/dsh-engram` 下的 GUI 路由拒绝任何非回环调用方,除非你用 `trustedHosts` 显式放行某个主机名;即便放行,同源栅栏依然生效。
|
|
109
|
+
- **可核验。** `npm run dsh-engram -- doctor`(`scripts/dsh-engram.mjs doctor`)报告插件被配置去碰什么;整个 store 可检视、不透明的东西为零。如果对这一段有怀疑,要查的代码就是 `lib/redact.js`、`lib/store.js` 和本插件的 `cordis.patch.yml`。
|
|
110
|
+
|
|
111
|
+
这**不是**说它是个气闸:如果你的 profile 给了 agent `danger-full-access` 或 shell,agent 可以用——engram 是记忆层而不是沙箱。上面这句话的意思是:*dsh-engram 自己*不新增任何攻击面。
|
|
112
|
+
|
|
113
|
+
## 数据契约:导出 / 导入
|
|
114
|
+
|
|
115
|
+
记忆不该当人质。四张表(memories / tasks / links / entities)可以用一个稳定、带版本号的机器可读格式整体倒出再恢复——重装、换机,或者(写一层薄适配器之后)搬到别的 harness / MCP 端点 / Claude Code skill,语料都跟得走。契约是带自描述头的纯 JSON:
|
|
116
|
+
|
|
117
|
+
```http
|
|
118
|
+
GET /api/dsh-engram/export?workspace=/path/to/project # 单个工作区
|
|
119
|
+
GET /api/dsh-engram/export # 全部工作区
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```jsonc
|
|
123
|
+
{
|
|
124
|
+
"meta": { "format": "dsh-engram/export", "version": 1, "exportedAt": 1724…, "workspace": "/path/to/project" },
|
|
125
|
+
"memories": [ /* 全部行,含归档——备份不丢溯源 */ ],
|
|
126
|
+
"tasks": [ /* draft/active/stable + 归档 */ ],
|
|
127
|
+
"links": [ /* 类型化边 */ ],
|
|
128
|
+
"entities": [ /* 图节点 */ ]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
恢复默认幂等、破坏性操作有门禁:
|
|
133
|
+
|
|
134
|
+
```http
|
|
135
|
+
POST /api/dsh-engram/import # body: { payload, mode, dryRun?, workspace?, confirm? }
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- **`mode: "merge"`**(默认)— 只写 `id` 尚不存在的行,备份可以随便重复套用而不会重复;每行保留自己的 `workspace`。
|
|
139
|
+
- **`mode: "replace"`** — 先把目标 `workspace` 的四张表清空,再只恢复属于它的行。破坏性操作,因此 body 里还要求 `confirm: "restore"`。
|
|
140
|
+
- **`dryRun: true`** — 只算出完全相同的执行计划(会写什么 / 跳过什么),不写任何一行。
|
|
141
|
+
- **导入遵守与 `storeMemory` 相同的不可变约定**:记忆文本落盘前先过密钥脱敏;会破坏契约的行(缺 id/workspace、空文本、超 `maxMemoryChars`、merge 时 id 已存在、超工作区记忆上限)会被跳过并写进响应报告,而不是中止整批。
|
|
142
|
+
|
|
143
|
+
单工作区的恢复往返:
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
curl -s "http://127.0.0.1:3080/api/dsh-engram/export?workspace=$PWD" -o engram-backup.json
|
|
147
|
+
curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
|
|
148
|
+
-H "content-type: application/json" \
|
|
149
|
+
-d "{\"payload\": $(cat engram-backup.json), \"mode\": \"merge\"}"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 兼容性
|
|
153
|
+
|
|
154
|
+
> **一句话结论**:所有**已真正发布**的版本(`0.2.0` … `0.3.6`)**只支持旧版
|
|
155
|
+
> DSH**(`0.1.0-rc.7` / `0.1.1` API 图,peers `^0.1.0-rc.7`)。DSH
|
|
156
|
+
> `0.1.2-alpha.2` 及更新版本与它们**不兼容**——settings/client API 族在该版本
|
|
157
|
+
> 断裂。新版 DSH 支持**自 `0.3.7` 起**提供——它是新 API 代际的第一个发布(见下文)。
|
|
158
|
+
|
|
159
|
+
| dsh-engram 发布版本 | 状态 | 支持的 DSH | 声明方式 |
|
|
160
|
+
| --- | --- | --- | --- |
|
|
161
|
+
| `0.2.0` … `0.3.6`(npm 全量 + GitHub v0.3.3–v0.3.6) | 已发布 | **仅旧版** —— `0.1.0-rc.7` / `0.1.0-rc.8` / `0.1.1-rc.1` / `0.1.1-rc.2` | peers `@deepseek-ai/dsh-*@^0.1.0-rc.7` |
|
|
162
|
+
| `0.3.7`(npm + GitHub v0.3.7) | 已发布 | **仅新版** —— `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers 同范围 |
|
|
163
|
+
|
|
164
|
+
### API 族断裂点:`0.1.2-alpha.2`
|
|
165
|
+
|
|
166
|
+
`0.1.2-alpha.2` 是新 API 族的第一个版本,也是第一个**让已发布插件失效**的版本:
|
|
167
|
+
|
|
168
|
+
- `@deepseek-ai/dsh-settings` **移除了** `settingsNamespace()` 与
|
|
169
|
+
`installSettingsSection()`(≤ `0.3.6` 的每个发布都直接 import 这两个符号),
|
|
170
|
+
改为 `settings.installSection`——旧宿主端在 `0.1.2-alpha.2+` 上直接加载失败。
|
|
171
|
+
- Web 客户端注入集整体更换:`@deepseek-ai/dsh-client-runtime` 在新代际**已不存在**
|
|
172
|
+
(被 `dsh-api-remotes` + `dsh-client-locale` / `-conversation` / `-renderer` /
|
|
173
|
+
`-ui-settings-plugins` 取代),设置卡此前改走 `ctx.remote.settings`。
|
|
174
|
+
- 同一边界的伴随升级:`@deepseek-ai/cordis` `^4.0.1 → ^4.0.2`、
|
|
175
|
+
`@deepseek-ai/schemastery` `^3.18.1 → ^3.18.2`、`@deepseek-ai/dsh` 不再随包
|
|
176
|
+
发布 `config/agent-presets`(改为 `dsh.configTrees` 机制)。
|
|
177
|
+
|
|
178
|
+
**web 设置卡已不依赖上面任何一项**:自 `0.3.7` 起,配置卡通过插件**自有**
|
|
179
|
+
HTTP 路由(`/api/dsh-engram/settings`,与记忆 viewer 同样的同源 fetch)读写
|
|
180
|
+
`dsh-engram` settings namespace,因此在 `0.1.2-alpha.2` 分界线两侧都是代际无关的。
|
|
181
|
+
|
|
182
|
+
所以:跑 ≤ `0.3.6` 的已发布版本时请把 DSH 保持在 `0.1.1-rc.2` 或更旧;升级
|
|
183
|
+
DSH 到 `0.1.2-alpha.2` 之前,先升级插件到 `>=0.3.7`。
|
|
184
|
+
|
|
185
|
+
### 当前发布版本 —— `0.3.7`
|
|
186
|
+
|
|
187
|
+
当前发布版本声明适配 DSH `>=0.1.2-alpha.2 <0.2.0-0`,并以本地
|
|
188
|
+
`deepseek-harness` 的 `dsh-v0.1.5-alpha.1` 代码和 Web 运行时完成完整验证。
|
|
189
|
+
|
|
190
|
+
| 组件 | 适配版本 |
|
|
191
|
+
| --- | --- |
|
|
192
|
+
| DSH runtime / host / client 包族 | `>=0.1.2-alpha.2 <0.2.0-0` |
|
|
193
|
+
| `@deepseek-ai/cordis` | `^4.0.2` |
|
|
194
|
+
| `@deepseek-ai/schemastery` | `^3.18.2` |
|
|
195
|
+
| Node.js | `>=22.19.0` |
|
|
196
|
+
|
|
197
|
+
`0.1.2-alpha.2` 是这组 API 的最低版本:宿主设置注册使用
|
|
198
|
+
`settings.installSection`,浏览器端设置卡通过插件自有 HTTP 路由
|
|
199
|
+
(`/api/dsh-engram/settings`,见上文 Route B)读写。
|
|
200
|
+
`0.1.2-alpha.2` 和 `0.1.3-alpha.2` 已完成宿主导入与设置注册 smoke 验证;
|
|
201
|
+
`0.1.5-alpha.1` 已完成完整回归(并在 `dsh 0.1.5-rc.1` 隔离 profile 上真机验证
|
|
202
|
+
设置 GET/PUT/冲突/复位/跨重启持久化全通)。旧的 `0.1.0-rc.7` 与 `0.1.1` API 图不在
|
|
203
|
+
本发布版本的兼容范围内。
|
|
204
|
+
|
|
205
|
+
如需按验证版本安装 DSH:
|
|
206
|
+
|
|
207
|
+
```sh
|
|
208
|
+
npm install --global @deepseek-ai/dsh@0.1.5-alpha.1
|
|
209
|
+
dsh --version
|
|
210
|
+
```
|
|
211
|
+
|
|
92
212
|
## 安装
|
|
93
213
|
|
|
94
214
|
```sh
|
|
@@ -224,8 +344,8 @@ npm run eval # 离线召回 + 结构基准(确定性语料,跑真实 store/
|
|
|
224
344
|
|
|
225
345
|
| 工具 | 用途 | 类型 |
|
|
226
346
|
|---|---|---|
|
|
227
|
-
| `engram_store` | 显式存入一条记忆(kind、tags、可选实体锚点、可选 supersedes/contradicts
|
|
228
|
-
| `engram_recall` | 工作区记忆的确定性关键词召回;可选 `search_sessions` 跨会话 FTS | 读 |
|
|
347
|
+
| `engram_store` | 显式存入一条记忆(kind、tags、可选实体锚点、可选 supersedes/contradicts id,可选 `source`/`conditions`) | 写 |
|
|
348
|
+
| `engram_recall` | 工作区记忆的确定性关键词召回;可选 `search_sessions` 跨会话 FTS;可选 `scope=global`/`recallScope` 跨工作区召回(带 `[W:<ws>]` 来源标记) | 读 |
|
|
229
349
|
| `engram_detail` | 一条记忆 id 的完整记录(来源、标签、命中数) | 读 |
|
|
230
350
|
| `esr_task` | 创建任务实体(draft → active) | 写 |
|
|
231
351
|
| `esr_close` | 按证据协议关闭任务(artifact + evaluation + memory_ref) | 写 |
|
|
@@ -402,7 +522,11 @@ ESR 操作协议(静态,每轮逐字节相同)
|
|
|
402
522
|
[ENGRAM] workspace: symbolic-index · 2 memories · 1 task(s) active · 0 links
|
|
403
523
|
[D] 06-18 Decided: use sqlite-vec for retrieval #a2331d87
|
|
404
524
|
[T] 06-18 Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref #tsk_8b26
|
|
405
|
-
drill:
|
|
525
|
+
drill: use [RECALL] below · engram_detail <id> (full record) · engram_recall <query> (more) · esr_task/esr_node/esr_link (work)
|
|
526
|
+
|
|
527
|
+
[RECALL] recall · 1 hit(s) · first msg: retrieval
|
|
528
|
+
- [D] 06-18 Decided: use sqlite-vec for retrieval upgrade #a2331d87 ×2
|
|
529
|
+
context: use above · engram_detail <id> · engram_recall <query>
|
|
406
530
|
|
|
407
531
|
[ESR] tasks: 1 active / 1 stable
|
|
408
532
|
- tsk_0d: Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref
|
|
@@ -420,6 +544,11 @@ promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-pr
|
|
|
420
544
|
`#` id 通过 `engram_detail` 取完整记录。工作区没有任务时,`[ESR]` 仍会渲染一行点名 `esr_task`/`esr_close`,
|
|
421
545
|
让机制对模型保持可见,而不是整体消失。
|
|
422
546
|
|
|
547
|
+
`[RECALL]`(会话启动自动召回,`autoRecallOnStart`)按会话首条消息对工作区做确定性 BM25 召回,
|
|
548
|
+
把命中前 `autoRecallLimit` 条注入,受 `autoRecallMaxChars` 字符预算约束;无命中或空工作区时不渲染。
|
|
549
|
+
它是一条**提示**而不是全文灌入——模型该把它当成"这条相关,可能需要用 `engram_detail` 看细节"。
|
|
550
|
+
纯读、不 bump 命中、superseded 过期真相不占槽位;随 `[ENGRAM]` 一起按会话冻结。
|
|
551
|
+
|
|
423
552
|
## 配置
|
|
424
553
|
|
|
425
554
|
默认值以 token 为优先;可通过 profile 补丁(`~/.dsh/profiles/web/cordis.patch.yml`)或 Web 配置卡片覆盖任意键:
|
|
@@ -429,6 +558,10 @@ promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-pr
|
|
|
429
558
|
config:
|
|
430
559
|
autoCapture: true # 零 LLM 工具结果捕获
|
|
431
560
|
sessionSearch: true # engram_recall 也可对历史会话 FTS
|
|
561
|
+
recallScope: workspace # 召回范围:workspace(严格工作区隔离,默认)| global(可选跨工作区召回,带 [W:<ws>] 来源标记)
|
|
562
|
+
autoRecallOnStart: true # 会话启动自动召回:按首条消息注入 [RECALL] 块(false=回到纯拉取)
|
|
563
|
+
autoRecallLimit: 3 # [RECALL] 最多注入条数(少而准)
|
|
564
|
+
autoRecallMaxChars: 700 # [RECALL] 字符预算
|
|
432
565
|
autoCapturePerSession: 40
|
|
433
566
|
indexMaxLines: 12 # [ENGRAM] 行数上限
|
|
434
567
|
indexMaxChars: 700 # [ENGRAM] 字符上限(token 预算)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Release Notes 模板(每次发布必用)
|
|
2
|
+
|
|
3
|
+
每个 GitHub Release 正文都应包含以下区块。**「DSH 版本支持」一节为必填**,
|
|
4
|
+
其余按发布内容填写。版本支持信息从 `package.json` 读取,避免手写漂移:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
# 直接生成「DSH 版本支持」markdown 段(读 package.json 的 engines.dsh + peers)
|
|
8
|
+
npm run dsh-engram:release-notes
|
|
9
|
+
# 或
|
|
10
|
+
node scripts/gen-release-notes.mjs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 模板正文(复制后填空)
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
## dsh-engram v<版本号>
|
|
19
|
+
|
|
20
|
+
<一句话:自上一个版本以来的主题>
|
|
21
|
+
|
|
22
|
+
### 功能
|
|
23
|
+
- ...
|
|
24
|
+
|
|
25
|
+
### 修复
|
|
26
|
+
- ...
|
|
27
|
+
|
|
28
|
+
### 其他
|
|
29
|
+
- ...
|
|
30
|
+
|
|
31
|
+
## DSH 版本支持(必填)
|
|
32
|
+
|
|
33
|
+
- **支持的 DSH 范围**:`<复制自 package.json engines.dsh,如 >=0.1.2-alpha.2 <0.2.0-0>`
|
|
34
|
+
- **验证版本**:`<如 dsh-v0.1.5-alpha.1>`(本地 deepseek-harness checkout)
|
|
35
|
+
- **最后验证日期**:`<YYYY-MM-DD>`
|
|
36
|
+
- **安装方式已验证**:`<如 dsh plugin --profile web add dsh-engram>`
|
|
37
|
+
- **peer 声明**:`<复制自 package.json peerDependencies 中的 @deepseek-ai/dsh-* 范围>`
|
|
38
|
+
|
|
39
|
+
> ⚠️ 新旧分界:DSH `0.1.2-alpha.2` 起 settings/client API 族大改
|
|
40
|
+
> (`@deepseek-ai/dsh-settings` 移除 `settingsNamespace`/`installSettingsSection`,
|
|
41
|
+
> 改 `settings.installSection`;client 注入集从 `dsh-client-runtime` 换到
|
|
42
|
+
> `dsh-api-remotes` 等;cordis `^4.0.2`、schemastery `^3.18.2`)。
|
|
43
|
+
> 若本版本仍声明 peers `^0.1.0-rc.7`,说明它**只支持旧版 DSH**
|
|
44
|
+
> (≤ `0.1.1-rc.2`),正文必须写明「仅支持旧版 DSH」并用上面第一段替换当前示例。
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 历史版本口径(补注用,legacy releases 说明)
|
|
50
|
+
|
|
51
|
+
已发布版本 `0.2.0` … `0.3.6` 的 Release/README 如果缺失 DSH 兼容说明,统一补这
|
|
52
|
+
一段:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
## DSH 版本支持
|
|
56
|
+
|
|
57
|
+
- **支持的 DSH 范围**:仅旧版 —— `0.1.0-rc.7` / `0.1.0-rc.8` / `0.1.1-rc.1` / `0.1.1-rc.2`
|
|
58
|
+
- **peer 声明**:`@deepseek-ai/dsh-*@^0.1.0-rc.7`
|
|
59
|
+
- ⚠️ DSH `>=0.1.2-alpha.2` 与本版本**不兼容**(settings/client API 族在该版本断裂)。
|
|
60
|
+
如需新版 DSH,请升级插件到 `master` 的下一个发布。
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## dsh-market 兼容信息格式参考
|
|
64
|
+
|
|
65
|
+
- 包清单的版本兼容字段是 **semver 范围**:`package.json` / `dsh.plugin.json` 的
|
|
66
|
+
`engines.dsh`(如 `">=0.1.5-alpha.2"` 或 `"0.1.0-rc.6 || 0.1.2-rc.1"`)。
|
|
67
|
+
- 市场侧 README 公约(`## Compatibility` 段):
|
|
68
|
+
`DSH 版本` · `最后验证日期` · `安装方式已验证`。
|