@yolk_vat-y/dsh-project-memory 0.5.11 → 0.5.13

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/CHANGELOG.md CHANGED
@@ -1,4 +1,228 @@
1
- # Changelog
1
+ ## 0.5.13 (2026-09-28)
2
+
3
+ 适配 DSH 0.2.x。`npm test` **539 → 540 项 / 28 个文件**。
4
+
5
+ ### 修复:0.2.x 上插件被整个停用
6
+
7
+ DSH 在 profile 组合阶段用运行版本核对插件的 `@deepseek-ai/dsh*` peer 范围,对不上就把该行
8
+ `disabled = true`。本插件三段范围的上限都是 `<0.2.0-0`,`0.2.0-rc.1` 落在范围外。后果不是
9
+ "某个功能失效":`cordis.patch.yml` 插入层从未进入组合树,工具 / 命令 / 面板 / 自动注入全部
10
+ 消失,插件管理器也拒绝安装或启用。
11
+
12
+ - `@deepseek-ai/dsh-llm` / `@deepseek-ai/dsh-tools` 的 peer 范围追加 `|| >=0.2.0-rc.1 <0.3.0-0`。
13
+ - devDependencies 从 `0.1.5-rc.1` 升到 `0.2.0-rc.1`(`@deepseek-ai/cordis` → `^4.0.4`),
14
+ 类型检查与测试对齐运行版本。
15
+ - 验证:DSH 自身的 `evaluatePluginCompatibility()` 判定为兼容;隔离 `DSH_HOME` 下真实挂载,
16
+ 12/12 个工具注册进真实 ToolRuntime。
17
+
18
+ ### 修复:`BlockAssembler.message(source)` 在 0.2.0 变为必填
19
+
20
+ 签名由 `message(source?)` 收紧为 `message(source)`。`chatText()` 原先无参调用:运行时**不抛错**
21
+ (展开 `undefined` 合法),所以测试全绿、故障静默,但组装出的 assistant 消息丢掉了
22
+ `provider`/`model` 归属。改为传入已解析出的路由,并在 `test/llm-route.test.mjs` 补一项断言
23
+ (曾用无参调用复现为红灯)。
24
+
25
+ ### 构建:client 产物不再每次构建都变
26
+
27
+ `tsdown.config.ts` 里 CSS 模块的类名映射直接遍历 lightningcss 的 `exports`,而它由 Rust
28
+ `HashMap` 支撑,hasher 每进程随机播种 —— 同一份 CSS 每次构建的键序都不同。类名与哈希本身稳定,
29
+ 键序在运行时也只按键取值、不可观测,但 `client/client.js` 是提交进仓库的产物,后果是每次
30
+ `npm run build:client` 都产生纯键序 diff,真实改动淹没在噪音里。改为按键排序输出后,连续三次
31
+ 构建产物逐字节一致(本版 `client/client.js` 因此有一次性的键序重排,无语义变化)。
32
+
33
+ ### 其余宿主契约
34
+
35
+ 逐包比对 0.1.5 / 0.1.7 → 0.2.0 的公共 API,插件用到的部分(`defineTool`、`ToolRunContext`、
36
+ `agent/pre-step`、`session/event`、`fs/observed`、`commands.register`、client 槽位与
37
+ `inputTriggers`、`sessions.list` 快照形状等)均为纯增量,无需改代码。本版唯一需要改源码的
38
+ 破坏点就是上面那条 `BlockAssembler.message`。
39
+
40
+ ## 0.5.12 (2026-09-26)
41
+
42
+ 本版五个修复 + 一个新增,主题是记忆的驻留、索引归属,以及两处让 TS 增强层失效 / 失真的缺陷。`npm test` **476 → 539 项 / 26 → 28 个文件**。
43
+
44
+ ### 修复:TS 增强队列的 TDZ 会打死宿主(`dsh: fatal load failure`)
45
+
46
+ ```
47
+ dsh: fatal load failure: ReferenceError: Cannot access 'p' before initialization
48
+ at enqueueEnhance (enhancer.js) → onFileChanged → WatchManager.pollRoot
49
+ ```
50
+
51
+ `enqueueEnhance()` 先启动任务、后登记队列条目,并在 `finally` 里用
52
+ `enhanceQueue.findIndex(q => q.promise === p)` 反查自己。`deepParseWithTS()` 是同步的,文件
53
+ 产不出增强符号时任务体走不到 `await`,`finally` 就在同步阶段执行——此时 `const p` 尚未完成
54
+ 初始化。队列为空时 `findIndex` 回调不被调用,问题不显形;队列非空时读到 TDZ 的 `p`,抛
55
+ ReferenceError。watch 轮询对每个改动的 `.js` 都在同一个同步循环里调 `onFileChanged`,且丢弃了
56
+ 返回的 promise,未处理的 rejection 被宿主当成致命错误。
57
+
58
+ - 队列条目改为先入队,`finally` 按对象身份摘除自己。
59
+ - 三个 fire-and-forget 入口各挂 catch。
60
+ - 顺带修掉同步完成的任务把一条已 resolve 的僵尸条目永久留在队列里的问题:同一路径同内容的
61
+ 后续调用会命中它,导致再也不增强。
62
+ - 测试:新增 `test/enhancer.test.mjs`(6 项),`src/enhancer.js` 此前无测试覆盖。
63
+
64
+ ### 修复:TS 增强层对 `.js` 文件从未生效(不分平台)
65
+
66
+ `deepParseWithTS()` 调 `ts.createProgram([filePath], {}, host)`,`options` 是空的。
67
+ TypeScript 在**调用 host 之前**就有一道扩展名闸门:`getSourceFileFromReferenceWorker` 先比对
68
+ `getSupportedExtensions(options)`,`.js/.jsx/.mjs/.cjs` 不在其中就直接返回——host 根本不会被问。
69
+
70
+ 而插件的 `isTypeScriptFile()` 明确把这四种扩展名列为可增强对象,watch / 懒索引 / `index_repo`
71
+ 三条入口也照常喂进去。**结果是 TS 增强层(README 承诺的"推导返回类型、解析泛型、抽取接口")
72
+ 对绝大多数真实文件一直没有产出,且没有任何日志。**
73
+
74
+ - `createProgram` 改用 `{ allowJs: true, noLib: true }`。`.js` / `.jsx` / `.mjs` / `.cjs` 从 0 个符号
75
+ 恢复到正常产出(`isTypeScriptFile()` 认的 8 种扩展名逐一断言),`.ts` 行为不变。
76
+ - **顺带修掉 L2 对无注解 JS 的文本损伤。** `applyEnhancedSymbols()` 是**就地重写** L1 条目的
77
+ `text`,而旧实现把参数一律渲染成 `${name}: ${type}`:本仓库 51 个 `.js/.mjs` 实测 334 条条目被
78
+ 重写,86% 的参数成了 `: any`,52 个带默认值的条目里 36 个把默认值丢了——
79
+
80
+ ```
81
+ chunkText(text, chunkChars = 3000, maxChunks = 40) — src/chunker.js:1 ← L1
82
+ chunkText(text: any, chunkChars: any, maxChunks: any): any -- src/chunker.js:1 ← 旧 L2
83
+ ```
84
+
85
+ 现在参数用**源码文本**(`= 默认值` / `?` / `...rest` / 解构模式都保住),推导出的返回类型只在
86
+ 有信息量时才追加(`any` / `unknown` / `{}` 不写)。改后同一份语料:默认值 0 丢失,被重写的条目
87
+ 里 51% 的文本与 L1 逐字相同(升级不再是损伤),其余 49% 多出一个真实返回类型(`: boolean` /
88
+ `: void` / `: () => void` / 对象形状)。
89
+ - `.cjs` 的惯用写法 `module.exports.foo = function () {}` 此前产不出符号:它的父节点是
90
+ `BinaryExpression`,不在函数表达式的名字白名单里——而 `isTypeScriptFile()` 明确把 `.cjs` 列为
91
+ 可增强对象。现在按 `module.exports.foo` / `exports.foo` / 字符串下标取名字。
92
+ - 增强条目的一行声明改用与 L1 `buildSymbol()` 相同的 ` — ` 分隔(旧为 ` -- `):L2 重写的正是
93
+ L1 的文本,两种分隔符会让同一个文件里的条目混排两种格式。
94
+ - **测试同步加固**:`test/enhancer.test.mjs` 原先的断言是 `existsSync(store.dir)`——store 目录
95
+ 只要有任何一次 commit 就会存在,前面几个用例已经把它建好,这条因此永远为真;它验的是
96
+ "跑过了"而不是"处理了"。换成 `store.entries[rel].some(e => e.enhanced)`,并补 `.js` 与
97
+ `.mjs` 的直接断言。固定 `setTimeout` 也改成轮询到条件成立,慢 CI 上不再靠运气。新增扩展名
98
+ 覆盖面、文本保真、CJS 断言与静默契约四组(共 13 项;前三组的 11 项在旧代码上为红,静默契约
99
+ 那 2 项锁的是"不加日志"这个决定,新旧都绿——它防的是将来有人把告警加回来)。
100
+
101
+ ### 修复:TS 增强在 Windows 上因路径写法不同而整层失效
102
+
103
+ compiler host 用 `fileName === filePath` 严格相等来认自己的文件。TypeScript 内部一律走
104
+ `normalizePath`(反斜杠转正斜杠、消解 `.`/`..`、绝对化),而 `path.join` 给出的是平台原生形态。
105
+ Windows 上两边字符串不同 → `getSourceFile` 返回 undefined → `deepParseWithTS` 返回 `[]`。
106
+ POSIX 上路径本就是正斜杠,因此从不现形。
107
+
108
+ 2026-09-26 的 Windows CI 就是这样暴露的(唯一失败断言是 enhancer 的「增强结果落进 store」)。
109
+
110
+ - 路径比较改用 `ts.normalizePath`(带 `typeof` 兜底:它在运行时导出但 `typescript.d.ts` 里
111
+ 没有声明,而 peer 范围覆盖 TS 5/6/7)。
112
+ - `useCaseSensitiveFileNames` 不再硬编码 `true`,改为跟随 `ts.sys`——TS 在 macOS 上是探测真实
113
+ 文件系统而非写死 `false`,跟随它才是正确语义。
114
+ - 预建唯一的 `SourceFile` 并按**对象身份**确认 TS 收下了它,不再用 `program.getSourceFile(filePath)`
115
+ 回查(那一步内部会把相对路径拼上 cwd,同样是脆弱点)。
116
+ - 回归断言改成**字符串形态**:反斜杠(Windows 形态)、裸 `./` 段、裸 `..` 段。原先那条用的是
117
+ `path.join(dir, '.', 'shape.ts')`,而 `path.join` 自己就会消解 `.`——它与 `path.join(dir, 'shape.ts')`
118
+ 是同一个字符串,断言在旧代码上也是绿的,等于没测。TS 的 `normalizePath` 在**任何平台**都把 `\`
119
+ 归一成 `/`,所以这三条在 Linux 上就能判定,不必等 Windows CI。
120
+ - 两条"本该有结果却没有"的路径**保持静默**(host 没认出 root path、`readFileSync` 失败),
121
+ 但不再是无从分辨的黑盒:调用方契约(入队 resolve、返回 `[]`)由断言正面锁住,理由写在代码里。
122
+ 不做日志是代价与收益的权衡——watch 每 15s 一轮、每个不受支持的 root 都会撞上它,按文件去重
123
+ 也只是把"每轮一条"换成"每个文件一条",而"这轮没增强"不会产出任何错误结果。2026-09-26 的
124
+ 教训是"静默"藏住了整层失效,但解法是补测试(见上一组断言),不是往终端打字。
125
+
126
+ ### 已知限制(本版未做):L2 不加载 TypeScript 的默认 lib
127
+
128
+ compiler host 里的 `getDefaultLibLocation: () => ts.getDefaultLibFilePath({})` 返回的是**文件**
129
+ 路径而不是目录,于是默认 lib 从来没有被加载过——显式标注的类型正常,依赖全局类型(`Promise` /
130
+ `Array` / DOM)的一律塌成 `any` / `unknown`:
131
+
132
+ ```
133
+ export function f() { return [1, 2, 3].map(n => n * 2) } → (): any (载入 lib 后是 number[])
134
+ export const g = async () => 1 → (): unknown (载入 lib 后是 Promise<number>)
135
+ ```
136
+
137
+ 本版把它换成**显式**的 `noLib: true` 并在代码里写明理由:行为不变(实测两者产出的符号完全一致),
138
+ 但"没生效"不再是一个没人写下来的意外。
139
+
140
+ 修它不止一行:目录给对之后 host 还必须按需提供 `lib.d.ts` 及其引用的 6 个文件(`lib.es5` /
141
+ `lib.decorators` / `lib.decorators.legacy` / `lib.dom` / `lib.webworker.importscripts` /
142
+ `lib.scripthost`,共 ~2.1MB 文本)。实测代价:每个 program 重解析 **p50=102ms/文件**;把解析好的
143
+ `SourceFile` 缓存跨 program 复用(或走 `ts.DocumentRegistry`)则是一次性 ~100ms + 每文件 ~1ms。
144
+ 这是"性能换质量"的产品选择,留到下一个版本单独做。
145
+
146
+ ### 修复:storeCache 的驻留估算量错了对象
147
+
148
+ `estimateResidentBytes()` 用 `Math.max(_residentChars, entries * 2500)` 估算一个 store 的内存占用,
149
+ 两个口径都不成立:
150
+
151
+ 1. `_residentChars` 是 `loadJson` 读进来的 JSON 文本长度,其中包含 `load()` 随即被
152
+ `stripPersistedDerived` 删掉的字节——老分片仍带 `searchText` / `linkedSymbols`。
153
+ 实测一个 store 的读盘文本是真实驻留的 **2.09 倍**。
154
+ 2. 估算在 `load()` 内就被调用,而 `searchText` 要到 `allEntries()` 才物化,同一个 store
155
+ 在物化前后估出两个不同的数。
156
+
157
+ 结果是高估:本可共存的 store 被过早逐出,下一次 `load()` 重读全部分片。在 Windows 上
158
+ (Defender 实时扫描放大每次文件读)尤其明显。
159
+
160
+ `deepseek-harness`(11698 文件 / 70119 条目)实测:
161
+
162
+ | 口径 | load() 后 | 物化 `searchText` 后 |
163
+ |---|---|---|
164
+ | 旧估算 | **167.2MB**(真实 63.5MB) | 167.2MB(真实 97.8MB) |
165
+ | 新估算 | 75.0MB(**+18.2%**) | 104.8MB(**+7.1%**) |
166
+
167
+ - 改为 `_estimateResidentBytes()` 统计内存中的对象图(形状开销 + `Buffer.byteLength()`,
168
+ CJK 在 UTF-8 里 3 字节/字符,用 `String.length` 会再低估一半),按 `_entriesVersion` 与
169
+ 新增的 `_materializeVersion` 记忆化——`evictStoreCache` 每次要对整个缓存逐 store 求和。
170
+ 代价是版本变化后的一次全量遍历,冷加载 +8~11%。
171
+ - 删除 `_residentChars` 及 `loadJson` 的 `sizeSink` 参数。
172
+ - `keepKey` 豁免(刚加载的 store 即使超预算也保留)保留,但不再静默:单实例超预算时按 root
173
+ 告警一次。
174
+
175
+ 行为变化:逐出变少,内存上限更接近 256MB 的本意;只有真正超预算时才像以前一样逐出。
176
+
177
+ 测试:新增 `test/store-cache.test.mjs`(22 项),`storeCache` 此前无测试覆盖。
178
+
179
+ ### 修复:watch 根不再钉住 store,也不在启动时急切读盘
180
+
181
+ `WatchManager` 长期持有每个 watch 根的 store 实例(`this.roots.get(root).store`),而
182
+ `removeRoot()` 的唯一调用方是 `watch_repo --watch false`。两个后果:
183
+
184
+ 1. 逐出对 watch 根不省内存——`storeCache` 丢了 key,watcher 还持有那份;
185
+ 2. 一旦被逐出,下一次 `load()` 会从盘上再建一份。同一个 root 出现两份互不可见的内存状态,
186
+ 各写各的脏分片,存在丢更新的窗口。
187
+
188
+ 同一个字段还让 `restorePersisted()` 在启动时对整条 `watchlist` 急切读盘:watch 过多少个根,
189
+ 启动就同步读多少个 store 的全部 shard。
190
+
191
+ - `addRoot()` 只登记 `{ snapshot, failures }`;新增 `storeFor(root)` 在 `pollRoot()` 需要时
192
+ 按需取,整个 `pollRoot` 期间共用一个实例。
193
+ - 每个 root 全进程只有一个活实例,内存上界回到 `STORE_CACHE_MAX_BYTES`。
194
+
195
+ ### 修复:父根不再重复索引嵌套的项目根
196
+
197
+ 一个没有项目标记(无 `.git`、无 `package.json`)的容器目录经由「标记缺失 → 回退到会话 cwd」
198
+ 成为根后,`walkDir()` 会把整棵树索引一遍,而树里的每个子项目各自都有自己的 store。实测某个
199
+ 父根的 store 里 **99.9% 的条目是子根的重复副本**,且是过期快照(部分源文件已从盘上删除)——
200
+ 这份 store 没有独有价值,却占用内存,并在父根下 `query_memory` 时返回陈旧内容。
201
+
202
+ - `walkDir()` 新增 `nestedStoreName`:子目录自带 store 时不再往下走,跳过的目录放进返回值的
203
+ `skipped` 字段。判据是子目录下存在 `<子目录>/<memoryDir>/format.json`——只有真正存过盘的
204
+ store 才算数,误建的空目录不作数。与 `findProjectRoot()` 的解析规则一致。
205
+ - `index_repo` 与 watch 轮询都启用;`index_repo` 在结果里列出被跳过的子根。
206
+ - 存量重复自动收敛:被跳过的子树不在 `seen` 里,既有的 `commitFileUpdates(..., { unseen })`
207
+ 清理路径会将其移除。升级后首次 `index_repo` 或一次 watch 轮询即生效,不需要迁移脚本。
208
+
209
+ ### 新增:父根未命中时列出独立子索引
210
+
211
+ 上一节之后,父根不再保存子项目的内容,在父根下 `query_memory` 问子项目必然查不到。现在本根
212
+ 记忆层(doc/symbol)无命中时,输出最前面会列出该根下自带 store 的子目录:
213
+
214
+ ```
215
+ Note: this root keeps no copy of N nested project(s) — each has its own index.
216
+ Re-run query_memory with `root: <path>` for the one you want:
217
+ - <path>
218
+ ```
219
+
220
+ - 触发条件只看记忆层:insight/experience 是全局层,与根无关,不应盖掉这条提示。
221
+ - 提示置于最前,因为 `truncate()` 截的是尾部,而 procedure 类条目单条可超 600 字符。
222
+ - 只影响 `query_memory` 的未命中路径,不涉及注入。
223
+
224
+ **验证**:`npm test` 518 项 / 28 个文件全绿;`npm run typecheck` 通过;`npm run eval:injection`
225
+ 逐项不变(命中 14 / 假阳性 0 / 漏召 0,P = R = 1.00)。
2
226
 
3
227
  ## 0.5.11 (2026-09-25)
4
228
 
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 dsh-project-memory contributors
3
+ Copyright (c) 2026 00080000 <3388065969@qq.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -35,7 +35,7 @@ A persistent **project development memory** for [DeepSeek Harness](https://githu
35
35
 
36
36
  ## Installation
37
37
 
38
- The plugin relies exclusively on stable public APIs (`defineTool`, `llm.stream`, `Schema`) declared via peerDependencies, ensuring compatibility with future rc/alpha releases without changes.
38
+ The plugin relies only on stable public APIs (`defineTool`, `llm.stream`, `Schema`) declared through peerDependencies.
39
39
 
40
40
  ```bash
