dsh-engram 0.3.4 → 0.3.6
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 +283 -27
- package/README.zh.md +184 -16
- package/cordis.patch.yml +6 -0
- package/docs/CONTEXT-GC-GUIDE.zh.md +192 -0
- package/docs/PROPOSAL-agentmemory.zh.md +108 -0
- package/docs/PROPOSAL-beads.zh.md +81 -0
- package/docs/PROPOSAL-contextgc.zh.md +188 -0
- package/docs/PROPOSAL-esrtrigger.zh.md +199 -0
- package/docs/PROPOSAL-hindsight.zh.md +156 -0
- package/lib/api.js +76 -2
- package/lib/capture.js +132 -4
- package/lib/client.js +45 -2
- package/lib/compaction.js +2 -18
- package/lib/context-gc.js +234 -2
- package/lib/goal-capture.js +89 -0
- package/lib/index-block.js +96 -1
- package/lib/index.js +233 -5
- package/lib/redact.js +48 -0
- package/lib/session-cjk.js +159 -0
- package/lib/settings.js +13 -0
- package/lib/status.js +57 -0
- package/lib/store.js +411 -6
- package/lib/todo-sink.js +146 -0
- package/lib/tools.js +98 -40
- package/lib/trigger.js +10 -8
- package/lib/usage.js +147 -5
- package/lib/web-provision.js +430 -0
- package/package.json +13 -2
- package/scripts/dsh-engram.mjs +268 -0
- package/scripts/engram-web-compaction.mjs +7 -0
- package/scripts/prepublish-check.mjs +106 -0
- package/scripts/setup-links.mjs +241 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> **[English](README.md) · [中文](README.zh.md)**
|
|
4
4
|
|
|
5
|
+
[](https://github.com/skepsun/dsh-engram/actions/workflows/ci.yml)
|
|
6
|
+
|
|
5
7
|
Minimalist long-term memory for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness), distilled from the
|
|
6
8
|
[symbolic-index](https://github.com/skepsun/symbolic-index) and [pi-esr](https://github.com/skepsun/pi-esr) ideas —
|
|
7
9
|
with one goal: **save tokens**.
|
|
@@ -11,6 +13,16 @@ with one goal: **save tokens**.
|
|
|
11
13
|
key files, repeated errors), plus an explicit `engram_store`. Nothing on the hot
|
|
12
14
|
path calls a model, and pure plumbing — `git push` / `git stash` / silent
|
|
13
15
|
commits — is deliberately never recorded (see *Auto-capture policy* below).
|
|
16
|
+
Every write is run through a deterministic **secret redactor** first — API
|
|
17
|
+
keys, JWTs, `Bearer` tokens, private keys, AWS/Stripe/Slack/GitHub tokens and
|
|
18
|
+
`key=value` secret shapes are replaced with `<REDACTED:…>` markers before the
|
|
19
|
+
dedup hash, the char cap, and storage, so sensitive text can never land on
|
|
20
|
+
disk. Failing **test runs** are recognized by pure patterns (`npm test`,
|
|
21
|
+
`node --test`, vitest/pytest/jest, failure lines) and captured as
|
|
22
|
+
`tags:["error","test"]` entries with a raised signal. DSH **goal-domain
|
|
23
|
+
integration**: a completed or blocked goal (`goal/change` session event)
|
|
24
|
+
auto-sediments as a `handoff`/`error` memory (`tag:goal`), so the outcome of
|
|
25
|
+
a goal survives its session (see the `GET /api/dsh-engram/goals` read surface).
|
|
14
26
|
- **Symbolic index + progressive disclosure** — a compact `[ENGRAM]` block (default
|
|
15
27
|
budget 700 chars ≈ 175 tokens; one line per memory) is injected at prompt
|
|
16
28
|
assembly and **frozen per session**, keeping the request prefix byte-stable for
|
|
@@ -23,7 +35,36 @@ with one goal: **save tokens**.
|
|
|
23
35
|
(repeat-failure revival: recency + hit climb toward promoteHits so the
|
|
24
36
|
failure resurfaces instead of piling up), and on zero local hits falls back
|
|
25
37
|
to DSH's own cross-session full-text index (`ctx.sessionQuery`, filtered by
|
|
26
|
-
cwd) instead of building a parallel SQLite index
|
|
38
|
+
cwd) instead of building a parallel SQLite index; when the FTS fallback also
|
|
39
|
+
comes back empty and the query contains **CJK** characters (Chinese FTS5
|
|
40
|
+
tokenization), recall runs a bounded substring scan over the newest session
|
|
41
|
+
logs (zstd-decompressed, LRU-cached) and appends matched past sessions —
|
|
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.
|
|
54
|
+
- **Memory-to-memory semantics** — `engram_store` accepts optional
|
|
55
|
+
`supersedes` / `contradicts` memory ids (validated against the same
|
|
56
|
+
workspace). A superseded ("stale truth") memory is demoted to the tail of
|
|
57
|
+
recall and **excluded from the `[ENGRAM]` block**, while the superseding
|
|
58
|
+
statement keeps the line; a contradicted memory stays ranked but is flagged
|
|
59
|
+
`· contradicted by <id>`. Old rows are never deleted — re-fetchable, just
|
|
60
|
+
honestly ranked. Opt-in `autoSupersede` config (default **off**) auto-marks
|
|
61
|
+
entity-anchored replacement updates ("改用 / no longer / switched…") as
|
|
62
|
+
superseding their matching older row; explicit `supersedes` always wins.
|
|
63
|
+
- **Failure→fix closure (zero LLM)** — error memories carry a `cmd:` signature
|
|
64
|
+
tag; when that same command later succeeds, engram auto-sediments a
|
|
65
|
+
`procedure` memory (`fixed: <cmd> — N earlier failing runs now succeed`),
|
|
66
|
+
tags the earlier error rows `resolved` (never re-ranking them), and recall
|
|
67
|
+
then surfaces the fix instead of the stale failure.
|
|
27
68
|
- **ESR-lite closure protocol** — `esr_task` / `esr_close` / `esr_link` give tasks
|
|
28
69
|
a `draft → active → stable` lifecycle where `stable` requires real evidence
|
|
29
70
|
(`artifact` / `evaluation` / `memory_ref`), surfacing closure gaps instead of
|
|
@@ -56,6 +97,18 @@ with one goal: **save tokens**.
|
|
|
56
97
|
The `tools/result` recorder feeds the actionables' data sources; P4
|
|
57
98
|
conversion metering stays (`#suggest-*` tags + 10-minute attribution +
|
|
58
99
|
`GET /api/dsh-engram/triggerstats`). Pure rules, zero LLM.
|
|
100
|
+
- **Session-end todo auto-sink (draft safety net, `autoSinkTodosOnEnd` default
|
|
101
|
+
on)** — during a session the native todo stays cheap, per-session working
|
|
102
|
+
memory that dies with the session. But when a session closes at its teardown
|
|
103
|
+
edge (`session/disposed`) while its plan still has pending todos, they are
|
|
104
|
+
**auto-landed as ESR `draft` tasks** (name = the todo text, description
|
|
105
|
+
marked 「源自会话计划」, deduped against existing task names, capped by
|
|
106
|
+
`maxTasksPerWorkspace`), so the plan never silently evaporates. Sinking to
|
|
107
|
+
**draft** — not active — keeps them out of the `[ESR]` active rows and creates
|
|
108
|
+
no evidence obligation; an explicit `esr_claim` / `esr_task` step is still
|
|
109
|
+
what promotes a draft to active. The "nothing auto-written during the
|
|
110
|
+
session" trade-off is unchanged; only the end-of-session data-loss gap is
|
|
111
|
+
closed. Disable via profile patch or the settings card.
|
|
59
112
|
- **Memory GC (pi-esr constraints)** — a scheduled, mechanical, archive-only
|
|
60
113
|
sweep: TTL-expired memories are archived, over-cap workspaces evict the
|
|
61
114
|
lowest-value entries, stable tasks past their retention window leave the
|
|
@@ -86,14 +139,105 @@ LLM-distillation and vector/graph niches are already crowded. dsh-engram fills t
|
|
|
86
139
|
three gaps that matter for token discipline:
|
|
87
140
|
|
|
88
141
|
1. **No model in the write path** — capture is deterministic pattern matching.
|
|
89
|
-
2. **No raw text in the prompt** — a bounded symbolic index is injected,
|
|
90
|
-
|
|
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").
|
|
91
145
|
3. **Honest task closure** — STABLE cannot be declared without evidence.
|
|
92
146
|
|
|
93
147
|
DSH already provides cross-session FTS (`ctx.sessionQuery`), storage
|
|
94
148
|
(`ctx.storageDomain`), prompt-injection hooks and settings slots; dsh-engram is a
|
|
95
149
|
thin composition layer over them, not a re-implementation.
|
|
96
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
|
+
|
|
97
241
|
## Install
|
|
98
242
|
|
|
99
243
|
```sh
|
|
@@ -275,15 +419,20 @@ npm run build:client
|
|
|
275
419
|
|
|
276
420
|
| Tool | Purpose | Kind |
|
|
277
421
|
|---|---|---|
|
|
278
|
-
| `engram_store` | Explicitly store one memory (kind, tags, optional entity anchor) | write |
|
|
279
|
-
| `engram_recall` | Deterministic keyword recall over workspace memories; optional `search_sessions` FTS over past sessions | read |
|
|
422
|
+
| `engram_store` | Explicitly store one memory (kind, tags, optional entity anchor, optional supersedes/contradicts ids, optional `source`/`conditions`) | write |
|
|
423
|
+
| `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 |
|
|
280
424
|
| `engram_detail` | Full record of one memory id (provenance, tags, hits) | read |
|
|
281
425
|
| `esr_task` | Create a task entity (draft → active) | write |
|
|
282
426
|
| `esr_close` | Close a task via the evidence protocol (artifact + evaluation + memory_ref) | write |
|
|
283
427
|
| `esr_link` | Add a typed relation between two entities (mini graph) | write |
|
|
428
|
+
| `esr_dep` | Add a dependency edge (blocks / relates-to / parent-of) between tasks | write |
|
|
429
|
+
| `esr_claim` | Atomically claim a task (assignee + claimedAt, draft → active) | write |
|
|
430
|
+
| `esr_unclaim` | Release a claimed task's assignee | write |
|
|
284
431
|
| `esr_ready` | List claimable tasks (no open blocker, unclaimed) | read |
|
|
285
432
|
| `esr_status` | Pull live ESR state + derived hints (`since_revision` short "unchanged" response) | read |
|
|
433
|
+
| `esr_node` | Create/update an entity node (stable symbol) | write |
|
|
286
434
|
| `esr_gc` | Run the memory GC for the workspace (`dry_run:true` previews) | write |
|
|
435
|
+
| `esr_model` | Precomputed mental model of the workspace (`brief`/`full`, `max_chars`) | read |
|
|
287
436
|
|
|
288
437
|
The `[ESR]` block is a frozen per-session snapshot and never auto-refreshes —
|
|
289
438
|
call `esr_status` for the live truth.
|
|
@@ -331,24 +480,74 @@ Triggers follow DSH's own compaction timing (step pressure / context-overflow /
|
|
|
331
480
|
`/compact`); the lock, replay validation, tool-call/result pairing and token
|
|
332
481
|
pricing are all reused from the basic engine. Any error falls back to the
|
|
333
482
|
default compressor — compaction is never broken; with `gcReplacesCompaction:
|
|
334
|
-
false` (or a host without `dsh-compaction-basic`)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
483
|
+
false` (or a host without `dsh-compaction-basic`) a bare `BasicCompactionEngine`
|
|
484
|
+
is mounted instead, so the `compaction` service never disappears while
|
|
485
|
+
dsh-engram is installed. Mounting registers the `compaction` service on the
|
|
486
|
+
plugin's fiber, so unloading/reloading engram restores the default engine.
|
|
487
|
+
|
|
488
|
+
> **Shrink gate**: the harness refuses any checkpoint that is not strictly
|
|
489
|
+
> smaller than the evicted span (`summary is not smaller than the shadowed
|
|
490
|
+
> content`) and rolls the compaction back — otherwise the context never shrinks
|
|
491
|
+
> and the session eventually hits the model-side overflow. Context GC
|
|
492
|
+
> self-predicts that gate: it mirrors the harness framing with the host
|
|
493
|
+
> `tokenMeter` (verified token-for-token identical) and truncates the
|
|
494
|
+
> pointer/narrative body to the evicted span's token budget (the pointer head
|
|
495
|
+
> and working set survive; the trimmed tail stays recoverable in the session
|
|
496
|
+
> log), so compactions actually commit. Full from-scratch usage:
|
|
497
|
+
> [`docs/CONTEXT-GC-GUIDE.zh.md`](docs/CONTEXT-GC-GUIDE.zh.md).
|
|
498
|
+
|
|
499
|
+
#### Where it is enabled (the entry point)
|
|
500
|
+
|
|
501
|
+
Assembly happens on **two planes** — and web is now fully automatic, zero config:
|
|
502
|
+
|
|
503
|
+
- **Host plane (headless / TUI / base profiles) — on by default, zero config**:
|
|
504
|
+
the main plugin (`lib/index.js`) mounts the engine inside `ctx.effect` via
|
|
505
|
+
`mountCompactionEngine(ctx, resolved, { readWorkspace }) → new Engine()`,
|
|
506
|
+
registering the `compaction` service on its own fiber; the plugin patch
|
|
507
|
+
disables the base `compaction-basic` row, making engram the sole host-plane
|
|
508
|
+
provider. `gcReplacesCompaction: true` (default) → `ContextGcEngine`;
|
|
509
|
+
`false` → a bare `BasicCompactionEngine`. Tune via profile patch
|
|
510
|
+
(`config: { gcReplacesCompaction: false }`) or the settings card's
|
|
511
|
+
`gcNarrative` knob (narrative off = fully mechanical).
|
|
512
|
+
- **Preset plane (web profile) — fully automatic, zero config, covers every
|
|
513
|
+
preset**: web keeps compaction inside each agent preset's isolated realm
|
|
514
|
+
(the shipped presets mount their own `compaction-basic`), unreachable from a
|
|
515
|
+
host-plane row — and unreachable from a profile patch too (`mountPreset`
|
|
516
|
+
builds its `Include.Config` with no patches; verified against the harness
|
|
517
|
+
source). So on boot, once the `agentPresets` service is ready, the plugin
|
|
518
|
+
**auto-edits every preset that still has the stock layout** — the default
|
|
519
|
+
preset plus the whole roster (shipped root + `~/.dsh/.agent-presets` user
|
|
520
|
+
root: `standard`, `code`, `cordis`, and your own presets): it swaps the
|
|
521
|
+
`compaction-basic` row inside the `compaction` group for
|
|
522
|
+
`dsh-engram/compaction`. That auto-wiring is:
|
|
523
|
+
- **on by default** (`autoWebCompaction: true`, toggle in the settings card's
|
|
524
|
+
memory-GC section), idempotent — an already-wired preset is left alone;
|
|
525
|
+
**what "off" means**: the plugin stops auto-wiring on later boots (the
|
|
526
|
+
headless/TUI/base host plane is unaffected) — it does NOT un-wire presets
|
|
527
|
+
that were already wired; use `npm run web-compaction:revert` for that;
|
|
528
|
+
- **per-preset**: only stock-layout presets are touched — a preset with a
|
|
529
|
+
custom compaction group, or none at all (e.g. the shipped `minimal`
|
|
530
|
+
preset), is never written, only logged;
|
|
531
|
+
- **backed up + validated**: a create-only `agent.cordis.yml.engram.bak`
|
|
532
|
+
holds each original next to its file, every result is re-read and verified
|
|
533
|
+
after writing, and any doubt rolls that file back; a failure only warns and
|
|
534
|
+
that session keeps DSH's default summarizer — the host plane is never
|
|
535
|
+
affected.
|
|
536
|
+
- **config actually propagates**: the engine config written into each row
|
|
537
|
+
comes from the live settings (`gcReplacesCompaction` / `gcNarrative`) — with
|
|
538
|
+
"already wired but with a different config" treated as a refresh, so
|
|
539
|
+
changing the settings knobs re-provisions the web rows on the next boot
|
|
540
|
+
instead of being silently ignored.
|
|
541
|
+
- A fully equivalent manual CLI, shipped in the npm package
|
|
542
|
+
(scans both the shipped and the user root; `--file <path>` targets one):
|
|
543
|
+
```bash
|
|
544
|
+
npx dsh-engram status # stock = untouched · wired = active · custom = hands-off
|
|
545
|
+
npx dsh-engram doctor # status + ranked next steps for every gap found
|
|
546
|
+
npx dsh-engram enable # same as the boot-time auto-wiring (usually a no-op)
|
|
547
|
+
npx dsh-engram revert # restore the stock row everywhere — run BEFORE uninstalling
|
|
548
|
+
```
|
|
549
|
+
(in the repo, `npm run web-compaction:*` is an alias of the same CLI.)
|
|
550
|
+
Either way the `compaction` group becomes:
|
|
352
551
|
```yaml
|
|
353
552
|
- id: compaction
|
|
354
553
|
name: cordis:group
|
|
@@ -360,14 +559,51 @@ provider of the same service in the same realm (`provide()` throws
|
|
|
360
559
|
- id: engram-compaction # replaces the original compaction-basic
|
|
361
560
|
name: dsh-engram/compaction
|
|
362
561
|
config:
|
|
363
|
-
gcReplacesCompaction: true #
|
|
364
|
-
gcNarrative: true
|
|
562
|
+
gcReplacesCompaction: true # from settings; false = default LLM summary for this session
|
|
563
|
+
gcNarrative: true # from settings; false = fully mechanical
|
|
365
564
|
- id: command-compact
|
|
366
565
|
name: '@deepseek-ai/dsh-command-compact'
|
|
367
566
|
- id: tool-result-pruner
|
|
368
567
|
name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
|
|
369
568
|
# keep the original config
|
|
370
569
|
```
|
|
570
|
+
⚠️ **Uninstall = revert first**: once a preset references
|
|
571
|
+
`dsh-engram/compaction`, removing dsh-engram leaves every such row dangling
|
|
572
|
+
and web sessions hang on load — so run `npx dsh-engram revert`
|
|
573
|
+
(restores every preset) BEFORE uninstalling. A harness upgrade that ships a
|
|
574
|
+
fresh standard preset overwrites the swap and self-heals the dangling
|
|
575
|
+
reference.
|
|
576
|
+
|
|
577
|
+
#### First impressions after an npm install (perceive / guide / configure)
|
|
578
|
+
|
|
579
|
+
- **Automatic**: install into a profile and boot once — the host plane is taken
|
|
580
|
+
over by `mountCompactionEngine`, and the web plane by `autoWebCompaction`
|
|
581
|
+
(default on) rewriting every stock preset at boot. **Zero manual setup.**
|
|
582
|
+
- **Perceivable**: at boot the plugin writes an authoritative snapshot to
|
|
583
|
+
`$DSH_HOME/engram/context-gc.status.json` (`host` plane mode + the per-preset
|
|
584
|
+
`web` results + effective config). At any time run `npx dsh-engram status` or
|
|
585
|
+
`npx dsh-engram doctor`; the web memory board (ESR kanban) also shows a
|
|
586
|
+
header chip ("Context GC·主机 ·N 预设") fed by the overview API. Two startup
|
|
587
|
+
log lines confirm it too: `engram context-gc: compaction = Context GC …` and
|
|
588
|
+
`engram web-provision: wired Context GC into preset …`.
|
|
589
|
+
- **Configurable**: one settings card (memory-GC section) governs everything:
|
|
590
|
+
`autoWebCompaction` (web auto-wiring on/off), `gcReplacesCompaction`
|
|
591
|
+
(Context GC vs default summarizer), `gcNarrative` (narrative safety net).
|
|
592
|
+
Changes take effect after restarting `dsh web`, and the web rows are
|
|
593
|
+
refreshed to match automatically.
|
|
594
|
+
|
|
595
|
+
**Verify it took**: after restarting `dsh web` (or the profile) —
|
|
596
|
+
- host plane (headless/TUI/base): the startup log shows
|
|
597
|
+
`engram context-gc: compaction = Context GC (mechanical eviction + re-fetch pointers)`;
|
|
598
|
+
- web plane: the log shows one line per wired preset —
|
|
599
|
+
`engram web-provision: wired Context GC into preset "standard" (…); restart dsh web so sessions pick it up`,
|
|
600
|
+
or `npx dsh-engram status` reports `wired` for each; sessions composed
|
|
601
|
+
from ANY preset then compact via mechanical eviction + re-fetch pointers.
|
|
602
|
+
A line like `… keeping the existing compaction service` means another provider
|
|
603
|
+
already owns `compaction` in that realm (the swap didn't take); `web-provision … skipped (custom)`
|
|
604
|
+
means that preset was customized (or has no compaction group) and was left untouched;
|
|
605
|
+
`dsh-compaction-basic unavailable` means the backend is missing and everything
|
|
606
|
+
falls back to the default compressor.
|
|
371
607
|
|
|
372
608
|
## Auto-capture policy
|
|
373
609
|
|
|
@@ -408,7 +644,11 @@ ESR operating protocol (static, byte-identical every turn)
|
|
|
408
644
|
[ENGRAM] workspace: symbolic-index · 2 memories · 1 task(s) active · 0 links
|
|
409
645
|
[D] 06-18 Decided: use sqlite-vec for retrieval #a2331d87
|
|
410
646
|
[T] 06-18 Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref #tsk_8b26
|
|
411
|
-
drill:
|
|
647
|
+
drill: use [RECALL] below · engram_detail <id> (full record) · engram_recall <query> (more) · esr_task/esr_node/esr_link (work)
|
|
648
|
+
|
|
649
|
+
[RECALL] recall · 1 hit(s) · first msg: retrieval
|
|
650
|
+
- [D] 06-18 Decided: use sqlite-vec for retrieval upgrade #a2331d87 ×2
|
|
651
|
+
context: use above · engram_detail <id> · engram_recall <query>
|
|
412
652
|
|
|
413
653
|
[ESR] tasks: 1 active / 1 stable
|
|
414
654
|
- tsk_0d: Retrieval upgrade — ACTIVE · gap: artifact, evaluation, memory_ref
|
|
@@ -430,6 +670,14 @@ records via `engram_detail`. When a workspace has no tasks, `[ESR]` still render
|
|
|
430
670
|
one line naming `esr_task`/`esr_close` so the mechanism stays visible to the
|
|
431
671
|
model instead of vanishing.
|
|
432
672
|
|
|
673
|
+
`[RECALL]` (session-start auto recall, `autoRecallOnStart`) runs one deterministic
|
|
674
|
+
BM25 pass over the workspace keyed on the session's first user message and injects
|
|
675
|
+
the top `autoRecallLimit` hits, bounded by the `autoRecallMaxChars` character
|
|
676
|
+
budget; nothing renders on zero hits or an empty workspace. It is a *pointer* — the
|
|
677
|
+
model should treat it as "this is relevant; `engram_detail` for the full record" —
|
|
678
|
+
rather than full-text dumping. It is a pure read (never bumps `hits`), superseded
|
|
679
|
+
stale truth never occupies a slot, and it freezes per session with `[ENGRAM]`.
|
|
680
|
+
|
|
433
681
|
## Config
|
|
434
682
|
|
|
435
683
|
Defaults are token-conscious; override any key via the profile patch
|
|
@@ -440,6 +688,10 @@ Defaults are token-conscious; override any key via the profile patch
|
|
|
440
688
|
config:
|
|
441
689
|
autoCapture: true # zero-LLM tool-result capture
|
|
442
690
|
sessionSearch: true # engram_recall may also FTS past sessions
|
|
691
|
+
recallScope: workspace # recall scope: "workspace" (strict isolation, default) | "global" (opt-in cross-workspace recall with [W:<ws>] origin markers)
|
|
692
|
+
autoRecallOnStart: true # session-start auto recall (false = back to pure pull)
|
|
693
|
+
autoRecallLimit: 3 # [RECALL] max injected hits (few and precise)
|
|
694
|
+
autoRecallMaxChars: 700 # [RECALL] char budget
|
|
443
695
|
autoCapturePerSession: 40
|
|
444
696
|
indexMaxLines: 12 # [ENGRAM] line cap
|
|
445
697
|
indexMaxChars: 700 # [ENGRAM] char cap (token budget)
|
|
@@ -482,7 +734,11 @@ dedup 1.0.
|
|
|
482
734
|
**/api/dsh-engram/stats + the observability panel** is the real-session layer —
|
|
483
735
|
it answers how the model actually uses the memory in production (ESR
|
|
484
736
|
proactivity ratio, recall hit rate, detail conversion), while the eval
|
|
485
|
-
answers how good the retrieval layer itself is.
|
|
737
|
+
answers how good the retrieval layer itself is. The stats read the
|
|
738
|
+
concatenated multi-frame `session.jsonl.zstd` logs with Node's built-in
|
|
739
|
+
`node:zlib` (frame scan + one reused decoder handle) — **no system `zstd` CLI
|
|
740
|
+
is required**, so the telemetry works out-of-the-box on Windows and other
|
|
741
|
+
hosts without an external codec.
|
|
486
742
|
|
|
487
743
|
Repo layout: `lib/` (host half: store / capture / index-block / tools / api /
|
|
488
744
|
settings), `client/` (browser half, TSX + `build.mjs`), `test/` (node:test).
|