@max-null/dsh-memory 0.6.1 → 0.7.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 +25 -20
- package/client.js +221 -102
- package/dist/anchors.d.ts +45 -0
- package/dist/anchors.js +45 -0
- package/dist/engine.d.ts +189 -8
- package/dist/engine.js +445 -17
- package/dist/index.js +32 -8
- package/dist/quarantine.d.ts +24 -0
- package/dist/quarantine.js +43 -0
- package/dist/query-log.d.ts +155 -0
- package/dist/query-log.js +220 -0
- package/dist/routes.js +2 -0
- package/dist/self.d.ts +1 -0
- package/dist/self.js +14 -10
- package/package.json +2 -2
package/dist/engine.d.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Service } from '@deepseek-ai/cordis';
|
|
8
8
|
import { type PromptFile } from './prompt-files.ts';
|
|
9
|
+
import type { AnchorProbes, MemoryAnchor } from './anchors.ts';
|
|
10
|
+
import type { MemorySource, SourceStat } from './query-log.ts';
|
|
11
|
+
export type { MemorySource, QueryLogEntry, SourceStat } from './query-log.ts';
|
|
9
12
|
declare const memoryIdBrand: unique symbol;
|
|
10
13
|
/** Opaque identity of one stored memory record. */
|
|
11
14
|
export type MemoryId = string & {
|
|
@@ -15,8 +18,9 @@ export type MemoryId = string & {
|
|
|
15
18
|
export declare function MemoryId(id: string): MemoryId;
|
|
16
19
|
export type MemoryNamespace = 'global' | 'project';
|
|
17
20
|
/**
|
|
18
|
-
* 审核维度(0.3.0):`suggested`
|
|
19
|
-
*
|
|
21
|
+
* 审核维度(0.3.0):`suggested` 未生效;`approved` 已生效。2026-09-15 起写入默认
|
|
22
|
+
* `approved`(静默机制),`suggested` 主要留给被隔离的记录与人工回退。
|
|
23
|
+
* 注入与否由独立维度 `injected` 控制(见 MemoryRecord)。
|
|
20
24
|
*/
|
|
21
25
|
export type MemoryStatus = 'suggested' | 'approved';
|
|
22
26
|
/**
|
|
@@ -46,6 +50,28 @@ export interface MemoryRecord {
|
|
|
46
50
|
status: MemoryStatus;
|
|
47
51
|
/** 注入维度:true = 每轮全量注入 system prompt(常驻);false = 仅检索。 */
|
|
48
52
|
injected: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* 隔离位(2026-09-15):命中危险内容规则 → 不进注入、不进检索,等人工放行或删除。
|
|
55
|
+
* 与 `status` 双保险:隔离记录同时保持 `suggested`,即使某条路径漏了过滤也不会注入。
|
|
56
|
+
*/
|
|
57
|
+
quarantined: boolean;
|
|
58
|
+
/** 隔离原因(命中的规则名,诊断与面板展示用)。 */
|
|
59
|
+
quarantineReason?: string;
|
|
60
|
+
/** 命中次数(2026-09-15 淘汰机制):被 memory_search 命中的累计次数。 */
|
|
61
|
+
hitCount: number;
|
|
62
|
+
/**
|
|
63
|
+
* 写入来源(2026-09-15 反例 5):'agent' = 主模型写入,'human' = 人工写入;
|
|
64
|
+
* 缺省 = 旧记录(来源未知,不猜测)。影响力统计按它聚合(见 sourceStats)。
|
|
65
|
+
*/
|
|
66
|
+
source?: MemorySource;
|
|
67
|
+
/** 注入由信号自动开启(true)还是人工设置(缺省);只有自动开的会被「长期未命中」自动降级。 */
|
|
68
|
+
injectedAuto?: boolean;
|
|
69
|
+
/** 有效性锚点(2026-09-15):绑定到某个可探测的环境值;值变了即失效。 */
|
|
70
|
+
anchor?: MemoryAnchor;
|
|
71
|
+
/** 锚点已失效(由会话启动时的校验写入);失效不删除,只降权并在检索结果里标注。 */
|
|
72
|
+
stale?: boolean;
|
|
73
|
+
/** 失效原因(锚点名 + 声明值 vs 当前值,诊断用)。 */
|
|
74
|
+
staleReason?: string;
|
|
49
75
|
content: string;
|
|
50
76
|
keywords: string[];
|
|
51
77
|
createdAt: number;
|
|
@@ -61,11 +87,20 @@ export interface MemoryWrite {
|
|
|
61
87
|
content: string;
|
|
62
88
|
namespace?: MemoryNamespace;
|
|
63
89
|
keywords?: string[];
|
|
90
|
+
/** 写入来源(2026-09-15);缺省 'agent'(主模型写入)。面板等人工路径传 'human'。 */
|
|
91
|
+
source?: MemorySource;
|
|
92
|
+
/**
|
|
93
|
+
* 有效性锚点(可选,2026-09-15):把这条记忆绑定到某个可探测的环境值上,值变了即自动
|
|
94
|
+
* 失效。只用于「随环境变化的事实」(某工具版本下的行为、某环境变量决定的配置)。
|
|
95
|
+
*/
|
|
96
|
+
anchor?: MemoryAnchor;
|
|
64
97
|
}
|
|
65
98
|
export interface MemoryFilter {
|
|
66
99
|
namespace?: MemoryNamespace;
|
|
67
100
|
status?: MemoryStatus;
|
|
68
101
|
injected?: boolean;
|
|
102
|
+
/** 隔离过滤(2026-09-15):缺省排除隔离记录;传 true 只取隔离记录(面板审用)。 */
|
|
103
|
+
quarantined?: boolean;
|
|
69
104
|
/** 记录类别过滤(0.6.0);缺省不过滤(工具层显式传 kind='fact' 保持旧行为)。 */
|
|
70
105
|
kind?: MemoryKind;
|
|
71
106
|
}
|
|
@@ -73,6 +108,36 @@ export interface MemoryHit {
|
|
|
73
108
|
record: MemoryRecord;
|
|
74
109
|
score: number;
|
|
75
110
|
}
|
|
111
|
+
/** 反查候选({@link MemoryEngine.suggestKeywordGaps}):一条「本应命中」的现存记忆。 */
|
|
112
|
+
export interface KeywordGapCandidate {
|
|
113
|
+
id: string;
|
|
114
|
+
/** 正文摘要({@link GAP_EXCERPT_CHARS} 字符以内)。 */
|
|
115
|
+
excerpt: string;
|
|
116
|
+
/** 现有关键词;补写时在其基础上追加。 */
|
|
117
|
+
keywords: string[];
|
|
118
|
+
/** 精确词元命中数:> 0 表示「现在重搜就该命中」。 */
|
|
119
|
+
exact: number;
|
|
120
|
+
/** 前缀近似命中的查询词元(如 vue3 ↔ vue)。 */
|
|
121
|
+
relaxed: string[];
|
|
122
|
+
}
|
|
123
|
+
/** 一个空命中查询的反查结果;`candidates` 为空即知识缺口。 */
|
|
124
|
+
export interface KeywordGap {
|
|
125
|
+
query: string;
|
|
126
|
+
/** 该查询在日志里空命中的次数。 */
|
|
127
|
+
count: number;
|
|
128
|
+
/** 最近一次出现的时间。 */
|
|
129
|
+
lastAt: number;
|
|
130
|
+
candidates: KeywordGapCandidate[];
|
|
131
|
+
}
|
|
132
|
+
/** {@link MemoryEngine.suggestKeywordGaps} 的报告(只产出报告,不自动补写)。 */
|
|
133
|
+
export interface KeywordGapReport {
|
|
134
|
+
/** 查询日志总条数。 */
|
|
135
|
+
logSize: number;
|
|
136
|
+
/** 空命中条数(含被 `limit` 截掉的查询)。 */
|
|
137
|
+
misses: number;
|
|
138
|
+
/** 逐查询的候选(最多 `limit` 个查询,按最近出现排序)。 */
|
|
139
|
+
gaps: KeywordGap[];
|
|
140
|
+
}
|
|
76
141
|
/** One durable memory change, emitted after the backend acknowledges the write. */
|
|
77
142
|
export type MemoryChange = {
|
|
78
143
|
operation: 'remembered';
|
|
@@ -88,6 +153,10 @@ export type MemoryChange = {
|
|
|
88
153
|
operation: 'injected';
|
|
89
154
|
id: MemoryId;
|
|
90
155
|
injected: boolean;
|
|
156
|
+
} | {
|
|
157
|
+
operation: 'quarantined';
|
|
158
|
+
id: MemoryId;
|
|
159
|
+
quarantined: boolean;
|
|
91
160
|
};
|
|
92
161
|
declare module '@deepseek-ai/cordis' {
|
|
93
162
|
interface Context {
|
|
@@ -130,6 +199,11 @@ export interface MemoryConfig {
|
|
|
130
199
|
/** 全局提示词模板根目录(0.6.0);默认 `$DSH_HOME/prompt-library`。
|
|
131
200
|
* project 模板随工作区(`<workspace>/.dsh/prompt-library`,与记忆同构)。 */
|
|
132
201
|
promptGlobalRoot?: string;
|
|
202
|
+
/**
|
|
203
|
+
* 查询日志文件路径(2026-09-15 §六.6);默认为全局存储根下的 `query-log.json`
|
|
204
|
+
* ——即与 `memory.json` **同目录**(`$DSH_HOME/storages/query-log.json`),不落子目录。
|
|
205
|
+
*/
|
|
206
|
+
queryLogPath?: string;
|
|
133
207
|
}
|
|
134
208
|
/**
|
|
135
209
|
* Cross-session plaintext memory over the storage hub, split by namespace:
|
|
@@ -148,6 +222,8 @@ export declare class MemoryEngine extends Service {
|
|
|
148
222
|
/** backend 只注册一次(registry 重名抛 duplicate;reload 清缓存后不得重复注册)。 */
|
|
149
223
|
private registeredProjectBackends;
|
|
150
224
|
private facility?;
|
|
225
|
+
/** 会话启动维护只跑一次的标记(2026-09-15)。 */
|
|
226
|
+
private maintenanceDone;
|
|
151
227
|
constructor(ctx: import('@deepseek-ai/cordis').Context, config?: MemoryConfig);
|
|
152
228
|
protected [Service.init](): Promise<void>;
|
|
153
229
|
/** 打开全局存储域(init 与 reload 共用;backend 复用已注册实例)。 */
|
|
@@ -167,10 +243,21 @@ export declare class MemoryEngine extends Service {
|
|
|
167
243
|
* (JsonStorageBackend 打开时加载一次,无 watch——2026-08-19 实测)。
|
|
168
244
|
*/
|
|
169
245
|
reload(): Promise<void>;
|
|
170
|
-
/**
|
|
246
|
+
/**
|
|
247
|
+
* Create one record. 低危内容直接 `approved`(静默生效,2026-09-15 静默记忆机制);
|
|
248
|
+
* 命中危险内容规则则隔离:`status` 留 `suggested`(天然不注入)**并且**置
|
|
249
|
+
* `quarantined`(不进检索),双保险,等待人工放行或删除。
|
|
250
|
+
*/
|
|
171
251
|
remember(input: MemoryWrite, projectCwd?: string): Promise<MemoryRecord>;
|
|
172
252
|
list(filter?: MemoryFilter, projectCwd?: string): Promise<MemoryRecord[]>;
|
|
253
|
+
/**
|
|
254
|
+
* 关键词检索。每次成功返回的检索都记一条查询日志(⑥ 2026-09-15)——命中与否都记:
|
|
255
|
+
* 漏检不产生任何事件,只有日志能事后回答「哪些查询本该命中谁」(反例 4)。日志是旁路:
|
|
256
|
+
* 写失败只记 warn,绝不改变本次检索结果。
|
|
257
|
+
*/
|
|
173
258
|
search(query: string, filter?: MemoryFilter, projectCwd?: string): Promise<MemoryHit[]>;
|
|
259
|
+
/** 检索打分本体(BM25 + 可选语义融合);查询日志由 {@link search} 记录。 */
|
|
260
|
+
private rank;
|
|
174
261
|
/**
|
|
175
262
|
* 语义排序(0.5.2):查询向量 + 候选向量的余弦相似度,仅在有
|
|
176
263
|
* embeddings 配置时被调用。缺失向量(新增/更新过的记录)在此补生成
|
|
@@ -178,9 +265,104 @@ export declare class MemoryEngine extends Service {
|
|
|
178
265
|
*/
|
|
179
266
|
private semanticRanking;
|
|
180
267
|
/** 按已知 id 定位记录所在表(global 优先,其次当前工作区表)。 */
|
|
268
|
+
/**
|
|
269
|
+
* 会话启动维护(2026-09-15):一次扫描做完三件事——旧数据迁移、长期未命中的自动降级、
|
|
270
|
+
* 锚点批量校验。只跑一次、幂等、**失败不阻断**(记忆可用性优先于维护完成度)。
|
|
271
|
+
*
|
|
272
|
+
* 调用点在 `ensureProjectOpen`:它是 async,且每会话首次打开工作区表时必经。
|
|
273
|
+
* `memory:recall` 的 provider 是**同步**回调,不能在那里 await。
|
|
274
|
+
*
|
|
275
|
+
* @param projectCwd - 会话工作区:迁移/降级/校验都连它一起处理。
|
|
276
|
+
*/
|
|
277
|
+
private runMaintenanceOnce;
|
|
278
|
+
/**
|
|
279
|
+
* 锚点探测上下文。`toolNames` 拿不到时返回 `undefined`——**不能给空数组**:那会被判成
|
|
280
|
+
* 「工具全没了」,让所有 `tool-list` 锚点误失效。
|
|
281
|
+
*/
|
|
282
|
+
private anchorProbes;
|
|
181
283
|
private tableOf;
|
|
182
|
-
/**
|
|
284
|
+
/** 命中标记:写回 lastUsedAt 与命中次数,达标即自动升常驻(2026-09-15)。 */
|
|
183
285
|
private markUsed;
|
|
286
|
+
/**
|
|
287
|
+
* 命中记账 + 自动升级(2026-09-15 静默记忆机制):命中次数达阈值即自动打开常驻
|
|
288
|
+
* 注入——「被反复检索命中」是它值得每轮付费的唯一客观证据。统计性字段不 emit,
|
|
289
|
+
* 只有真的改变了注入状态才发 `memory/changed`。
|
|
290
|
+
*/
|
|
291
|
+
private recordUse;
|
|
292
|
+
/** 查询日志(惰性创建;位置见 {@link queryLogPath})。 */
|
|
293
|
+
private queryLogInstance?;
|
|
294
|
+
private queryLog;
|
|
295
|
+
/** 查询日志文件路径:与 `memory.json` 同目录(不落子目录)。 */
|
|
296
|
+
private queryLogPath;
|
|
297
|
+
/**
|
|
298
|
+
* 记录一次检索:查询文本(截断到 120 字符)+ 命中条数 + 时间。**失败绝不影响检索**——
|
|
299
|
+
* 日志是旁路证据,不是检索的一部分。模板库检索不进日志:`prompt_search` 复用同一条
|
|
300
|
+
* `search` 通道,但模板是文件库、不属于「记忆漏检」的观察面。
|
|
301
|
+
*
|
|
302
|
+
* 查询文本是新的明文落盘点,因此先过危险内容硬拦(与写入路径同一套规则):命中就只记
|
|
303
|
+
* 规则名、不留原文——凭据不该因为「只是被搜了一下」而进另一个明文文件。
|
|
304
|
+
*/
|
|
305
|
+
private recordQuery;
|
|
306
|
+
/**
|
|
307
|
+
* 影响力统计(⑥ / 反例 5):按来源聚合「写入量 / 被检索命中数 / 常驻注入数」。
|
|
308
|
+
* 只算 fact 记录(模板索引不是记忆),隔离记录不计(与 list 的默认口径一致:
|
|
309
|
+
* 它们不在流通里)。「被人工删除数」暂缺的原因见 `summarizeSources` 的注释。
|
|
310
|
+
*/
|
|
311
|
+
sourceStats(projectCwd?: string): Promise<SourceStat[]>;
|
|
312
|
+
/**
|
|
313
|
+
* 低频反查(⑥,**只产出报告,不自动改写**):拿「命中 0 的查询」与既有记忆反查,
|
|
314
|
+
* 给出「本应命中却没命中」的候选,供人工或模型补 keywords。
|
|
315
|
+
*
|
|
316
|
+
* 不做自动补写:判断「这条记忆是否真该被那个查询找到」需要 LLM 语义判断,插件只提供
|
|
317
|
+
* 证据(候选为空即知识缺口——「这类知识你还没有」本身也是有用信息)。调用时机由人
|
|
318
|
+
* 或模型定,不在检索路径上、不产生每轮成本。
|
|
319
|
+
*
|
|
320
|
+
* @param projectCwd - 会话工作区;候选库连工作区记忆一起取。
|
|
321
|
+
* @param limit - 最多反查几个空命中查询(按最近出现排序)。
|
|
322
|
+
*/
|
|
323
|
+
suggestKeywordGaps(projectCwd?: string, limit?: number): Promise<KeywordGapReport>;
|
|
324
|
+
/**
|
|
325
|
+
* 长期未命中的自动降级(2026-09-15 淘汰机制):**只撤自动开的常驻注入**——人工开
|
|
326
|
+
* 的开关不会被时间悄悄关掉(那是人的决定,不是信号的结论)。判定用 lastUsedAt,
|
|
327
|
+
* 从未命中过则回退 updatedAt。会话启动时调一次即可(见 index.ts 的预热路径)。
|
|
328
|
+
*
|
|
329
|
+
* @param projectCwd - 会话工作区;给了就连工作区表一起扫。
|
|
330
|
+
* @param now - 当前时间(测试注入用)。
|
|
331
|
+
* @returns 被撤下的条数(诊断与日志用)。
|
|
332
|
+
*/
|
|
333
|
+
demoteStale(projectCwd?: string, now?: number): Promise<number>;
|
|
334
|
+
/**
|
|
335
|
+
* 锚点批量校验(2026-09-15):会话启动时调一次,把失效的条目标成 `stale`。
|
|
336
|
+
*
|
|
337
|
+
* 失效同时**撤掉常驻注入**——内容可能已经错了,继续每轮注入就是在误导(反例 2 的
|
|
338
|
+
* 教训);但**不删除、也不排除检索**,它在结果里带标注,等模型核对后回写。
|
|
339
|
+
* 探测不到(`undefined`)按「未校验」处理,不动任何状态。
|
|
340
|
+
*
|
|
341
|
+
* @param probes - 探测上下文(工具清单 / 自身版本 / env 读取器)。
|
|
342
|
+
* @param projectCwd - 会话工作区;给了就连工作区表一起校验。
|
|
343
|
+
* @returns 本次状态发生变化的条数。
|
|
344
|
+
*/
|
|
345
|
+
verifyAnchors(probes: AnchorProbes, projectCwd?: string): Promise<number>;
|
|
346
|
+
/**
|
|
347
|
+
* 旧数据迁移(2026-09-15 静默记忆机制):一次性、幂等,会话启动维护时调用。
|
|
348
|
+
*
|
|
349
|
+
* 旧语义里 `suggested` 是「等人工审核」,新语义里它只剩「隔离」与「人工回退」两种含义——
|
|
350
|
+
* 不迁移的话那些记录会永久卡住:既不注入,也没人去放行。迁移逐条过危险内容检测:
|
|
351
|
+
* 安全则升 `approved`(写入即生效),命中则标 `quarantined` 进隔离(**必须先检测再放行**:
|
|
352
|
+
* 历史条目若含密钥,直接放行等于让它开始进注入)。
|
|
353
|
+
*
|
|
354
|
+
* 另外把「有 `lastUsedAt` 但无 `hitCount`」的老记录回填 `hitCount = 1`——它们可能早已被
|
|
355
|
+
* 反复用过,不该从零开始攒两次命中。旧 `injected: true` 且无 `injectedAuto` 的一律不动:
|
|
356
|
+
* 那是人工决定,不该被自动降级。
|
|
357
|
+
*
|
|
358
|
+
* @param projectCwd - 会话工作区;给了就连工作区表一起迁移。
|
|
359
|
+
* @returns 迁移统计(放行 / 隔离 / 回填计数)。
|
|
360
|
+
*/
|
|
361
|
+
migrateLegacy(projectCwd?: string): Promise<{
|
|
362
|
+
approved: number;
|
|
363
|
+
quarantined: number;
|
|
364
|
+
counted: number;
|
|
365
|
+
}>;
|
|
184
366
|
forget(id: MemoryId, projectCwd?: string): Promise<boolean>;
|
|
185
367
|
setStatus(id: MemoryId, status: MemoryStatus, projectCwd?: string): Promise<MemoryRecord>;
|
|
186
368
|
/**
|
|
@@ -189,9 +371,9 @@ export declare class MemoryEngine extends Service {
|
|
|
189
371
|
*/
|
|
190
372
|
setInjected(id: MemoryId, injected: boolean, projectCwd?: string): Promise<MemoryRecord>;
|
|
191
373
|
/**
|
|
192
|
-
* 修改一条记忆的内容/关键词(0.3.1
|
|
193
|
-
*
|
|
194
|
-
*
|
|
374
|
+
* 修改一条记忆的内容/关键词(0.3.1 整理记忆用)。2026-09-15 起:改动**不再**
|
|
375
|
+
* 退回待审核——静默机制下记忆由系统与模型自行维护,更新即保持原有审核状态;
|
|
376
|
+
* `injected` 保留原值。若新内容命中危险规则则转为隔离(与写入路径同一判定)。
|
|
195
377
|
*/
|
|
196
378
|
update(id: MemoryId, patch: {
|
|
197
379
|
content?: string;
|
|
@@ -253,4 +435,3 @@ export declare class MemoryEngine extends Service {
|
|
|
253
435
|
/** 删除模板:md 文件与索引一并删除;未命中返回 false。 */
|
|
254
436
|
promptRemove(idOrName: string, projectCwd?: string): Promise<boolean>;
|
|
255
437
|
}
|
|
256
|
-
export {};
|