41
41
  cd dsh-project-memory && dsh plugin --profile web add . -w
@@ -49,12 +49,6 @@ The plugin is also published on npm as a scoped package:
49
49
  dsh plugin --profile web add @yolk_vat-y/dsh-project-memory -w
50
50
  ```
51
51
 
52
- A prebuilt tarball is published with each release, installable without a build step:
53
-
54
- ```bash
55
- dsh plugin --profile web add /path/to/dsh-project-memory.tgz
56
- ```
57
-
58
52
  Each indexed project has its own store at `<root>/.dsh-project-memory/`. Add it to `.gitignore` if it should not be committed.
59
53
 
60
54
  ## Usage
@@ -91,7 +85,7 @@ The design follows four principles:
91
85
 
92
86
  - **Volatility** — context is ephemeral; it is lost when a session is compacted.
93
87
  - **Persistence** — the **memory** is stored on disk and survives compaction and new sessions.
94
- - **Compactness** — the code layer stores one bounded declaration line per symbol (≤200 chars) and the document layer keeps a ≤300-char `summary` plus a bounded `terms` set per chunk. **Derived data is never stored**: doc→symbol links and the BM25 `searchText` are computed at read time. How small the index ends up depends on symbol density, so treat "0.5%" as the sparse end of the range, not a guarantee: a Java/Vue project measured **~0.5% of source** (8.8 MB → 49 KB), while a symbol-dense TypeScript monorepo (11.7k files / 108 MB indexed) measured **~19%** for the code layer and **~106%** for the document layer. On a docs-only corpus (179 chunks / 225 KB of Markdown) `terms` ≈ **27.5%** of source and the on-disk store ≈ **166%** of source — on doc-heavy projects budget for roughly the docs themselves.
88
+ - **Compactness** — the code layer stores one bounded declaration line per symbol (≤200 chars) and the document layer keeps a ≤300-char `summary` plus a bounded `terms` set per chunk. **Derived data is never stored**: doc→symbol links and the BM25 `searchText` are computed at read time. How small the index ends up depends on symbol density and chunk length, so treat these as measurements of specific corpora (2026-09-25), not as guarantees: a code-only Vue app (289 files) lands at **325 bytes/entry ≈ 21% of source**, while a symbol-dense TypeScript monorepo (12,408 files / 106 MB of code + 14 MB of docs) measures **22% of source for the code layer (553 bytes/entry overall)** and **130% for the document layer**. Doc-heavy corpora are the largest per entry: a 274-document workspace (PDFs and Markdown) stores **1,506 bytes/entry**.
95
89
  - **Verifiability** — **recalls** carry a `path:line` citation where applicable, so the agent can confirm details against the source.
96
90
 
97
91
  Building the **memory** does not require an upfront scan: files are memorized as the model reads them, so the **memory** grows to cover exactly what has been worked with. Re-reading a file that has not changed is a no-op (content hash), so the **memory** stays fresh with minimal ongoing overhead.
@@ -233,29 +227,35 @@ where `config.yml` contains the same override block.
233
227
 
234
228
  ## Performance
235
229
 
236
- ### Synthetic Benchmark (Node 24.19, WSL2 on 20 vCPU, Linux file system)
230
+ ### Measured on real projects (2026-09-25)
237
231
 
238
- | Scenario | Scale | Measured |
239
- |----------|-------|----------|
240
- | Full cold index | 5,000 files / 20k entries | 269 ms avg (p50 267) |
241
- | Cold load | 5,000 files | 40 ms |
242
- | Hot lazy re-index (single file) | 5k files | p50 2.4 ms / max 5.5 ms |
243
- | query_memory (cached) | 5k files / 20k entries | p50 2.6 ms / p95 5.4 ms |
244
- | query_memory (cached) | 1k files / 4k entries | p50 0.6 ms / p95 1.6 ms |
245
- | Full cold index | 10,000 files / 40k entries | 551 ms avg (p50 528) |
246
- | Cold load | 10,000 files | 90 ms |
247
- | Hot lazy re-index (single file) | 10k files | p50 5.4 ms / max 9.2 ms |
232
+ Four corpora, one machine (Node 24.19, 20 vCPU, Linux file system), each run twice with the **second, warm-cache run** quoted. "Cold index" is a full index pass (walk + sha256 + extract + commit); "query" runs the shipped scorer over 100 sampled queries; "re-index 1 file" is the watch/lazy hot path.
248
233
 
249
- > Synthetic benchmark: generated code (~4–5 symbols/file), Node 24.19 on WSL2 / 20 vCPU / Linux file system, measured 2026-09-14. Reproduce with `npm run bench:synthetic -- 5000` (harness: `scripts/bench-synthetic.mjs`). Measures pure indexing overhead without LLM calls. query_memory uses the IDF cache + precomputed searchText; the first query after a write rebuilds IDF (**106 ms at 40k entries**, 57 ms at 20k, 12 ms at 4k), subsequent queries hit the cache.
234
+ | Corpus | Files / entries | Cold index | Cold load | Query p50 / p95 | Re-index 1 file | Store content / on disk | Heap after load |
235
+ |--------|-----------------|-----------|-----------|-----------------|-----------------|------------------------|-----------------|
236
+ | Vue 3 + Vite app (code only) | 289 / 2,142 | 283 ms | 5.2 ms | 0.86 / 1.8 ms | 0.4 ms | 0.66 MB / 1.52 MB | 6.1 MB |
237
+ | Docs + PDFs workspace (274 docs) | 286 / 2,120 | 6.5 s | 12.4 ms | 4.6 / 13.9 ms | 0.4 ms | 3.05 MB / 3.63 MB | 9.0 MB |
238
+ | TypeScript monorepo, 3,000-file slice | 3,000 / 17,733 | 2.1 s | 59 ms | 10.2 / 21.4 ms | 1.8 ms | 11.0 MB / 19.0 MB | 22.6 MB |
239
+ | TypeScript monorepo, whole tree | 12,408 / 79,168 | 8.6 s | 239 ms | 45.8 / 89.3 ms | 7.4 ms | 41.7 MB / 74.7 MB | 73.9 MB |
250
240
 
251
- ### Real Project Storage
241
+ **How it scales.** Re-indexing a changed file costs O(file), not O(corpus) — 0.4–7.4 ms across every corpus above. Cold load (≈19 µs/file), query (≈0.6 µs/entry) and resident heap (≈1.4 KB/entry once the first query materializes `searchText`) grow linearly with the index, which keeps small and mid-size projects in the single-digit-millisecond range.
252
242
 
253
- | Project | Files | Entries | Store Size | Per Entry |
254
- |---------|-------|---------|------------|-----------|
255
- | Java Spring Boot backend | 1,254 | 7,335 | 6.7 MB | ~0.9 KB |
256
- | Vue 3 + Vite frontend | 289 | 2,141 | 1.0 MB | ~0.5 KB |
243
+ > Two notes on method: `read+hash` depends on the OS page cache (2.5 s cold vs 0.3 s warm on the 12.4k-file tree), so the warm run is the one quoted; and this benchmark drifts by up to ~20% across days on the same machine, so compare numbers measured in the same session.
257
244
 
258
- > Real projects (Java + Vue), tested on Linux file system (Node 24). Real project entries are smaller than synthetic benchmarks due to lower symbol density and shorter declarations.
245
+ ### Synthetic Benchmark (Node 24.19, 20 vCPU, Linux file system)
246
+
247
+ | Scenario | Scale | Measured |
248
+ |----------|-------|----------|
249
+ | Full cold index | 5,000 files / 20k entries | 373 ms avg (p50 374) |
250
+ | Cold load | 5,000 files | 56 ms |
251
+ | Hot lazy re-index (single file) | 5k files | p50 2.8 ms / max 3.5 ms |
252
+ | query_memory (cached) | 5k files / 20k entries | p50 3.3 ms / p95 6.9 ms |
253
+ | query_memory (cached) | 1k files / 4k entries | p50 0.7 ms / p95 1.5 ms |
254
+ | Full cold index | 10,000 files / 40k entries | 696 ms avg (p50 668) |
255
+ | Cold load | 10,000 files | 123 ms |
256
+ | Hot lazy re-index (single file) | 10k files | p50 5.9 ms / max 13.5 ms |
257
+
258
+ > Synthetic benchmark: generated code (~4–5 symbols/file), Node 24.19 on 20 vCPU / Linux file system, measured 2026-09-25. Reproduce with `npm run bench:synthetic -- 5000` (harness: `scripts/bench-synthetic.mjs`). Measures pure indexing overhead without LLM calls. query_memory uses the IDF cache + searchText materialized on first use; the first query after a write rebuilds IDF (**142 ms at 40k entries**, 67 ms at 20k, 14 ms at 4k), subsequent queries hit the cache.
259
259
 
260
260
  ### Reproduce it on your own project
261
261
 
@@ -267,16 +267,17 @@ npm run bench -- /path/to/your/project
267
267
  node scripts/bench.mjs /path/to/your/project [--json] [--samples 100] [--no-pdf] [--keep]
268
268
  ```
