dsh-engram 0.5.1 → 0.6.0

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 CHANGED
@@ -264,8 +264,8 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
264
264
  <!-- dsh-market compatibility block — keep labels bilingual and in sync with
265
265
  docs/RELEASE-NOTES.TEMPLATE.md (market README convention) -->
266
266
 
267
- - **DSH 版本 / DSH versions**: `>=0.1.2-alpha.2 <0.3.0-0` (本版 `0.5.1`; 已发布的 `0.3.7`、`0.4.0` 仍声明旧的 `0.1.x` 范围 `>=0.1.2-alpha.2 <0.2.0-0` — 见下方矩阵)
268
- - **最后验证日期 / Last verified**: 2026-10-19 — 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)+ 数据导出/导入与替换/冲突可视化(issue #3),npm test 334/334 通过
267
+ - **DSH 版本 / DSH versions**: `>=0.1.2-alpha.2 <0.3.0-0` (本版 `0.6.0`; 已发布的 `0.3.7`、`0.4.0` 仍声明旧的 `0.1.x` 范围 `>=0.1.2-alpha.2 <0.2.0-0` — 见下方矩阵)
268
+ - **最后验证日期 / Last verified**: 2026-10-19 — Context Ledger / 默认值纠偏 / JIT 推送,npm test 359/359 通过
269
269
  - **安装方式已验证 / Verified install**: `dsh plugin --profile web add dsh-engram`
270
270
 
271
271
  > **Short version.** Every **published** release (`0.2.0` … `0.3.6`) supports
@@ -280,7 +280,8 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
280
280
  | `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` |
281
281
  | `0.3.7` / `0.4.0` (npm) | published | **new DSH, settings `installSection`** — `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.2.0-0` |
282
282
  | `0.5.0` (npm) | published | **DSH `0.1.2-alpha.2` … `<0.3.0-0`** — 设置卡迁移到 entry-id volatile Config(`0.1.7+`/`0.2.x`),`≤0.1.5` 自动回退 `installSection` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.3.0-0` |
283
- | `0.5.1` (本期 / this release) | published | 同 `0.5.0` — 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)、数据导出/导入 GUI、替换/冲突可视化(issue #3) | 同 `0.5.0` |
283
+ | `0.5.1` | published | 同 `0.5.0` — 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)、数据导出/导入 GUI、替换/冲突可视化(issue #3) | 同 `0.5.0` |
284
+ | `0.6.0` (本期 / this release) | published | 同 `0.5.0` — Context Ledger、BoundaryPrune/ResultPack 默认开 + 账本否决、JIT 推送、Context GC 改 opt-in | 同 `0.5.0` |
284
285
 
285
286
  ### Where the API family broke — `0.1.2-alpha.2`
286
287
 
@@ -314,7 +315,7 @@ the loader), and the settings form is keyed by the profile entry id (`engram`),
314
315
  so nothing needs "mounting". `0.5.0` feature-detects the two generations in
315
316
  `installEngramSettings`:
316
317
 
317
- - DSH `≥0.1.7` / `0.2.x` — the 27 GUI knobs are the volatile `Config` fields
318
+ - DSH `≥0.1.7` / `0.2.x` — the 31 GUI knobs are the volatile `Config` fields
318
319
  (`lib/settings.js` `makeEngineConfigSchema`, exported as `Config` from
319
320
  `lib/index.js`); the card reads/writes entry `engram` through the same
320
321
  `/api/dsh-engram/settings` HTTP route, and the `service.settings` seam
@@ -330,14 +331,27 @@ drives mutations).
330
331
  So: keep DSH on `0.1.1-rc.2` or older while running a release ≤ `0.3.6`, and
331
332
  upgrade the plugin to `>=0.3.7` before moving DSH to `0.1.2-alpha.2`.
332
333
 
333
- ### Current release — `0.5.1`
334
-
335
- Patch release on top of `0.5.0`: session scanning resolves the **versioned** log
336
- name (`session.v3/v4.jsonl.zstd`) instead of the hardcoded
337
- `session.jsonl.zstd` — on current DSH that mismatch made `/stats`, `/toolstats`,
338
- BoundaryPrune's repayment history and the CJK session search all return zero
339
- silently (issue #8). The memory viewer also gains data export/import and
340
- supersede/contradict visualization (issue #3).
334
+ ### Current release — `0.6.0`
335
+
336
+ From a "memory plugin" to a **context portfolio**: make token savings auditable
337
+ first, then use the ledger to correct defaults, veto unprofitable prunes and
338
+ push memory at deterministic moments.
339
+
340
+ - **Context Ledger** — per session / workspace / day
341
+ `net = avoided − injected − rewrite penalty`; exposed on `/stats` and
342
+ `/overview`, rendered on the telemetry page, persisted to
343
+ `~/.dsh/engram/ledger.json`.
344
+ - **Default correction** — `boundaryPrune` / `resultPack` default ON (pruning is
345
+ lossless via the archive); `gcReplacesCompaction` / `autoWebCompaction` are
346
+ now opt-in (no preset rewriting by default).
347
+ - **Ledger veto** — `boundaryPruneAdaptive` stops pruning a workspace whose own
348
+ accounting is net negative after ≥3 real fires (fail-open).
349
+ - **JIT push** — error recurrence / file focus / goal completion, append-only,
350
+ ≤3 lines per session, silent without a hit; `[JIT]` section + `jitEnabled`.
351
+ - **Migration** — restart `dsh web` after upgrading; if you still want Context
352
+ GC takeover, set `gcReplacesCompaction:true` (plus `autoWebCompaction:true`
353
+ for web presets). The `0.5.1` versioned session-log fix (issue #8) and data
354
+ backup GUI (issue #3) are unchanged.
341
355
 
342
356
  The published `0.4.0` declared `>=0.1.2-alpha.2 <0.2.0-0` — which the semver
343
357
  gate **rejects on every DSH `0.2.0-0` prerelease** (`<0.2.0-0` excludes
@@ -358,7 +372,7 @@ feature-detected legacy fallback).
358
372
  `0.1.2-alpha.2` is the minimum version for this API family: host settings are
359
373
  **feature-detected** — on DSH `≤0.1.5` they register the `dsh-engram`
360
374
  namespace via `settings.installSection`; on `≥0.1.7` / `0.2.x` the form is the
361
- plugin's `Config` with the 27 GUI knobs marked `.volatile()` (entry id
375
+ plugin's `Config` with the 31 GUI knobs marked `.volatile()` (entry id
362
376
  `engram`). The browser settings card reads and writes through the plugin's own
363
377
  HTTP route (`/api/dsh-engram/settings`, see Route B above) on both. The full
364
378
  regression suite (npm test 329/329, incl. the new settings-v2 coverage for the
@@ -623,10 +637,12 @@ Triggers follow DSH's own compaction timing (step pressure / context-overflow /
623
637
  `/compact`); the lock, replay validation, tool-call/result pairing and token
624
638
  pricing are all reused from the basic engine. Any error falls back to the
625
639
  default compressor — compaction is never broken; with `gcReplacesCompaction:
626
- false` (or a host without `dsh-compaction-basic`) a bare `BasicCompactionEngine`
627
- is mounted instead, so the `compaction` service never disappears while
628
- dsh-engram is installed. Mounting registers the `compaction` service on the
629
- plugin's fiber, so unloading/reloading engram restores the default engine.
640
+ false` (the default since v0.6, or a host without `dsh-compaction-basic`) a bare
641
+ `BasicCompactionEngine` is mounted instead, so the `compaction` service never
642
+ disappears while dsh-engram is installed. Mounting registers the `compaction`
643
+ service on the plugin's fiber, so unloading/reloading engram restores the
644
+ default engine. **Taking over compaction is opt-in**: turn on
645
+ `gcReplacesCompaction` (and `autoWebCompaction` for web presets).
630
646
 
631
647
  > **Shrink gate**: the harness refuses any checkpoint that is not strictly
632
648
  > smaller than the evicted span (`summary is not smaller than the shadowed
@@ -641,19 +657,19 @@ plugin's fiber, so unloading/reloading engram restores the default engine.
641
657
 
642
658
  #### Where it is enabled (the entry point)
643
659
 
644
- Assembly happens on **two planes** — and web is now fully automatic, zero config:
660
+ Assembly happens on **two planes**, but both switches are opt-in (no takeover by
661
+ default):
645
662
 
646
- - **Host plane (headless / TUI / base profiles) — on by default, zero config**:
663
+ - **Host plane (headless / TUI / base profiles)**:
647
664
  the main plugin (`lib/index.js`) mounts the engine inside `ctx.effect` via
648
665
  `mountCompactionEngine(ctx, resolved, { readWorkspace }) → new Engine()`,
649
666
  registering the `compaction` service on its own fiber; the plugin patch
650
667
  disables the base `compaction-basic` row, making engram the sole host-plane
651
- provider. `gcReplacesCompaction: true` (default) → `ContextGcEngine`;
652
- `false` → a bare `BasicCompactionEngine`. Tune via profile patch
653
- (`config: { gcReplacesCompaction: false }`) or the settings card's
654
- `gcNarrative` knob (narrative off = fully mechanical).
655
- - **Preset plane (web profile) — fully automatic, zero config, covers every
656
- preset**: web keeps compaction inside each agent preset's isolated realm
668
+ provider. `gcReplacesCompaction: false` (default) → a bare
669
+ `BasicCompactionEngine` (DSH's own summarizer); `true` → `ContextGcEngine`
670
+ (pointer summaries). Enable via profile patch
671
+ (`config: { gcReplacesCompaction: true }`) or the settings card.
672
+ - **Preset plane (web profile) — covers every stock preset once enabled**: web keeps compaction inside each agent preset's isolated realm
657
673
  (the shipped presets mount their own `compaction-basic`), unreachable from a
658
674
  host-plane row — and unreachable from a profile patch too (`mountPreset`
659
675
  builds its `Include.Config` with no patches; verified against the harness
@@ -663,8 +679,9 @@ Assembly happens on **two planes** — and web is now fully automatic, zero conf
663
679
  root: `standard`, `code`, `cordis`, and your own presets): it swaps the
664
680
  `compaction-basic` row inside the `compaction` group for
665
681
  `dsh-engram/compaction`. That auto-wiring is:
666
- - **on by default** (`autoWebCompaction: true`, toggle in the settings card's
667
- memory-GC section), idempotent — an already-wired preset is left alone;
682
+ - **off by default (opt-in)** (`autoWebCompaction: false`, toggle in the
683
+ settings card's memory-GC section); once enabled it is idempotent — an
684
+ already-wired preset is left alone;
668
685
  **what "off" means**: the plugin stops auto-wiring on later boots (the
669
686
  headless/TUI/base host plane is unaffected) — it does NOT un-wire presets
670
687
  that were already wired; use `npm run web-compaction:revert` for that;
@@ -719,9 +736,10 @@ Assembly happens on **two planes** — and web is now fully automatic, zero conf
719
736
 
720
737
  #### First impressions after an npm install (perceive / guide / configure)
721
738
 
722
- - **Automatic**: install into a profile and boot once — the host plane is taken
723
- over by `mountCompactionEngine`, and the web plane by `autoWebCompaction`
724
- (default on) rewriting every stock preset at boot. **Zero manual setup.**
739
+ - **Semi-automatic (opt-in)**: DSH's own summarizer stays the default. Turn on
740
+ `gcReplacesCompaction` (host plane) and `autoWebCompaction` (web presets) in
741
+ the settings card, then boot once — `mountCompactionEngine` takes over and
742
+ the stock presets are rewritten. **No file editing after that.**
725
743
  - **Perceivable**: at boot the plugin writes an authoritative snapshot to
726
744
  `$DSH_HOME/engram/context-gc.status.json` (`host` plane mode + the per-preset
727
745
  `web` results + effective config). At any time run `npx dsh-engram status` or
@@ -730,8 +748,9 @@ Assembly happens on **two planes** — and web is now fully automatic, zero conf
730
748
  log lines confirm it too: `engram context-gc: compaction = Context GC …` and
731
749
  `engram web-provision: wired Context GC into preset …`.
732
750
  - **Configurable**: one settings card (memory-GC section) governs everything:
733
- `autoWebCompaction` (web auto-wiring on/off), `gcReplacesCompaction`
734
- (Context GC vs default summarizer), `gcNarrative` (narrative safety net).
751
+ `autoWebCompaction` (web auto-wiring, default off), `gcReplacesCompaction`
752
+ (Context GC vs host summarizer, default off), `gcNarrative` (narrative
753
+ safety net).
735
754
  Changes take effect after restarting `dsh web`, and the web rows are
736
755
  refreshed to match automatically.
737
756
 
@@ -748,7 +767,7 @@ means that preset was customized (or has no compaction group) and was left untou
748
767
  `dsh-compaction-basic unavailable` means the backend is missing and everything
749
768
  falls back to the default compressor.
750
769
 
751
- ### BoundaryPrune (subtask-boundary pruning, default OFF)
770
+ ### BoundaryPrune (subtask-boundary pruning, default ON + ledger veto)
752
771
 
753
772
  Context GC owns *what a compaction summary says*; BoundaryPrune owns *when a
754
773
  deterministic prune fires*: at todo-completion progress and goal terminal
@@ -783,10 +802,13 @@ later request. The two switches are independent.
783
802
  T2048+small-edges lands 2.4× the first-prune savings of the host default on
784
803
  the real corpus (10.14M vs 4.23M chars). Lowering the threshold is safe with
785
804
  `resultPack` on, which archives the removed middles.
786
- - Default **off**: `boundaryPrune: false`. When on, the log shows
805
+ - Default **on**: `boundaryPrune: true` (`false` is the opt-out). Adaptive gate
806
+ `boundaryPruneAdaptive: true` lets the Context Ledger stop further fires once
807
+ this workspace's own accounting is net negative (≥3 fires) — evidence can only
808
+ turn pruning off. The log shows
787
809
  `engram boundary-prune (todo @N reqs): pruned …`.
788
810
 
789
- ### ResultPack L2 (archive + `result_recall`, default OFF)
811
+ ### ResultPack L2 (archive + `result_recall`, default ON)
790
812
 
791
813
  BoundaryPrune makes pruning lossless: the removed middle of every pruned
792
814
  tool result is redacted (same rules as the memory write path) and archived to
@@ -824,6 +846,58 @@ Mechanisms must be provably activating, and dormant ones are off by default.
824
846
  the loop already measured from session logs (engram_recall queries → hits →
825
847
  engram_detail follows) so you can see injection→query→detail activation.
826
848
 
849
+ ### Context Ledger (net-savings accounting)
850
+
851
+ Turns "saves tokens" into a checkable number. Every model request is one ledger
852
+ tick: injected `[ENGRAM]`/`[RECALL]` and `[ESR]` blocks are charged per request
853
+ they survive; results removed by BoundaryPrune are credited per later request
854
+ they no longer replay; every prune pays one prefix-rewrite penalty
855
+ `(1 − ledgerCacheDiscount)` (the KV-cache break). `GET /api/dsh-engram/stats`
856
+ and `/overview` return `ledger`:
857
+
858
+ - `totals` — `requests / injectedChars / avoidedChars / rewriteCount /
859
+ rewriteChars / netChars / netTokens` with
860
+ `net = avoided − injected − rewriteChars × (1 − discount)`;
861
+ - `byWorkspace` / `byDay` — the same arithmetic per workspace and calendar day
862
+ (`byDay` newest first);
863
+ - `openSessions` — sessions still accumulating in memory (a restart loses at
864
+ most `flushEvery` requests).
865
+
866
+ The Telemetry tab renders it directly (net-token headline, saved / injected /
867
+ rewrite-penalty cards, a 14-day red/green net chart), and the memory overview
868
+ adds a "net context tokens" stat card. Durable rollup at
869
+ `~/.dsh/engram/ledger.json` (atomic write, daily buckets, delta flush); the hot
870
+ path is an integer add, zero I/O and zero model calls.
871
+ Defaults: `ledgerEnabled: true`, `ledgerCacheDiscount: 0.1`; off = no listeners,
872
+ no writes. **Honest scope**: tokens are `chars / 4` estimates, not provider
873
+ billing, and no quality delta is claimed; Context GC compaction savings are not
874
+ wired yet (the host does not expose before/after sizes), so the ledger only
875
+ counts what it can prove.
876
+
877
+ ### JIT push (v0.6-C)
878
+
879
+ Pull-only recall measured dormant (24 `engram_recall` / 8 `engram_detail` across
880
+ 112 real sessions), so v0.6 pushes on three deterministic events instead. One
881
+ line per event, at most 3 lines per session (`jitMaxLines`, `jitMaxLineChars`=220),
882
+ and silence when there is nothing relevant to point at:
883
+
884
+ | Event | Trigger | What is pushed |
885
+ |---|---|---|
886
+ | Error recurrence | the same `cmd:` signature fails a second time and a prior error/procedure memory exists | that memory's id + a one-line excerpt + `engram_detail` |
887
+ | File focus | the same file is edited twice in a row and a filePath/dir-anchored memory exists | preference first, then fact/decision |
888
+ | Goal completion | goal complete/block while active ESR tasks remain | the first two open tasks + `esr_status` |
889
+
890
+ - **Append-only**: a pushed line is never edited or withdrawn, so the prefix
891
+ changes once per push. Each push charges a rewrite
892
+ (`estimatePrefixChars + line length`) in the Context Ledger
893
+ (`rewriteCount` / `rewriteChars`), visible on the telemetry page.
894
+ - **Silent without a hit** — no memory/task to point at, no line.
895
+ - **Zero LLM, pure rules**; `jitEnabled:false` disables it, and the empty
896
+ `[JIT]` section costs nothing.
897
+ - **Not yet**: the resume brief (next-day continuation) and an explicit
898
+ remaining-requests ROI gate — today the 3-line budget plus the ledger are the
899
+ guardrails.
900
+
827
901
  ## Auto-capture policy
828
902
 
829
903
  Capture is deterministic and offline — it only sees tool *results*, never the
@@ -908,12 +982,13 @@ Defaults are token-conscious; override any key via the profile patch
908
982
  autoCapture: true # zero-LLM tool-result capture
909
983
  autoCapturePreferences: true # zero-LLM user-correction/preference capture (prefer/avoid/switch-to cues)
910
984
  preferenceCapturePerSession: 8 # per-session preference memory cap
911
- boundaryPrune: false # ResultPack L1: prune stale over-threshold tool results at todo/goal boundaries (default OFF)
985
+ boundaryPrune: true # ResultPack L1: prune stale over-threshold tool results at todo/goal boundaries (default ON)
986
+ boundaryPruneAdaptive: true # ledger veto: stop pruning this workspace once its net is negative (>=3 fires)
912
987
  boundaryPruneThresholdChars: 0 # threshold override (0 = host 8192; e.g. 2048 prunes earlier)
913
988
  boundaryPruneHeadChars: 0 # retained head under an override (0 = host 4096)
914
989
  boundaryPruneTailChars: 0 # retained tail under an override (0 = host 1024)
915
990
  boundaryPruneIdleCadence: 0 # request-count boundary for todo/goal-less sessions (0 = event-only)
916
- resultPack: false # ResultPack L2: archive pruned middles + result_recall paged recall (default OFF; needs L1 on)
991
+ resultPack: true # ResultPack L2: archive pruned middles + result_recall paged recall (default ON; needs L1 on)
917
992
  resultPackRetentionDays: 7 # archive retention days (mtime sweep on the GC cadence)
918
993
  sessionSearch: true # engram_recall may also FTS past sessions
919
994
  recallScope: workspace # recall scope: "workspace" (strict isolation, default) | "global" (opt-in cross-workspace recall with [W:<ws>] origin markers)
@@ -932,8 +1007,12 @@ Defaults are token-conscious; override any key via the profile patch
932
1007
  gcEnabled: true # scheduled memory GC
933
1008
  gcIntervalHours: 24 # sweep cadence
934
1009
  gcStableRetentionDays: 120 # stable tasks leave [ESR] after this
935
- gcReplacesCompaction: true # Context GC: take over DSH auto-compact (false = keep the default LLM summarizer)
1010
+ gcReplacesCompaction: false # Context GC: take over DSH auto-compact (default off = host summarizer; true = opt-in pointers)
936
1011
  gcNarrative: true # scoped LLM narrative for un-provenanced turns; false = fully mechanical (zero LLM)
1012
+ autoWebCompaction: false # rewrite stock web presets (default off; explicit opt-in, revert with the CLI)
1013
+ jitEnabled: true # JIT push: error recurrence / file focus / goal completion → one memory line
1014
+ ledgerEnabled: true # Context Ledger: injected cost / prune savings / rewrite penalty → net tokens
1015
+ ledgerCacheDiscount: 0.1 # cached-prefix price share; rewrites pay (1−discount)
937
1016
  engramIndexOrder: 40 # systemPrompt section order (before tools band)
938
1017
  esrOrder: 41
939
1018
  ```
@@ -941,7 +1020,7 @@ Defaults are token-conscious; override any key via the profile patch
941
1020
  ## Development
942
1021
 
943
1022
  ```sh
944
- npm test # 291 tests: core + web API + GC + usage-observability + Context GC + ESR triggers + BoundaryPrune + preference learning (node:test)
1023
+ npm test # 359 tests: core + web API + GC + usage-observability + Context GC + ESR triggers + BoundaryPrune + preference learning + Context Ledger + JIT push (node:test)
945
1024
  npm run eval # offline recall + structure benchmark (deterministic)
946
1025
  npm run build:client
947
1026
  ```
package/README.zh.md CHANGED
@@ -85,7 +85,7 @@
85
85
  - **记忆 GC(pi-esr 约束)** — 定时、机械、只归档的回收:TTL 过期记忆归档、超容量工作区淘汰低价值条目、
86
86
  stable 任务超保留窗离开 `[ESR]` 表面、悬空链接边清理。工作集(active 任务引用 / 任务记忆 / 已入索引命中)
87
87
  永不触碰;**不硬删任何东西**——归档条目保留 id、始终可重取。
88
- - **Context GC(自动 GC = 替代 DSH 自动 compact)** — 不是记忆面板 GC:接管 DSH 自带的上下文压缩
88
+ - **Context GC(自动 GC = 替代 DSH 自动 compact,opt-in)** — 不是记忆面板 GC:开启后接管 DSH 自带的上下文压缩
89
89
  (`compaction` 服务),把「有损 LLM 全量摘要」换成**机械驱逐 + 重取指针**——扫描被驱逐轮次里的
90
90
  engram/ESR 锚(`#记忆id`、`tsk_*`、`ent_*`、`file_path`),只留一行「细节在 `engram_detail` /
91
91
  `engram_recall` / `[ESR]` 里」;只有无锚轮次才走 scoped LLM 叙事兜底(`gcNarrative`,默认开、可关成纯机械)。
@@ -166,8 +166,8 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
166
166
 
167
167
  <!-- dsh-market 兼容信息块 —— 与 docs/RELEASE-NOTES.TEMPLATE.md 的市场 README 公约保持一致 -->
168
168
 
169
- - **DSH 版本**:`>=0.1.2-alpha.2 <0.3.0-0`(本版 `0.5.1`;已发布的 `0.3.7`、`0.4.0` 仍声明旧的 `0.1.x` 范围 `>=0.1.2-alpha.2 <0.2.0-0` —— 见下文矩阵)
170
- - **最后验证日期**:2026-10-19 —— 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)+ 数据导出/导入与替换/冲突可视化(issue #3),npm test 334/334 通过
169
+ - **DSH 版本**:`>=0.1.2-alpha.2 <0.3.0-0`(本版 `0.6.0`;已发布的 `0.3.7`、`0.4.0` 仍声明旧的 `0.1.x` 范围 `>=0.1.2-alpha.2 <0.2.0-0` —— 见下文矩阵)
170
+ - **最后验证日期**:2026-10-19 —— Context Ledger / 默认值纠偏 / JIT 推送,npm test 359/359 通过
171
171
  - **安装方式已验证**:`dsh plugin --profile web add dsh-engram`
172
172
 
173
173
  > **一句话结论**:所有**已真正发布**的版本(`0.2.0` … `0.3.6`)**只支持旧版
@@ -180,7 +180,8 @@ curl -s -X POST http://127.0.0.1:3080/api/dsh-engram/import \
180
180
  | `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` |
181
181
  | `0.3.7` / `0.4.0`(npm) | 已发布 | **新版 DSH,settings `installSection`** —— `>=0.1.2-alpha.2 <0.2.0-0` | `engines.dsh` + peers 同范围 |
182
182
  | `0.5.0`(npm) | 已发布 | **DSH `0.1.2-alpha.2` … `<0.3.0-0`** —— 设置卡迁移到 entry-id volatile Config(`0.1.7+`/`0.2.x`),`≤0.1.5` 自动回退 `installSection` | `engines.dsh` + peers `>=0.1.2-alpha.2 <0.3.0-0` |
183
- | `0.5.1`(本期 / this release) | 已发布 | 同 `0.5.0` —— 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)、数据导出/导入 GUI、替换/冲突可视化(issue #3) | 同 `0.5.0` |
183
+ | `0.5.1` | 已发布 | 同 `0.5.0` —— 会话扫描修复(`session.v<N>.jsonl.zstd`,issue #8)、数据导出/导入 GUI、替换/冲突可视化(issue #3) | 同 `0.5.0` |
184
+ | `0.6.0`(本期 / this release) | 已发布 | 同 `0.5.0` —— Context Ledger、BoundaryPrune/ResultPack 默认开 + 账本否决、JIT 推送、Context GC 改 opt-in | 同 `0.5.0` |
184
185
 
185
186
  ### API 族断裂点:`0.1.2-alpha.2`
186
187
 
@@ -207,7 +208,7 @@ HTTP 路由(`/api/dsh-engram/settings`,与记忆 viewer 同样的同源 fetc
207
208
  profile 条目 id(`engram`)为命名空间,不再需要任何“挂载”调用。`0.5.0`
208
209
  在 `installEngramSettings` 里对两代际做特性检测:
209
210
 
210
- - DSH `≥0.1.7` / `0.2.x` —— 27 个 GUI 旋钮即 `Config` 的 volatile 字段
211
+ - DSH `≥0.1.7` / `0.2.x` —— 31 个 GUI 旋钮即 `Config` 的 volatile 字段
211
212
  (`lib/settings.js` `makeEngineConfigSchema`,由 `lib/index.js` 导出为
212
213
  `Config`);卡片仍走同一 `/api/dsh-engram/settings` HTTP 路由读写条目 `engram`,
213
214
  `service.settings` 接缝在 `loader/volatile-update` 事件上把 `resolved`
@@ -220,13 +221,23 @@ profile 条目 id(`engram`)为命名空间,不再需要任何“挂载”
220
221
  所以:跑 ≤ `0.3.6` 的已发布版本时请把 DSH 保持在 `0.1.1-rc.2` 或更旧;升级
221
222
  DSH 到 `0.1.2-alpha.2` 之前,先升级插件到 `>=0.3.7`。
222
223
 
223
- ### 当前发布版本 —— `0.5.1`
224
+ ### 当前发布版本 —— `0.6.0`
224
225
 
225
- 在 `0.5.0` 之上的补丁版:会话扫描改为解析**版本化**日志名
226
- (`session.v3/v4.jsonl.zstd`)而不硬编码 `session.jsonl.zstd`——在当前 DSH 上
227
- 这个错位让 `/stats`、`/toolstats`、BoundaryPrune 的偿还历史、CJK 会话搜索全部
228
- **静默返回 0**(issue #8)。记忆查看器同时补上数据导出/导入与替换/冲突可视化
229
- (issue #3)。
226
+ 从「记忆插件」转向**上下文投资组合**:先把省 token 变成可审计的账本,再用账本
227
+ 纠正默认值、否决不划算的剪枝,并按确定性事件把记忆推给模型。
228
+
229
+ - **Context Ledger**:每会话/工作区/天记录 `net = 节省 − 注入 − 重写罚金`,
230
+ `/stats` 与 `/overview` 返回,GUI 遥测页有面板;落盘 `~/.dsh/engram/ledger.json`。
231
+ - **默认值纠偏**:`boundaryPrune` / `resultPack` 默认开(剪枝由归档兜底),
232
+ `gcReplacesCompaction` / `autoWebCompaction` 改 opt-in(不再主动改写宿主预设)。
233
+ - **账本否决**:`boundaryPruneAdaptive` 在某 workspace 净收益为负且 ≥3 次剪枝时
234
+ 自动停剪(fail-open)。
235
+ - **JIT 推送**:错误复发 / 文件焦点 / goal 完成三类事件,append-only、每会话 ≤3 行、
236
+ 无命中沉默;`[JIT]` section + `jitEnabled`。
237
+ - **迁移**:升级后重启 `dsh web`;若仍要 Context GC 接管,显式打开
238
+ `gcReplacesCompaction:true`(web 还需 `autoWebCompaction:true`)。
239
+ - `0.5.1` 的会话日志版本化修复(`session.v<N>.jsonl.zstd`,issue #8)与数据备份
240
+ GUI(issue #3)保持不变。
230
241
 
231
242
  已发布的 `0.4.0` 声明 `>=0.1.2-alpha.2 <0.2.0-0`——这个范围被 semver 门槛在
232
243
  **所有 DSH `0.2.0-0` 预发布**上拒绝(`<0.2.0-0` 排除 `0.2.0-rc.x`),且其宿主设置端
@@ -244,13 +255,13 @@ Config** 模型(并带特性检测的旧代际回退)。
244
255
 
245
256
  `0.1.2-alpha.2` 是这组 API 的最低版本:宿主设置注册按代际**特性检测**——DSH `≤0.1.5`
246
257
  走 `settings.installSection` 注册 `dsh-engram` 命名空间;`≥0.1.7` / `0.2.x` 走插件
247
- `Config`(27 个 GUI 旋钮标记为 `.volatile()`,条目 id `engram`)。浏览器设置卡在两代际
258
+ `Config`(31 个 GUI 旋钮标记为 `.volatile()`,条目 id `engram`)。浏览器设置卡在两代际
248
259
  都经插件自有 HTTP 路由(`/api/dsh-engram/settings`,见上文 Route B)读写。完整回归
249
260
  (npm test 329/329,含新增的 volatile-Config 模型 settings-v2 覆盖)在 DSH
250
261
  `0.2.0-rc.2` 依赖集(cordis 4.0.4 + schemastery 3.18.4 + cosmokit,与全局 DSH CLI 同版本)
251
262
  上通过;`.volatile()`/loader 热提交路径与 describe 侧投影已针对真实 `dsh-settings
252
263
  0.2.0-rc.2` 工具函数验证;并在隔离的 `dsh 0.2.0-rc.2` profile 上完成了**真机启动烟测并
253
- 通过**:插件启动无版本范围拒绝、设置卡表单以条目 `engram` 对外提供(全部 27 个 GUI
264
+ 通过**:插件启动无版本范围拒绝、设置卡表单以条目 `engram` 对外提供(全部 31 个 GUI
254
265
  旋钮,含 `trustedHosts`)、`/api/dsh-engram/settings` 的 GET → PUT(mutate ops,
255
266
  revision 0→1)→ unset 往返落盘并读回。旧的 `0.1.0-rc.7` 与 `0.1.1` API 图不在
256
267
  本发布版本的兼容范围内。
@@ -340,6 +351,7 @@ node scripts/setup-links.mjs # 把 @deepseek-ai 工作区包软链进 node_m
340
351
  纯 SVG 实现(无图表库,保持 bundle 纯净)。
341
352
  以及一个 **遥测仪表盘** 页签:把 /stats 的真实调用累计(工作区 × 天滚动)画成纯 SVG 仪表盘——三枚大圆环
342
353
  直读 **ESR 主动性**(与 escalate 阈值 0.34 比对,偏低标橙并提示)、**召回命中率**、**detail 转化**,
354
+ 外加一块 **Context Ledger** 面板(净 token、节省/注入/重写罚金三卡、近 14 天净收益红绿柱),
343
355
  五张小指标卡(累计调用 / esr / 记忆 / 平均命中每查询 / 失败),近 14 天 mem-vs-esr 堆叠柱状图 +
344
356
  工具调用 Top 8 横向条形图(mem 蓝 / esr 紫,与全文配色一致),20s 自动刷新,样本不足(<10 次)自动标注。
345
357
  以及 **详情侧栏**(主从布局):任务 / 记忆 / 节点 / 关系行都可一键在右侧打开详情卡——任务含
@@ -356,7 +368,7 @@ node scripts/setup-links.mjs # 把 @deepseek-ai 工作区包软链进 node_m
356
368
  面板同时列出各工具调用计数与最近 14 天逐日滚动。这些是真实会话的真实数字——想提升 ESR 主动性,
357
369
  就观察面板上 esr 占比并调整注入提示。
358
370
  - **设置 → 插件 → 插件配置 → dsh-engram** — 与内置「终端 / Agent 循环 / 网页搜索」同款的
359
- **默认折叠卡片**:标题 + 一行描述 + 箭头,点击展开/收起;展开后 12 个设置项按
371
+ **默认折叠卡片**:标题 + 一行描述 + 箭头,点击展开/收起;展开后常用 11 项 + 高级分组按
360
372
  「捕获与检索 / 索引 / 生命周期与 GC / 安全」四个分组展示。改动对新建会话即时生效
361
373
  (已冻结的块保持稳定);支持放弃修改 / 保存,有未保存改动时标题上出现「未保存」徽标。
362
374
  卡片通过连接自身的 settings RPC 直连命名空间(不走 isLoopback 门控的 scope),因此即使
@@ -380,7 +392,7 @@ npm run build:client
380
392
  ## 测试与评测
381
393
 
382
394
  ```sh
383
- npm test # 311 项单元测试(含 usage 滚动 / /stats 路由 / Context GC / ESR 触发 / BoundaryPrune)
395
+ npm test # 359 项单元测试(含 usage 滚动 / /stats 路由 / Context GC / ESR 触发 / BoundaryPrune / Context Ledger / JIT 推送)
384
396
  npm run eval # 离线召回 + 结构基准(确定性语料,跑真实 store/recall 路径)
385
397
  ```
386
398
 
@@ -438,7 +450,7 @@ GC 永不触碰工作集:active 任务 `memory_refs` 引用的记忆、task
438
450
  (`hits >= promoteHits`)。用 `esr_gc` + `dry_run: true` 先预览。**不硬删**——报告的末尾为所有
439
451
  归档项附上重取指针,归档可恢复、不是丢失。
440
452
 
441
- ### Context GC(替换 DSH 自动 compact)
453
+ ### Context GC(替换 DSH 自动 compact,opt-in)
442
454
 
443
455
  DSH 默认的上下文压缩是**有损 LLM 全量摘要**(`compaction-basic`):被驱逐的历史被压缩成散文,
444
456
  不可查询、摘要本身还烧 token。dsh-engram 的自动 GC 替代它:`ContextGcEngine extends
@@ -453,7 +465,9 @@ BasicCompactionEngine` 只重写唯一的 `summarize()` 钩子,把摘要正文
453
465
 
454
466
  触发时机完全跟随 DSH 现有 compact(step pressure / context-overflow / `/compact`);锁、回放校验、
455
467
  tool-call/result 配对、token 定价全部复用基本引擎。任何错误 → 回退默认压缩,**永不破坏 compact**。
456
- 装配即注册 `compaction` 服务,卸载/reload engram 自动还回默认引擎。
468
+ 装配即注册 `compaction` 服务,卸载/reload engram 自动还回默认引擎。**自 v0.6 起默认不接管**:
469
+ 默认挂裸 `BasicCompactionEngine`(宿主 LLM 摘要),要换成指针摘要需显式打开
470
+ `gcReplacesCompaction`(web 预设还需 `autoWebCompaction`)。
457
471
 
458
472
  > **收缩闸门(shrink gate)**:harness 拒绝任何不比被驱逐片段更小的 checkpoint(`summary is not
459
473
  > smaller than the shadowed content`),否则压缩回滚、上下文永不缩小、最终触发模型侧溢出。Context GC
@@ -464,24 +478,23 @@ tool-call/result 配对、token 定价全部复用基本引擎。任何错误
464
478
 
465
479
  #### 启用入口(这一节就是答案:该功能开在哪)
466
480
 
467
- Context GC 的装配在**两个平面**——web 现在**全部自动**,零配置:
481
+ Context GC 的装配在**两个平面**,但两个开关都是 opt-in(默认不接管):
468
482
 
469
483
  - **host 平面(headless / TUI / base 型 profile)——开箱即用,无需任何配置**:
470
484
  主插件 `lib/index.js` 在 `ctx.effect` 里直接
471
485
  `mountCompactionEngine(ctx, resolved, { readWorkspace }) → new Engine()` 注册 `compaction` 服务;
472
486
  配合插件 patch 禁用基座 `compaction-basic` 行,engram 即为该平面唯一 provider。
473
- - `gcReplacesCompaction: true`(**默认**)→ `ContextGcEngine`(机械驱逐 + 重取指针);
474
- - `gcReplacesCompaction: false` → 挂裸 `BasicCompactionEngine`(退化为 DSH 默认 LLM 摘要),
475
- **`compaction` 服务永不缺席**。
476
- - 关闭/开启:profile patch 里给 engram 行加 `config: { gcReplacesCompaction: false }`,或设置卡
477
- 「记忆 GC」区的 `gcNarrative` 关掉叙事。
478
- - **preset 平面(web profile)——全自动,零配置,覆盖全部预设**:web 面把 compaction 放在 agent
487
+ - `gcReplacesCompaction: false`(**默认**)→ 挂裸 `BasicCompactionEngine`(DSH 默认 LLM 摘要),
488
+ **`compaction` 服务永不缺席**;
489
+ - `gcReplacesCompaction: true` → `ContextGcEngine`(机械驱逐 + 重取指针,opt-in);
490
+ - 开启:profile patch 里给 engram 行加 `config: { gcReplacesCompaction: true }`,或在设置卡打开。
491
+ - **preset 平面(web profile)——显式开启后覆盖全部 stock 预设**:web 面把 compaction 放在 agent
479
492
  preset 的 isolate realm(shipped 预设自挂 `compaction-basic`),host 条目进不去、profile patch 也
480
493
  够不到预设文件(`mountPreset` 的 Include 不带 patches——已对源码核实)。所以插件在启动时
481
494
  (`agentPresets` 服务就绪后)**自动改写每个仍处于出厂 stock 布局的预设**——默认预设 + 整张 roster
482
495
  (shipped 根 + `~/.dsh/.agent-presets` 用户根,`standard`/`code`/`cordis` 和你自己的预设都覆盖):
483
496
  把 `compaction` 组里的 `compaction-basic` 行换成 `dsh-engram/compaction`。这一自动装配是:
484
- - **默认开**(`autoWebCompaction: true`,设置卡「记忆 GC」可关),幂等——已替换则跳过;
497
+ - **默认关(opt-in)**(`autoWebCompaction: false`;设置卡「记忆 GC」可开),开启后幂等——已替换则跳过;
485
498
  **关掉的含义**:只是在之后的启动时不再自动接管(headless/TUI/base 的 host 平面不受影响)——
486
499
  它**不会**还原之前已接管的预设,要还原用 `npm run web-compaction:revert`;
487
500
  - **逐预设判定,只碰 stock 布局**——用户自定义过 compaction 组的预设,以及根本没有 compaction 组的
@@ -527,15 +540,16 @@ Context GC 的装配在**两个平面**——web 现在**全部自动**,零配
527
540
 
528
541
  #### 从 npm 安装后的第一印象(可感知 / 易引导 / 易配置)
529
542
 
530
- - **自动**:装进 profile 后启动一次即可——host 平面由 `mountCompactionEngine` 直接接管;web 平面
531
- 由 `autoWebCompaction`(默认开)在启动时自动改写全部 stock 预设。**无需任何手动配置。**
543
+ - **半自动(opt-in)**:默认保留 DSH 自己的摘要;在设置卡打开 `gcReplacesCompaction`(host 平面)与
544
+ `autoWebCompaction`(web 预设)后,启动一次即由 `mountCompactionEngine` 接管、并把全部 stock
545
+ 预设改写成 `dsh-engram/compaction`。开启后无需再手动改文件。
532
546
  - **可感知**:插件启动时把权威状态写到 `$DSH_HOME/engram/context-gc.status.json`
533
547
  (`host` 平面模式 + `web` 平面每个预设的接管结果 + 生效配置)。随时
534
548
  `npx dsh-engram status` 或 `npx dsh-engram doctor` 查看;web 记忆看板(ESR 任务看板)头部也有
535
549
  一枚状态徽标("Context GC·主机 ·N 预设",来自 overview API)。启动日志也有两行关键确认:
536
550
  `engram context-gc: compaction = Context GC …` 与 `engram web-provision: wired Context GC into preset …`。
537
- - **易配置**:一个设置卡(「记忆 GC」区)管全部:`autoWebCompaction`(web 自动接管开关)、
538
- `gcReplacesCompaction`(用 Context GC 还是回退默认摘要)、`gcNarrative`(叙事兜底开关)——
551
+ - **易配置**:一个设置卡(「记忆 GC」区)管全部:`autoWebCompaction`(web 自动接管,默认关)、
552
+ `gcReplacesCompaction`(用 Context GC 还是宿主摘要,默认关)、`gcNarrative`(叙事兜底开关)——
539
553
  改动后 `dsh web` 重启生效,web 预设行会被自动刷新成新配置。
540
554
 
541
555
  **验证是否生效**:`dsh web`(或对应 profile)重启后——
@@ -548,7 +562,7 @@ Context GC 的装配在**两个平面**——web 现在**全部自动**,零配
548
562
  - (若见 `… keeping the existing compaction service` 说明同域已有 provider、未替换成功;`web-provision … skipped (custom)`
549
563
  说明某预设是自定义/无 compaction 布局、未被触碰;`dsh-compaction-basic unavailable` 说明环境缺依赖,自动回退默认压缩。)
550
564
 
551
- ### BoundaryPrune(子任务边界剪枝,默认关)
565
+ ### BoundaryPrune(子任务边界剪枝,默认开 + 账本否决)
552
566
 
553
567
  Context GC 管「压缩的摘要内容」,BoundaryPrune 管「确定性剪枝的触发时机」——
554
568
  在 todo 完成推进 / goal 终态这些子任务边界,主动调用 DSH 自带的
@@ -573,10 +587,13 @@ Context GC 管「压缩的摘要内容」,BoundaryPrune 管「确定性剪枝
573
587
  并配更小保留边缘(`boundaryPruneHeadChars`/`boundaryPruneTailChars`)可更早
574
588
  开剪——T2048+小边缘在真实语料上首剪节省是宿主默认的 2.4 倍(10.14M vs 4.23M
575
589
  字符)。降阈值在 `resultPack` 开启时安全(被剪中段有归档兜底)。
576
- - 默认**关**:`boundaryPrune: false`。开启后启动日志出现
577
- `engram boundary-prune (todo @N reqs): pruned …`。
590
+ - 默认**开**:`boundaryPrune: true`;`false` 是 opt-out。自适应闸门
591
+ (`boundaryPruneAdaptive: true`,默认开)让 Context Ledger 在该 workspace 真实剪枝后
592
+ 净收益为负(≥3 次)时自动停剪——证据只能关、不会自行开。启动日志出现
593
+ `engram boundary-prune (todo @N reqs): pruned …`;被账本否决时出现
594
+ `engram boundary-prune: ledger gate holds for …`。
578
595
 
579
- ### ResultPack L2(结果归档 + `result_recall`,默认关)
596
+ ### ResultPack L2(结果归档 + `result_recall`,默认开)
580
597
 
581
598
  让 BoundaryPrune 剪枝**无损**:每个被剪工具结果的被移除中段,按与记忆写入
582
599
  相同的规则脱敏后归档到 `~/.dsh/engram/resultpack/<callId>.json`,并在剪枝
@@ -591,8 +608,9 @@ Context GC 管「压缩的摘要内容」,BoundaryPrune 管「确定性剪枝
591
608
  [ENGRAM] 索引);保留期 `resultPackRetentionDays`(默认 7 天)按 mtime 清理,
592
609
  搭 GC 同一节奏。
593
610
  - **自我禁用**:只开 L2 不开 L1 将无内容可归档,故不注册工具并打一条警告。
594
- - 默认**关**:`resultPack: false`(须与 `boundaryPrune: true` 同开)。开启后
595
- 启动日志出现 `engram result-pack enabled: result_recall(callId, offset) registered`。
611
+ - 默认**开**:`resultPack: true`(与默认开启的 L1 配对,保证默认剪枝无损);
612
+ 关掉后 L1 仍可工作,但被剪中段不可召回。开启时启动日志出现
613
+ `engram result-pack enabled: result_recall(callId, offset) registered`。
596
614
 
597
615
  ### 激活遥测(SoL-Pi M15/C13)
598
616
 
@@ -607,6 +625,45 @@ overview)现在返回 `activation: { boundaryPrune, recall }`:
607
625
  (engram_recall 查询 → 命中 → engram_detail 下钻)闭合,可看到
608
626
  注入→查询→下钻的完整激活。
609
627
 
628
+ ### Context Ledger(净收益账本)
629
+
630
+ 把「省 token」变成可核对的账。每个模型请求记一笔:注入的 `[ENGRAM]`/`[RECALL]`
631
+ 与 `[ESR]` 块按存活请求数计入成本;被 BoundaryPrune 剪掉的结果按后续请求数计入
632
+ 节省;每次剪枝按 `(1 − ledgerCacheDiscount)` 计一次前缀重写罚金(KV 缓存击穿)。
633
+ `GET /api/dsh-engram/stats` 与 `/overview` 返回 `ledger`:
634
+
635
+ - `totals` — `requests / injectedChars / avoidedChars / rewriteCount / rewriteChars /
636
+ netChars / netTokens`(`net = avoided − injected − rewriteChars × (1 − discount)`);
637
+ - `byWorkspace` / `byDay` — 按工作区、按天的同一口径,`byDay` 倒序;
638
+ - `openSessions` — 仍在累计、尚未落盘的会话数(进程重启最多丢 `flushEvery` 个请求的账)。
639
+
640
+ GUI「遥测」页签直接画这块账本(净 token 大数字 + 节省/注入/重写罚金三卡 + 近 14 天红绿柱),
641
+ 记忆概览页也多一张「上下文净 token」卡。落盘在 `~/.dsh/engram/ledger.json`(原子写、按天滚动、
642
+ delta 落盘),进程内只做整数累加,热路径零 I/O、零模型调用。默认 `ledgerEnabled: true`、
643
+ `ledgerCacheDiscount: 0.1`;关掉 = 不挂监听、不写文件。**口径诚实**:token 按
644
+ `chars / 4` 估算,不含 provider 实际计费与模型质量差;Context GC 的压缩节省暂未
645
+ 接入(宿主未暴露前后大小),因此账本只记「可证明」的部分。
646
+
647
+ ### JIT 推送(v0.6-C)
648
+
649
+ 拉取式召回被实证休眠(112 会话仅 24 次 `engram_recall`、8 次 `engram_detail`),所以
650
+ v0.6 增加按事件**推送**:三个确定性事件,命中才推、每次一行、每会话最多 3 行(`jitMaxLines`,
651
+ 每行 `jitMaxLineChars`=220):
652
+
653
+ | 事件 | 触发条件 | 推什么 |
654
+ |---|---|---|
655
+ | 错误复发 | 同一 `cmd:` 签名第二次失败,且已有 error/procedure 记忆 | 该记忆的 id + 一行摘要 + `engram_detail` 下钻 |
656
+ | 文件焦点 | 同一文件连续第二次编辑,且有 filePath/dir 锚定记忆 | 优先 preference,其次 fact/decision |
657
+ | 目标完成 | goal complete/block 且有 active ESR 任务 | 前两个未闭环任务 + `esr_status` 提示 |
658
+
659
+ - **append-only**:推送行一旦出现就不再改写/撤回,前缀每次推送只变一次;
660
+ 每次推送按 `estimatePrefixChars + 行长度` 记一次重写罚金(Context Ledger 的
661
+ `rewriteCount` / `rewriteChars`),净收益可在遥测页直接看。
662
+ - **无命中沉默**:没有可指向的记忆/任务就不推——宁可不推,也不灌噪声。
663
+ - **零 LLM、纯规则**;`jitEnabled:false` 一键关,`[JIT]` section 为空时不占任何 token。
664
+ - **尚未做**:resume brief(隔天继续)与「预计剩余请求不足时跳过推送」的显式 ROI 门——
665
+ 当前靠 3 行硬预算 + 账本观测。
666
+
610
667
  ## 自动捕获策略
611
668
 
612
669
  捕获是确定性、离线的——只看到工具*结果*,从不看对话本身。什么会被记录成一条记忆:
@@ -679,12 +736,13 @@ promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-pr
679
736
  autoCapture: true # 零 LLM 工具结果捕获
680
737
  autoCapturePreferences: true # 零 LLM 用户纠正/偏好捕获(prefer/avoid/switch-to 句式)
681
738
  preferenceCapturePerSession: 8 # 每会话偏好记忆条数上限
682
- boundaryPrune: false # ResultPack L1:todo/goal 边界剪掉超阈值旧工具结果(默认关)
739
+ boundaryPrune: true # ResultPack L1:todo/goal 边界剪掉超阈值旧工具结果(默认开)
740
+ boundaryPruneAdaptive: true # 账本否决:该 workspace 净收益为负(≥3 次剪枝)时自动停剪
683
741
  boundaryPruneThresholdChars: 0 # 阈值覆盖(0=宿主 8192;如 2048 可更早开剪)
684
742
  boundaryPruneHeadChars: 0 # 覆盖时的保留头部(0=宿主 4096)
685
743
  boundaryPruneTailChars: 0 # 覆盖时的保留尾部(0=宿主 1024)
686
744
  boundaryPruneIdleCadence: 0 # 空闲步数边界(0=仅事件边界;覆盖无 todo/goal 会话)
687
- resultPack: false # ResultPack L2:剪掉的中段脱敏归档 + result_recall 按页召回(默认关,须与 L1 同开)
745
+ resultPack: true # ResultPack L2:剪掉的中段脱敏归档 + result_recall 按页召回(默认开,须与 L1 同开)
688
746
  resultPackRetentionDays: 7 # 归档保留天数(mtime 清理,走 GC 节奏)
689
747
  sessionSearch: true # engram_recall 也可对历史会话 FTS
690
748
  recallScope: workspace # 召回范围:workspace(严格工作区隔离,默认)| global(可选跨工作区召回,带 [W:<ws>] 来源标记)
@@ -702,9 +760,12 @@ promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-pr
702
760
  gcEnabled: true # 定时记忆 GC
703
761
  gcIntervalHours: 24 # 回收节奏
704
762
  gcStableRetentionDays: 120 # 超过此天数的 stable 任务离开 [ESR]
705
- gcReplacesCompaction: true # Context GC:接管 DSH 自动 compact(false=回退默认 LLM 压缩)
763
+ gcReplacesCompaction: false # Context GC:接管 DSH 自动 compact(默认关=宿主 LLM 压缩;true=opt-in 指针摘要)
706
764
  gcNarrative: true # 无锚轮次走 scoped LLM 叙事;false=纯机械(零 LLM)
707
- boundaryPrune: false # BoundaryPrune:todo/goal 边界主动剪超阈值旧工具结果(默认关)
765
+ jitEnabled: true # JIT 推送:错误复发/文件焦点/goal 完成时推一行已有记忆
766
+ ledgerEnabled: true # Context Ledger:注入成本/剪枝节省/前缀重写罚金 → 净 token
767
+ ledgerCacheDiscount: 0.1 # 缓存折价:重写按 (1−0.1) 计罚金;0.1 = 命中只付 10%
768
+ autoWebCompaction: false # web 预设自动改写(默认关;开启需显式确认,可用 revert 还原)
708
769
  boundaryPruneMinRemaining: 50 # 偿还门:预计剩余请求数低于此值不剪
709
770
  engramIndexOrder: 40 # systemPrompt section 顺序(位于 tools 段之前)
710
771
  esrOrder: 41
@@ -713,7 +774,7 @@ promote: 2 pending todo(s) vs 1 ESR task(s) — esr_task(name="…") #suggest-pr
713
774
  ## 开发
714
775
 
715
776
  ```sh
716
- npm test # 152 个测试:核心 + Web API + GC + Context GC + ESR 触发(node:test)
777
+ npm test # 359 个测试:核心 + Web API + GC + Context GC + ESR 触发 + Context Ledger + JIT(node:test)
717
778
  npm run build:client
718
779
  ```
719
780