dsh-prime-memory 0.26.22 → 0.26.24
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 +15 -0
- package/dist/clients/drsc-inproc.d.ts +1 -1
- package/dist/clients/drsc-inproc.js +62 -18
- package/dsh.plugin.json +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# 更新日志(Changelog)
|
|
2
2
|
|
|
3
|
+
## [0.26.24] — 2026-11-02
|
|
4
|
+
|
|
5
|
+
- **修掉 `0.26.23` 自己引入的回归:读面可能被永久堵死**。上一版规定「对端已升版但 `drsReadFacade()` 返回 undefined 时抛可重试错、不回退自开库」,用意是避免把自开库结果永久缓存。但**未就绪是无法与「本进程根本不会有人 activate collector」区分的**,两类真实场景落进后者:① 宿主外跑本仓读面做校验的脚本(新进程 import collector,`apply()` 从未运行,门面永远 undefined);② collector 装了但未进当前 profile 的装载清单。它们会每次读都报错,比 `0.26.21` 的直接自开库更糟。
|
|
6
|
+
- **改法:有界重试后再回退**(`src/clients/drsc-inproc.ts`)。预算 `FACADE_RETRY_BUDGET = 3`:前 3 次读抛可重试错且不缓存(给宿主内 collector 的 `setImmediate` 初始化留出窗口,保住「优先用门面」);第 4 次仍拿不到门面即认定本进程等不到,回退自开只读库。两条路径都读的是同一个 WAL 库、句柄只读,不存在读到错数据的风险,差别只是是否多占一份常驻 `summaries`。门面一旦就绪,计数器归零。
|
|
7
|
+
- **测试**:`tests/drsc-inproc.test.ts` 相应改为断言完整状态机——预算内逐次抛错(错误文本含 `1/3`、`2/3`、`3/3`)且期间**绝不**调用 `openProjectionStore`;第 4 次回退成功打开自有句柄并返回正常结果;`resetCollectorReadState()` 后门面就绪能拿回门面。全量 **1904 例 / 190 文件**,typecheck/lint/build/build:smoke/smoke 全 0。
|
|
8
|
+
- **验证口径提示**:宿主外脚本按设计走的是**回退路径**(会开自己的只读句柄),因此「脚本里出现自开库」不是缺陷;宿主内的判据是日志无「背景窗拉取失败」且蒸馏/`conversation_search` 出真数据。
|
|
9
|
+
|
|
10
|
+
## [0.26.23] — 2026-11-02
|
|
11
|
+
|
|
12
|
+
- **方案 B:DRSC 读面改走 collector 的正式门面(`drsReadFacade`)**。`0.26.21` 的进程内直调虽然绕开了宿主鉴权,但代价是**耦合 collector 的五个内部导出名**(`openProjectionStore`/`createQueryContext`/`search`/`sessionDetail`/`listSessions`),并且**自己另开一份投影库**——多占一份常驻 `summaries` 内存与一个读事务。现在 collector `0.2.5` 导出 `drsReadFacade()`,返回绑它**自己活 QueryContext** 的只读对象;本仓优先用这个门面,只认一个稳定符号,也不再开第二份库。
|
|
13
|
+
- **三条路径的优先级与降级**(`src/clients/drsc-inproc.ts`):① collector ≥0.2.5 且库已就绪 → 走门面;② collector 是新版本但库还在 `setImmediate` 初始化 → **本轮抛可重试错、且不缓存**,让下一次读重取门面。这一点是本轮自查出的真设计缺陷:若此时回退自开库,会把自开库结果**永久缓存**,把「优先用门面」变成只在首轮赌运气。降级走既有链路(背景窗当轮空窗、工具面显式报错),语义与旧 HTTP 遇 `503 initializing` 一致;③ collector 是旧版(无该导出)→ 回退自开只读库(保留 `0.26.21` 行为)。
|
|
14
|
+
- **参数映射不分叉**:门面对象被适配成与兼容路径同一个「模块形」,所以 `op`/`q`/`tail`/`limit`/`redact` 的解析口径、非法值整键缺席、`session` 缺 id 返回空行等语义只有一份实现,两条路径共用。
|
|
15
|
+
- **依赖下限抬到 `>=0.2.5 <0.3.0`**(`package.json` dependencies;`dsh.plugin.json` 的 `pluginDependencies` 只列包名不带区间,无需改)。低于该版仍可用,只是走兼容路径。
|
|
16
|
+
- **测试**:`tests/drsc-inproc.test.ts` 新增 3 例——门面可用时绝不调用 `openProjectionStore`;未就绪时报错且不缓存、对端就绪后下一次读拿到门面;旧版 collector 仍回退自开库。全量 **1904 例 / 190 文件**,typecheck/lint/build/build:smoke/smoke 全 0。
|
|
17
|
+
|
|
3
18
|
## [0.26.22] — 2026-11-02
|
|
4
19
|
|
|
5
20
|
- **#27 真机通过**:用户在 desktop 上 `register(work/dsh-host)` → `list` 显示「**现有 59 条**」,与注册前自生长清单里的 `work/dsh-host(59)` 逐位一致;`?? 0` 分支也未被误伤(有绑定即显示真数)。
|
|
@@ -38,7 +38,7 @@ export interface CollectorReadModule {
|
|
|
38
38
|
}
|
|
39
39
|
/** 注入面(单测);缺省按裸名动态 import。 */
|
|
40
40
|
export type CollectorLoader = () => Promise<CollectorReadModule>;
|
|
41
|
-
/** 清掉缓存(单测用;也供 collector
|
|
41
|
+
/** 清掉缓存(单测用;也供 collector 换装后强制重取)。 */
|
|
42
42
|
export declare function resetCollectorReadState(): void;
|
|
43
43
|
/**
|
|
44
44
|
* 执行一次读,返回与 HTTP 包络同形的对象(`ok:true` + 各 op 的字段)。
|
|
@@ -22,37 +22,80 @@
|
|
|
22
22
|
const COLLECTOR_PACKAGE = 'dsh-reference-session-collector';
|
|
23
23
|
const defaultLoader = async () => (await import(/* @vite-ignore */ COLLECTOR_PACKAGE));
|
|
24
24
|
let modulePromise;
|
|
25
|
-
let queryContext;
|
|
26
25
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
26
|
+
* 对端已升版但库未就绪时的重试预算。不能无限重试:宿主**外**的脚本、以及
|
|
27
|
+
* 「collector 装了但未装载」的部署里,drsReadFacade() 永远返回 undefined,
|
|
28
|
+
* 死等会把读面永久堵死(比回退更糟)。也不能一上来就回退:那会把自开库
|
|
29
|
+
* 结果永久缓存,把“优先用门面”变成只在首轮赌运气。
|
|
30
|
+
*/
|
|
31
|
+
const FACADE_RETRY_BUDGET = 3;
|
|
32
|
+
let facadeMisses = 0;
|
|
33
|
+
/**
|
|
34
|
+
* 兼容路径(旧 collector)的只读句柄(`openProjectionStore` 的返回值)。常驻 summaries
|
|
35
|
+
* 是内存 Map,每请求重开等于重扫全表,故只开一次;undefined 是合法的未开哨兵。
|
|
30
36
|
*/
|
|
31
37
|
let storeHandle;
|
|
32
|
-
/** 清掉缓存(单测用;也供 collector
|
|
38
|
+
/** 清掉缓存(单测用;也供 collector 换装后强制重取)。 */
|
|
33
39
|
export function resetCollectorReadState() {
|
|
34
40
|
modulePromise = undefined;
|
|
35
|
-
queryContext = undefined;
|
|
36
41
|
storeHandle = undefined;
|
|
42
|
+
facadeMisses = 0;
|
|
43
|
+
}
|
|
44
|
+
/** 把门面对象适配成本模块统一的「模块形」,以便与兼容路径共用同一套参数映射。
|
|
45
|
+
* 前三个成员在门面路径上永不会被调用:moduleOf 命中门面后直接返回,不走进兼容分支。
|
|
46
|
+
*/
|
|
47
|
+
function fromFacade(facade) {
|
|
48
|
+
const unused = () => {
|
|
49
|
+
throw new Error('门面路径不应自开投影库');
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
resolveIndexDir: unused,
|
|
53
|
+
openProjectionStore: unused,
|
|
54
|
+
createQueryContext: unused,
|
|
55
|
+
listSessions: (ctx, offset, limit) => facade.listSessions(offset, limit),
|
|
56
|
+
sessionDetail: (ctx, sessionId, opts) => facade.sessionDetail(sessionId, opts),
|
|
57
|
+
search: (ctx, query, opts) => facade.search(query, opts),
|
|
58
|
+
};
|
|
37
59
|
}
|
|
38
|
-
/**
|
|
60
|
+
/** 取一个可用的「模块形」:优先 collector 门面(>=0.2.5),否则回退自开只读库。 */
|
|
39
61
|
async function moduleOf(load) {
|
|
40
|
-
const pending = (modulePromise ??=
|
|
62
|
+
const pending = (modulePromise ??= (async () => {
|
|
63
|
+
const mod = (await load());
|
|
64
|
+
if (typeof mod.drsReadFacade === 'function') {
|
|
65
|
+
const facade = mod.drsReadFacade();
|
|
66
|
+
// 对端是新版本但库未就绪(collector 的重活推迟在 setImmediate 臂):先给它几次
|
|
67
|
+
// 重试机会(抛可重试错、不缓存 rejection,下一次读重取门面);超过预算就认定
|
|
68
|
+
// 本进程里不会有人 activate 它(宿主外的脚本、或 collector 装了但未装载),
|
|
69
|
+
// 回退自开只读库。两条路都不会读错数据:库是同一个 WAL 文件,自开句柄只读。
|
|
70
|
+
facadeMisses += 1;
|
|
71
|
+
if (facade !== undefined) {
|
|
72
|
+
facadeMisses = 0;
|
|
73
|
+
return fromFacade(facade);
|
|
74
|
+
}
|
|
75
|
+
if (facadeMisses <= FACADE_RETRY_BUDGET) {
|
|
76
|
+
throw new Error(`collector 投影库尚未就绪(第 ${facadeMisses}/${FACADE_RETRY_BUDGET} 次重试)`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return legacyStoreModule(mod);
|
|
80
|
+
})().catch((err) => {
|
|
41
81
|
if (modulePromise === pending)
|
|
42
82
|
modulePromise = undefined;
|
|
43
83
|
throw err;
|
|
44
84
|
}));
|
|
45
85
|
return pending;
|
|
46
86
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return queryContext;
|
|
50
|
-
const mod = await moduleOf(load);
|
|
87
|
+
/** 旧 collector(无 drsReadFacade)的兼容路径:自己开一份只读库。 */
|
|
88
|
+
async function legacyStoreModule(mod) {
|
|
51
89
|
if (storeHandle === undefined) {
|
|
52
90
|
storeHandle = mod.openProjectionStore(mod.resolveIndexDir());
|
|
53
91
|
}
|
|
54
|
-
|
|
55
|
-
return
|
|
92
|
+
const ctx = mod.createQueryContext(storeHandle);
|
|
93
|
+
return {
|
|
94
|
+
...mod,
|
|
95
|
+
listSessions: (_c, offset, limit) => mod.listSessions(ctx, offset, limit),
|
|
96
|
+
sessionDetail: (_c, sessionId, opts) => mod.sessionDetail(ctx, sessionId, opts),
|
|
97
|
+
search: (_c, query, opts) => mod.search(ctx, query, opts),
|
|
98
|
+
};
|
|
56
99
|
}
|
|
57
100
|
/** 把 HTTP 查询串拆成参数表(与 collector 路由同源解析口径:它也只读 searchParams)。 */
|
|
58
101
|
function paramsOf(query) {
|
|
@@ -66,7 +109,6 @@ function paramsOf(query) {
|
|
|
66
109
|
* 把传输从 fetch 换成函数调用,不顺手改任何语义。
|
|
67
110
|
*/
|
|
68
111
|
export async function callCollectorRead(query, load = defaultLoader) {
|
|
69
|
-
const ctx = await contextFor(load);
|
|
70
112
|
const mod = await moduleOf(load);
|
|
71
113
|
const p = paramsOf(query);
|
|
72
114
|
const op = p.get('op') ?? '';
|
|
@@ -76,7 +118,7 @@ export async function callCollectorRead(query, load = defaultLoader) {
|
|
|
76
118
|
const limit = positiveInt(p.get('limit'));
|
|
77
119
|
return {
|
|
78
120
|
ok: true,
|
|
79
|
-
...mod.search(
|
|
121
|
+
...mod.search(READ_SURFACE, p.get('q') ?? '', {
|
|
80
122
|
// 非法/缺省时整个键缺席,而不是传 `limit: undefined` —— `'limit' in opts`
|
|
81
123
|
// 与 `opts.limit === undefined` 不是一回事,对端可能按前者走钳位分支。
|
|
82
124
|
...(limit === undefined ? {} : { limit }),
|
|
@@ -90,7 +132,7 @@ export async function callCollectorRead(query, load = defaultLoader) {
|
|
|
90
132
|
return { ok: true, session: null, rows: [] };
|
|
91
133
|
}
|
|
92
134
|
const tail = positiveInt(p.get('tail'));
|
|
93
|
-
const detail = mod.sessionDetail(
|
|
135
|
+
const detail = mod.sessionDetail(READ_SURFACE, sessionId, {
|
|
94
136
|
// collector 侧:tail 为正整数才切尾窗,键缺席即全量(#14-B 的整会话臂依赖这点)。
|
|
95
137
|
...(tail === undefined ? {} : { tail }),
|
|
96
138
|
redact,
|
|
@@ -101,7 +143,7 @@ export async function callCollectorRead(query, load = defaultLoader) {
|
|
|
101
143
|
// 这两个是位置参数,undefined 与“没传”在 JS 里同义,无需上面对象字面量那套处置。
|
|
102
144
|
const offset = positiveInt(p.get('offset')) ?? 0;
|
|
103
145
|
const limit = positiveInt(p.get('limit'));
|
|
104
|
-
return { ok: true, ...mod.listSessions(
|
|
146
|
+
return { ok: true, ...mod.listSessions(READ_SURFACE, offset, limit) };
|
|
105
147
|
}
|
|
106
148
|
throw new Error(`collector 未知 op: ${op === '' ? '(missing)' : op}`);
|
|
107
149
|
}
|
|
@@ -112,3 +154,5 @@ function positiveInt(raw) {
|
|
|
112
154
|
const n = Number(raw);
|
|
113
155
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined;
|
|
114
156
|
}
|
|
157
|
+
/** 统一形后的模块不再需要真 QueryContext,这个占位只用来占位。 */
|
|
158
|
+
const READ_SURFACE = 'inproc-read-surface';
|
package/dsh.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-prime-memory",
|
|
3
3
|
"name": "dsh-prime-memory",
|
|
4
|
-
"version": "0.26.
|
|
4
|
+
"version": "0.26.24",
|
|
5
5
|
"description": {
|
|
6
6
|
"en": "Layered distilled memory plugin (L0 capture → L1 atomic memory → L2 scenes → L3 persona) with automatic recall injection before each model step.",
|
|
7
7
|
"zh": "L0~L3 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-prime-memory",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.24",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org",
|
|
6
6
|
"access": "public",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"dependencies": {
|
|
99
99
|
"@deepseek-ai/schemastery": "^3.18.4",
|
|
100
100
|
"@node-rs/jieba": "^2.0.2",
|
|
101
|
-
"dsh-reference-session-collector": ">=0.2.
|
|
101
|
+
"dsh-reference-session-collector": ">=0.2.5 <0.3.0",
|
|
102
102
|
"sqlite-vec": "^0.1.7-alpha.2",
|
|
103
103
|
"undici": "^7.29.0"
|
|
104
104
|
},
|