269
269
 
270
- It reports the cold index split into read+hash / extract / commit, cold load, IDF rebuild, cold and hot query latency (p50/p95/max over 100 sampled queries through the shipped scorer), single-file hot re-index, store size and bytes per entry. Example — our internal Vue project (289 files / 2,141 entries, Node 24, 20 CPU, Linux):
270
+ It reports the cold index split into read+hash / extract / commit, cold load, IDF rebuild, cold and hot query latency (p50/p95/max over 100 sampled queries through the shipped scorer), single-file hot re-index, store content vs on-disk size, resident heap (after load and after the first query), bytes per entry and RSS. Example — the Vue app row above:
271
271
 
272
272
  ```
273
- cold index 253 ms (read+hash 9 ms · extract 229 ms · commit 13 ms) ← 2nd, warm-cache run
274
- store 1.10 MB · 538 bytes/entry · cold load 4.6 ms
275
- hot query p50 0.80 ms · p95 1.35 ms (2,141 entries)
276
- re-index 1 file p50 0.33 ms
273
+ cold index 283 ms (read+hash 11 ms · extract 256 ms · commit 14 ms) ← 2nd, warm-cache run
274
+ store 0.66 MB content · 1.52 MB on disk · 325 bytes/entry · cold load 5.2 ms
275
+ memory heap 6.1 MB after load → 6.7 MB after the first query (RSS 62 MB)
276
+ hot query p50 0.86 ms · p95 1.8 ms (2,142 entries)
277
+ re-index 1 file p50 0.4 ms
277
278
  ```
278
279
 
279
- Two caveats we would rather state than hide: `read+hash` depends on the OS page cache — on that corpus the first run spent 787 ms and the second 253 ms, so say which run you quote — and **real projects score slower than the synthetic table above** — on a 3,000-file slice of a large TypeScript repository (15,594 entries) hot queries were p50 7.5 ms, because real declaration text is longer than generated stubs. Pass `--queries your-queries.json` to run the same labeled-set method (hit@5 / hit@10 / MRR) against your own project.
280
+ Pass `--queries your-queries.json` to run the labeled-set method (hit@5 / hit@10 / MRR) against your own project.
280
281
 
281
282
  ## Design tradeoffs
282
283
 
@@ -288,7 +289,7 @@ Two caveats we would rather state than hide: `read+hash` depends on the OS page
288
289
  - **Model-facing memory: the agent writes, no human in the loop** — no human approval step: the consumer of this memory is the agent, and agents are usually headless, so memory that only promotes when someone clicks a card would never promote at all. `draft` is a provenance marker plus an evidence threshold, not an approval queue — the one inferring writer, `reflection` (off by default), writes task-level drafts only, and drafts never reach recall or injection.
289
290
  - **Full entries returned directly** — no "minimal index first, fetch details in a second call": entries are already compact, so returning them whole is both more verifiable and one round-trip cheaper.
290
291
  - **`forget` by query is aggressive; use IDs for precision** — no confirmation prompt, recycle bin, or exact-match-only mode: experience notes are low-risk, high-volume, and retrieval-only, so stale noise hurts more than an over-broad delete. For exact deletion use the ID shown by `query_memory`.
291
- - **TypeScript enhancement is optional, lazy, and cached** — the L2 TS Compiler API runs asynchronously on a priority queue (P0 `fs/observed`, P1 `watch`, P2 `index_repo`) and caches results by content hash; TS is never required and enhancement never blocks: requiring it would make non-TS projects uninstallable, and blocking would stall `index_repo` on large projects. `npm i -D typescript@5|6` is the entire setup, and a missing TS falls back to the L1 regex scanner.
292
+ - **TypeScript enhancement is optional, lazy, and cached** — the L2 TS Compiler API runs asynchronously on a priority queue (P0 `fs/observed`, P1 `watch`, P2 `index_repo`) and caches results by content hash; TS is never required and enhancement never blocks: requiring it would make non-TS projects uninstallable, and blocking would stall `index_repo` on large projects. `npm i -D typescript@5|6` is the entire setup, and a missing TS falls back to the L1 regex scanner. The **default lib is not loaded** (`noLib`): inference that depends on global types (`Promise`/`Array`/DOM) degrades to `any`/`unknown`, while explicitly annotated types are unaffected.
292
293
  - **Subagent sessions are out of scope for now**
293
294
 
294
295
  ## Development (for contributors)
@@ -297,7 +298,7 @@ These commands are for **maintaining the plugin code** — regular users do not
297
298
 
298
299
  ```bash
299
300
  npm install
300
- npm test # 476 tests (205 core + 16 TaskBridge + 12 insight-store + 9 insight-actions + 8 doc-index + 7 auto-inject + 10 host-contract + 5 reflection + 4 llm-route + 2 client-hints + 8 recall + 14 readiness + 7 insight-derive + 7 readiness-eval + 6 ops + 8 injection-audit + 5 injection-budget + 6 injection-scenarios + 18 bugfix-0.5.7 + 3 client-icons + 10 client-slash + 5 workflow-command + 7 client-session-id + 6 task-view + 79 root-guards + 9 store-gitignore)
301
+ npm test # 539 tests (214 core + 16 TaskBridge + 12 insight-store + 9 insight-actions + 8 doc-index + 7 auto-inject + 10 host-contract + 5 reflection + 4 llm-route + 2 client-hints + 10 recall + 14 readiness + 7 insight-derive + 7 readiness-eval + 6 ops + 11 injection-audit + 5 injection-budget + 6 injection-scenarios + 18 bugfix-0.5.7 + 3 client-icons + 10 client-slash + 5 workflow-command + 7 client-session-id + 6 task-view + 79 root-guards + 9 store-gitignore + 22 store-cache + 27 enhancer)
301
302
  npm run eval:injection # scenario P/R on the synthetic pool: 14/14 hits, 0 false positives, control group clean
302
303
  npm run eval:injection -- --store .dsh-project-memory/insights.json # replay on YOUR store; control group is a hard gate
303
304
  npm run selfcheck:triggers # which entries can still push, which declarations are dead (reads your local store)
@@ -308,4 +309,6 @@ Release notes live in [`CHANGELOG.md`](CHANGELOG.md) and on [GitHub Releases](ht
308
309
 
309
310
  ## License
310
311
 
311
- MIT
312
+ MIT — see [`LICENSE`](LICENSE).
313
+
314
+ Copyright (c) 2026 00080000 &lt;3388065969@qq.com&gt;
package/README.zh-CN.md CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ## 安装
36
36
 
37
- 插件仅依赖通过 peerDependencies 声明的稳定公共 API(`defineTool`、`llm.stream`、`Schema`),保证与后续 rc/alpha 版本无需改动即兼容。
37
+ 插件只依赖通过 peerDependencies 声明的稳定公共 API(`defineTool`、`llm.stream`、`Schema`)。
38
38
 
39
39
  ```bash
40
40
  cd dsh-project-memory && dsh plugin --profile web add . -w
@@ -48,12 +48,6 @@ cd dsh-project-memory && dsh plugin --profile web add . -w
48
48
  dsh plugin --profile web add @yolk_vat-y/dsh-project-memory -w
49
49
  ```
50
50
 
51
- 每个版本会附带预构建 tarball,无需构建步骤即可安装:
52
-
53
- ```bash
54
- dsh plugin --profile web add /path/to/dsh-project-memory.tgz
55
- ```
56
-
57
51
  每个被索引的项目在 `<root>/.dsh-project-memory/` 下有独立存储。如无需入库,可加入 `.gitignore`。
58
52
 
59
53
  ## 用法
@@ -88,7 +82,7 @@ dsh plugin --profile web add /path/to/dsh-project-memory.tgz
88
82
 
89
83
  - **易失性** — 上下文是临时的,会话压缩即丢失。
90
84
  - **持久性** — **记忆**存于磁盘,跨压缩与会话保留。
91
- - **紧凑性** — 代码层每个符号只存一行声明(≤200 字符),文档层每个 chunk 保留 ≤300 字符的 `summary` 与有界的 `terms`。**派生数据一律不落盘**:doc→symbol 链接与 BM25 的 `searchText` 都在读取期计算。最终体积取决于符号密度,所以「0.5%」是区间里稀疏的那一端、不是承诺:Java/Vue 项目实测约 **0.5% 源码**(8.8 MB → 49 KB),而符号密集的 TypeScript monorepo(11.7k 文件 / 108 MB 索引)实测代码层约 **19%**、文档层约 **106%**。纯文档语料实测(179 chunk / 225 KB Markdown)`terms` ≈ 源码 **27.5%**、整库落盘 ≈ 源码 **166%**——文档占比高的项目请按「约等于文档本身大小」估。
85
+ - **紧凑性** — 代码层每个符号只存一行声明(≤200 字符),文档层每个 chunk 保留 ≤300 字符的 `summary` 与有界的 `terms`。**派生数据一律不落盘**:doc→symbol 链接与 BM25 的 `searchText` 都在读取期计算。最终体积取决于符号密度与 chunk 长度,下面是特定语料的实测值(2026-09-25),不是承诺:纯代码的 Vue 应用(289 文件)实测 **325 bytes/条目 ≈ 源码 21%**;符号密集的 TypeScript monorepo(12,408 文件 / 代码 106 MB + 文档 14 MB)实测代码层 **占源码 22%**(整库 553 bytes/条目)、文档层 **占其源码 130%**。文档占比高的语料单条目最大:一个 274 篇文档的工作区(PDF + Markdown)实测 **1,506 bytes/条目**。
92
86
  - **可核验性** — **召回**在适用时携带 `路径:行号` 引用,agent 可对照源文件核实。
93
87
 
94
88
  构建**记忆**无需预先全量扫描:文件在模型读取时被记忆,**记忆**恰好覆盖实际处理过的内容。未变更的文件重读是空操作(内容哈希),因此**记忆**的持续维护开销很低。
@@ -230,29 +224,35 @@ dsh web --patch ./config.yml
230
224
 
231
225
  ## 性能
232
226
 
233
- ### 合成基准测试(Node 24.19,WSL2 / 20 vCPU,Linux 文件系统)
227
+ ### 真实项目实测(2026-09-25)
234
228
 
235
- | 场景 | 规模 | 实测 |
236
- |------|------|------|
237
- | 批量冷记忆构建 | 5,000 文件 / 20k 条目 | 269 ms 均值(p50 267)|
238
- | 冷加载 | 5,000 文件 | 40 ms |
239
- | 热路径懒记忆 | 单文件重记忆+落盘 | p50 2.4 ms / 最大 5.5 ms (5k) |
240
- | query_memory (缓存命中) | 5k 文件 / 20k 条目 | p50 2.6 ms / p95 5.4 ms |
241
- | query_memory (缓存命中) | 1k 文件 / 4k 条目 | p50 0.6 ms / p95 1.6 ms |
242
- | 批量冷记忆构建 | 10,000 文件 / 40k 条目 | 551 ms 均值(p50 528)|
243
- | 冷加载 | 10,000 文件 | 90 ms |
244
- | 热路径懒记忆 | 单文件重记忆+落盘 | p50 5.4 ms / 最大 9.2 ms (10k) |
229
+ 四个语料、同一台机器(Node 24.19,20 vCPU,Linux 文件系统),每个跑两遍、引用**第二次(页缓存已热)**的数据。「冷索引」= 完整索引一轮(walk + sha256 + 抽取 + 落盘);「查询」= 线上同一套 scorer 跑 100 条采样;「单文件重索引」= watch / 懒索引热路径。
245
230
 
246
- > 合成基准:生成代码(~4–5 符号/文件),Node 24.19 / WSL2 / 20 vCPU / Linux 文件系统,实测于 2026-09-14。复现命令 `npm run bench:synthetic -- 5000`(脚本 `scripts/bench-synthetic.mjs`)。测量纯索引开销,不含 LLM 调用。query_memory 使用 IDF 缓存 + 预计算 searchText;写入后的首次查询会重建 IDF(**40k 条目 106 ms**,20k 条目 57 ms,4k 条目 12 ms),后续查询命中缓存。
231
+ | 语料 | 文件数 / 条目数 | 冷索引 | 冷加载 | 查询 p50 / p95 | 单文件重索引 | 存储内容 / 落盘 | 加载后堆 |
232
+ |------|----------------|--------|--------|----------------|--------------|----------------|----------|
233
+ | Vue 3 + Vite 应用(纯代码) | 289 / 2,142 | 283 ms | 5.2 ms | 0.86 / 1.8 ms | 0.4 ms | 0.66 MB / 1.52 MB | 6.1 MB |
234
+ | 文档 + PDF 工作区(274 篇文档) | 286 / 2,120 | 6.5 s | 12.4 ms | 4.6 / 13.9 ms | 0.4 ms | 3.05 MB / 3.63 MB | 9.0 MB |
235
+ | TypeScript monorepo,3,000 文件切片 | 3,000 / 17,733 | 2.1 s | 59 ms | 10.2 / 21.4 ms | 1.8 ms | 11.0 MB / 19.0 MB | 22.6 MB |
236
+ | TypeScript monorepo,整棵树 | 12,408 / 79,168 | 8.6 s | 239 ms | 45.8 / 89.3 ms | 7.4 ms | 41.7 MB / 74.7 MB | 73.9 MB |
247
237
 
248
- ### 真实项目存储体积
238
+ **扩展形状。** 重索引一个变更文件的成本是 O(文件)、不是 O(语料)——上面每个语料都在 0.4–7.4 ms。冷加载(≈19 µs/文件)、查询(≈0.6 µs/条目)与常驻堆(首次查询物化 `searchText` 后 ≈1.4 KB/条目)随索引规模线性增长,因此小型与中型项目都落在个位数毫秒。
249
239
 
250
- | 项目 | 文件数 | 条目数 | 存储体积 | 单条目 |
251
- |------|--------|--------|----------|--------|
252
- | Java Spring Boot 后端 | 1,254 | 7,335 | 6.7 MB | ~0.9 KB |
253
- | Vue 3 + Vite 前端 | 289 | 2,141 | 1.0 MB | ~0.5 KB |
240
+ > 两条口径说明:`read+hash` 受操作系统页缓存影响(12.4k 文件时冷缓存 2.5 s、热缓存 0.3 s),所以引用的是热缓存那一遍;同一台机器上跨天跑同一基准会有约 20% 以内的漂移,请只比较同一会话内测出的数字。
254
241
 
255
- > 真实项目(Java + Vue),测试于 Linux 文件系统(Node 24)。真实项目单条目体积小于合成基准,因符号密度更低、声明行更短。
242
+ ### 合成基准测试(Node 24.19,20 vCPU,Linux 文件系统)
243
+
244
+ | 场景 | 规模 | 实测 |
245
+ |------|------|------|
246
+ | 批量冷记忆构建 | 5,000 文件 / 20k 条目 | 373 ms 均值(p50 374)|
247
+ | 冷加载 | 5,000 文件 | 56 ms |
248
+ | 热路径懒记忆 | 单文件重记忆+落盘 | p50 2.8 ms / 最大 3.5 ms (5k) |
249
+ | query_memory (缓存命中) | 5k 文件 / 20k 条目 | p50 3.3 ms / p95 6.9 ms |
250
+ | query_memory (缓存命中) | 1k 文件 / 4k 条目 | p50 0.7 ms / p95 1.5 ms |
251
+ | 批量冷记忆构建 | 10,000 文件 / 40k 条目 | 696 ms 均值(p50 668)|
252
+ | 冷加载 | 10,000 文件 | 123 ms |
253
+ | 热路径懒记忆 | 单文件重记忆+落盘 | p50 5.9 ms / 最大 13.5 ms (10k) |
254
+
255
+ > 合成基准:生成代码(~4–5 符号/文件),Node 24.19 / 20 vCPU / Linux 文件系统,实测于 2026-09-25。复现命令 `npm run bench:synthetic -- 5000`(脚本 `scripts/bench-synthetic.mjs`)。测量纯索引开销,不含 LLM 调用。query_memory 使用 IDF 缓存 + 首次使用时物化 searchText;写入后的首次查询会重建 IDF(**40k 条目 142 ms**,20k 条目 67 ms,4k 条目 14 ms),后续查询命中缓存。
256
256
 
257
257
  ### 自己复现这些数字
258
258
 
@@ -264,16 +264,17 @@ npm run bench -- /你的/项目路径
264
264
  node scripts/bench.mjs /你的/项目路径 [--json] [--samples 100] [--no-pdf] [--keep]
265
265
  ```
266
266
 
267
- 输出包含:冷索引(拆成 read+hash / extract / commit 三段)、冷加载、IDF 重建、冷查询与热查询延迟(走线上同一套 scorer,100 条采样报 p50/p95/max)、单文件热重索引、存储体积与每条字节数。示例——我们内部的 Vue 项目(289 文件 / 2,141 条目,Node 24,20 CPU,Linux):
267
+ 输出包含:冷索引(拆成 read+hash / extract / commit 三段)、冷加载、IDF 重建、冷查询与热查询延迟(走线上同一套 scorer,100 条采样报 p50/p95/max)、单文件热重索引、存储内容与落盘体积、常驻堆(加载后与首次查询后)、单条目字节数。示例——上表里的 Vue 应用:
268
268
 
269
269
  ```
270
- 冷索引 253 ms (read+hash 9 ms · extract 229 ms · commit 13 ms)← 第二次、页缓存已热
271
- 存储 1.10 MB · 538 bytes/条目 · 冷加载 4.6 ms
272
- 热查询 p50 0.80 ms · p95 1.35 ms (2,141 条目)
273
- 单文件重索引 p50 0.33 ms
270
+ 冷索引 283 ms (read+hash 11 ms · extract 256 ms · commit 14 ms)← 第二次、页缓存已热
271
+ 存储 内容 0.66 MB · 落盘 1.52 MB · 325 bytes/条目 · 冷加载 5.2 ms
272
+ 内存 加载后堆 6.1 MB → 首次查询后 6.7 MB(RSS 62 MB)
273
+ 热查询 p50 0.86 ms · p95 1.8 ms (2,142 条目)
274
+ 单文件重索引 p50 0.4 ms
274
275
  ```
275
276
 
276
- 两个我们宁可自己说清楚的坑:`read+hash` 受操作系统页缓存影响——同一个语料第一遍花了 787 ms、第二遍 253 ms,报数时请说明是第几遍;**真实项目比上面的合成基准慢**——在一个大型 TypeScript 仓库的 3,000 文件切片上(15,594 条目)热查询 p50 为 7.5 ms,因为真实声明文本比生成出来的桩代码长得多。带 `--queries 你的查询集.json` 可以在你自己的项目上跑同一套标注集方法(hit@5 / hit@10 / MRR)。
277
+ 带 `--queries 你的查询集.json` 可以在你自己的项目上跑标注集方法(hit@5 / hit@10 / MRR)。
277
278
 
278
279
  ## 设计取舍
279
280
 
@@ -285,7 +286,7 @@ node scripts/bench.mjs /你的/项目路径 [--json] [--samples 100] [--no-pdf]
285
286
  - **面向模型的记忆:agent 自己写,不把人放进回路** — 不要求人工批准:记忆的消费方是 agent,而 agent 通常是无头的,只在有人点卡片时才升级的记忆等于永远不会升级。`draft` 是「来源标记 + 佐证门槛」而不是审批队列——唯一的推断型写入者 `reflection`(默认关闭)只写任务级草稿,草稿不进召回与注入。
286
287
  - **直接返回完整条目** — 条目本就紧凑,完整返回更可核验,也少一轮往返。
287
288
  - **`forget` 按关键词激进;精确请用 ID** — 不做确认弹窗、回收站或仅精确匹配:经验笔记低风险、高量、仅用于检索,陈旧噪音比误删更伤。精确删用 `query_memory` 输出里的 ID。
288
- - **TS 增强可选、异步、缓存** — L2 TS Compiler API 在优先级队列异步跑(P0 `fs/observed`、P1 `watch`、P2 `index_repo`),结果按内容哈希缓存;不强制 TS、也不阻塞索引:强制会让非 TS 项目装不上,阻塞会卡死大项目的 `index_repo`;`npm i -D typescript@5|6` 即自动启用,没有 TS 时回退 L1 正则。
289
+ - **TS 增强可选、异步、缓存** — L2 TS Compiler API 在优先级队列异步跑(P0 `fs/observed`、P1 `watch`、P2 `index_repo`),结果按内容哈希缓存;不强制 TS、也不阻塞索引:强制会让非 TS 项目装不上,阻塞会卡死大项目的 `index_repo`;`npm i -D typescript@5|6` 即自动启用,没有 TS 时回退 L1 正则。**默认 lib 不加载**(编译期 `noLib`):依赖全局类型(`Promise`/`Array`/DOM)的推导会退化成 `any`/`unknown`,显式标注的类型不受影响。
289
290
  - **子代理会话暂不纳入(以后可能做)**
290
291
 
291
292
  ## 开发(面向贡献者)
@@ -294,7 +295,7 @@ node scripts/bench.mjs /你的/项目路径 [--json] [--samples 100] [--no-pdf]
294
295
 
295
296
  ```bash
296
297
  npm install
297
- npm test # 476 项测试(核心 205 + TaskBridge 16 + insight-store 12 + insight-actions 9 + doc-index 8 + auto-inject 7 + host-contract 10 + reflection 5 + llm-route 4 + client-hints 2 + recall 8 + readiness 14 + insight-derive 7 + readiness-eval 7 + ops 6 + injection-audit 8 + injection-budget 5 + injection-scenarios 6 + bugfix-0.5.7 18 + client-icons 3 + client-slash 10 + workflow-command 5 + client-session-id 7 + task-view 6 + root-guards 79 + store-gitignore 9)
298
+ npm test # 539 项测试(核心 214 + TaskBridge 16 + insight-store 12 + insight-actions 9 + doc-index 8 + auto-inject 7 + host-contract 10 + reflection 5 + llm-route 4 + client-hints 2 + recall 10 + readiness 14 + insight-derive 7 + readiness-eval 7 + ops 6 + injection-audit 11 + injection-budget 5 + injection-scenarios 6 + bugfix-0.5.7 18 + client-icons 3 + client-slash 10 + workflow-command 5 + client-session-id 7 + task-view 6 + root-guards 79 + store-gitignore 9 + store-cache 22 + enhancer 27)
298
299
  npm run eval:injection # 合成池上的场景 P/R:命中 14/14、假阳性 0、对照组零注入
299
300
  npm run eval:injection -- --store .dsh-project-memory/insights.json # 用你自己的 store 重放;对照组是硬闸门
300
301
  npm run selfcheck:triggers # 哪些条目还推得动、哪些声明是死的(读你本地的 store)
@@ -305,4 +306,6 @@ npm run bench -- /你的/项目路径 # 对任意项目量索引/查询性能
305
306
 
306
307
  ## 许可证
307
308
 
308
- MIT
309
+ MIT,全文见 [`LICENSE`](LICENSE)。
310
+
311
+ Copyright (c) 2026 00080000 &lt;3388065969@qq.com&gt;