dsh-prime-memory 0.26.20 → 0.26.21
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 +7 -0
- package/dist/clients/drsc-inproc.d.ts +50 -0
- package/dist/clients/drsc-inproc.js +114 -0
- package/dist/clients/drsc.d.ts +15 -28
- package/dist/clients/drsc.js +56 -0
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# 更新日志(Changelog)
|
|
2
2
|
|
|
3
|
+
## [0.26.21] — 2026-11-02
|
|
4
|
+
|
|
5
|
+
- **#20 追加修复:DRSC 读面从 HTTP 改为进程内直调**。桌面宿主在**任何**注册路由之前有一道 pre-dispatch 鉴权(`isAuthenticated` 只认由 `requestAudience(Host)` 派生的签名 cookie,豁免分支仅 `GET /?token=` 与已认证的 `GET /`),而插件服务端 `fetch` 既无 cookie 也无可取用 launch token 的服务面(该字段是宿主 `BrowserAuth` 私有)。真机实测确认:基址修对后请求确实发出、参数形状正确,但仍被回 `401` —— 即上一版只解决了「打错端口」,没解决「打不通」。本版新增 `src/clients/drsc-inproc.ts`,直调 collector 导出的只读查询面(`resolveIndexDir` → `openProjectionStore` → `createQueryContext` → `search` / `sessionDetail` / `listSessions`),全程不碰 `ProjectionSync` 写侧引擎,故不会与 collector 自身的同步器抢水位、也不会多挂一个 interval;索引库为 WAL(实测同目录有 `-wal`/`-shm`),宿主持写连接时本侧只读句柄可并发读,且句柄只开一次(常驻 summaries 是内存 Map,每请求重开等于重扫全表)。
|
|
6
|
+
- **传输分流规则**:只有「显式 HTTP 意图」才走 fetch —— 即配了 `baseUrl`(跨机/自托管 collector)或调用方注入 `fetchImpl`。两者皆未给时走进程内。四个读点仍汇聚在 `getJson`,查询串形状与旧 HTTP 逐字一致,因此字段映射、「`session=null` → 空数组」「limit 钳位」「`redact` 硬臂」这些口径全部不变;进程内读失败同样包成 `DrscClientError`,调用方的「显式报错 vs 空窗降级」分面处理不受影响。
|
|
7
|
+
- **真数据端到端自证**:以真实 collector 模块 + 真实索引库驱动,直调与走适配器的 `tail:4` / 全量两条臂返回**逐位一致的 seq 序列**(4 行 / 1770 行);`DrscClient.sessionTail` 得到 4 行且 `role/kind/time/text` 映射正常,`search('端口')` 命中 3 条含 snippet,`listSessions` total=103。新增 `tests/drsc-inproc.test.ts`(参数映射 + 句柄单次开启 + 载入失败不缓存 rejection 以自愈)与 `drsc-client.test.ts` 的传输分流用例,合计 12 例;全量 **1898 例 / 189 文件**。
|
|
8
|
+
- **过程中修掉两处自身缺陷**:① `{ limit: undefined }` 会让 `'limit' in opts` 为真,而 collector 可能据此走钳位分支 —— 改为非法/缺省时整个键缺席;② 适配器误把 `openProjectionStore` 的返回值丢弃、试图从模块上读一个不存在的属性取句柄 —— 已改回持有返回值。
|
|
9
|
+
|
|
3
10
|
## [0.26.20] — 2026-11-02
|
|
4
11
|
|
|
5
12
|
- **内部命名与计划对齐**:共用取数助手按其计划原名 `mergeRoomCatalog()` 落地(0.26.18/0.26.19 线上产物里叫 `roomCountsBySlug()`,功能与形状不变,纯内部导出名,无外部消费方)。同时重写该函数的文档注释:原先「四处同形中三处已正确」的表述不准确,现按实证写明 rooms-export / room-admin list / 候选词表三处已接,而 rooms-get 端点除计数外还要把注册表 `source`/`label` 覆盖回已存在条目,属不同形状,有意不接(该处就地留了说明注释)。
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DRSC 进程内读适配器(issue #20 的追加修复)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要换掉 HTTP:桌面宿主在**任何**注册路由之前有一道 pre-dispatch 鉴权
|
|
5
|
+
* (`isAuthenticated` 只认由 `requestAudience(Host)` 派生的签名 cookie,豁免分支
|
|
6
|
+
* 只有 `GET /?token=` 与已认证的 `GET /`)。插件服务端 `fetch` 没有 cookie,也没有
|
|
7
|
+
* 任何可取用该 token 的服务面(`launchToken` 是宿主 `BrowserAuth` 的私有字段),
|
|
8
|
+
* 于是基址修对了也只会拿到 401 —— 真机已实测(见 #20 评论)。
|
|
9
|
+
*
|
|
10
|
+
* 本模块改为直调 collector 导出的**只读查询面**:
|
|
11
|
+
* resolveIndexDir() → openProjectionStore(dir) → createQueryContext(store)
|
|
12
|
+
* → search / sessionDetail / listSessions
|
|
13
|
+
* 全程不碰 `ProjectionSync`(写侧引擎),因此不会与 collector 自己的同步器抢水位、
|
|
14
|
+
* 也不会多挂一个 interval。索引库是 WAL(实测同目录有 -wal/-shm),宿主持写连接时
|
|
15
|
+
* 本侧只读句柄可并发读。
|
|
16
|
+
*
|
|
17
|
+
* 包名用变量做动态 import:本仓 devDeps 没有该包,静态 import 会让 typecheck 挂;
|
|
18
|
+
* 运行期它由 profile 顶层 node_modules 提供(本插件已把它列为 dependency 与
|
|
19
|
+
* pluginDependencies,装载序在前,故首次读时必已就位)。
|
|
20
|
+
*/
|
|
21
|
+
/** 只用到读面,形状按 collector 的 d.ts 手工声明,不引它的类型(避免跨包类型依赖)。 */
|
|
22
|
+
export interface CollectorReadModule {
|
|
23
|
+
resolveIndexDir: (explicit?: string, env?: NodeJS.ProcessEnv) => string;
|
|
24
|
+
openProjectionStore: (dir: string) => unknown;
|
|
25
|
+
createQueryContext: (store: unknown) => unknown;
|
|
26
|
+
search: (ctx: unknown, query: string, opts?: {
|
|
27
|
+
session?: string;
|
|
28
|
+
limit?: number;
|
|
29
|
+
redact?: boolean;
|
|
30
|
+
}) => Record<string, unknown>;
|
|
31
|
+
sessionDetail: (ctx: unknown, sessionId: string, opts?: {
|
|
32
|
+
turn?: string;
|
|
33
|
+
fields?: string;
|
|
34
|
+
tail?: number;
|
|
35
|
+
redact?: boolean;
|
|
36
|
+
}) => Record<string, unknown>;
|
|
37
|
+
listSessions: (ctx: unknown, offset?: number, limit?: number) => Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/** 注入面(单测);缺省按裸名动态 import。 */
|
|
40
|
+
export type CollectorLoader = () => Promise<CollectorReadModule>;
|
|
41
|
+
/** 清掉缓存(单测用;也供 collector 换装后强制重开)。 */
|
|
42
|
+
export declare function resetCollectorReadState(): void;
|
|
43
|
+
/**
|
|
44
|
+
* 执行一次读,返回与 HTTP 包络同形的对象(`ok:true` + 各 op 的字段)。
|
|
45
|
+
*
|
|
46
|
+
* 保持同形是有意的:`DrscClient` 的四个读点全部经由 `getJson`,字段映射、
|
|
47
|
+
* 「session=null → 空数组」「limit 钳位」这些口径都已在那边测过;这里只负责
|
|
48
|
+
* 把传输从 fetch 换成函数调用,不顺手改任何语义。
|
|
49
|
+
*/
|
|
50
|
+
export declare function callCollectorRead(query: string, load?: CollectorLoader): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DRSC 进程内读适配器(issue #20 的追加修复)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要换掉 HTTP:桌面宿主在**任何**注册路由之前有一道 pre-dispatch 鉴权
|
|
5
|
+
* (`isAuthenticated` 只认由 `requestAudience(Host)` 派生的签名 cookie,豁免分支
|
|
6
|
+
* 只有 `GET /?token=` 与已认证的 `GET /`)。插件服务端 `fetch` 没有 cookie,也没有
|
|
7
|
+
* 任何可取用该 token 的服务面(`launchToken` 是宿主 `BrowserAuth` 的私有字段),
|
|
8
|
+
* 于是基址修对了也只会拿到 401 —— 真机已实测(见 #20 评论)。
|
|
9
|
+
*
|
|
10
|
+
* 本模块改为直调 collector 导出的**只读查询面**:
|
|
11
|
+
* resolveIndexDir() → openProjectionStore(dir) → createQueryContext(store)
|
|
12
|
+
* → search / sessionDetail / listSessions
|
|
13
|
+
* 全程不碰 `ProjectionSync`(写侧引擎),因此不会与 collector 自己的同步器抢水位、
|
|
14
|
+
* 也不会多挂一个 interval。索引库是 WAL(实测同目录有 -wal/-shm),宿主持写连接时
|
|
15
|
+
* 本侧只读句柄可并发读。
|
|
16
|
+
*
|
|
17
|
+
* 包名用变量做动态 import:本仓 devDeps 没有该包,静态 import 会让 typecheck 挂;
|
|
18
|
+
* 运行期它由 profile 顶层 node_modules 提供(本插件已把它列为 dependency 与
|
|
19
|
+
* pluginDependencies,装载序在前,故首次读时必已就位)。
|
|
20
|
+
*/
|
|
21
|
+
/** collector 包入口(裸名,由 profile 顶层 node_modules 解析)。 */
|
|
22
|
+
const COLLECTOR_PACKAGE = 'dsh-reference-session-collector';
|
|
23
|
+
const defaultLoader = async () => (await import(/* @vite-ignore */ COLLECTOR_PACKAGE));
|
|
24
|
+
let modulePromise;
|
|
25
|
+
let queryContext;
|
|
26
|
+
/**
|
|
27
|
+
* 只读句柄(`openProjectionStore` 的返回值)。常驻 summaries 是内存 Map,
|
|
28
|
+
* 每请求重开等于重扫全表,故只开一次;undefined 是合法的未开哨兵
|
|
29
|
+
* (句柄真为 undefined 时 createQueryContext 会在首读即报错,不会静默重开)。
|
|
30
|
+
*/
|
|
31
|
+
let storeHandle;
|
|
32
|
+
/** 清掉缓存(单测用;也供 collector 换装后强制重开)。 */
|
|
33
|
+
export function resetCollectorReadState() {
|
|
34
|
+
modulePromise = undefined;
|
|
35
|
+
queryContext = undefined;
|
|
36
|
+
storeHandle = undefined;
|
|
37
|
+
}
|
|
38
|
+
/** 载入并缓存 collector 模块;失败不缓存 rejection,否则一次抖动会永久打死后续所有读。 */
|
|
39
|
+
async function moduleOf(load) {
|
|
40
|
+
const pending = (modulePromise ??= load().catch((err) => {
|
|
41
|
+
if (modulePromise === pending)
|
|
42
|
+
modulePromise = undefined;
|
|
43
|
+
throw err;
|
|
44
|
+
}));
|
|
45
|
+
return pending;
|
|
46
|
+
}
|
|
47
|
+
async function contextFor(load) {
|
|
48
|
+
if (queryContext !== undefined)
|
|
49
|
+
return queryContext;
|
|
50
|
+
const mod = await moduleOf(load);
|
|
51
|
+
if (storeHandle === undefined) {
|
|
52
|
+
storeHandle = mod.openProjectionStore(mod.resolveIndexDir());
|
|
53
|
+
}
|
|
54
|
+
queryContext = mod.createQueryContext(storeHandle);
|
|
55
|
+
return queryContext;
|
|
56
|
+
}
|
|
57
|
+
/** 把 HTTP 查询串拆成参数表(与 collector 路由同源解析口径:它也只读 searchParams)。 */
|
|
58
|
+
function paramsOf(query) {
|
|
59
|
+
return new URLSearchParams(query.startsWith('?') ? query.slice(1) : query);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 执行一次读,返回与 HTTP 包络同形的对象(`ok:true` + 各 op 的字段)。
|
|
63
|
+
*
|
|
64
|
+
* 保持同形是有意的:`DrscClient` 的四个读点全部经由 `getJson`,字段映射、
|
|
65
|
+
* 「session=null → 空数组」「limit 钳位」这些口径都已在那边测过;这里只负责
|
|
66
|
+
* 把传输从 fetch 换成函数调用,不顺手改任何语义。
|
|
67
|
+
*/
|
|
68
|
+
export async function callCollectorRead(query, load = defaultLoader) {
|
|
69
|
+
const ctx = await contextFor(load);
|
|
70
|
+
const mod = await moduleOf(load);
|
|
71
|
+
const p = paramsOf(query);
|
|
72
|
+
const op = p.get('op') ?? '';
|
|
73
|
+
// collector 的 redact 臂:HTTP 侧是 `redact=1`;缺省是原文臂。我们所有读点都硬要求脱敏。
|
|
74
|
+
const redact = p.get('redact') !== null && p.get('redact') !== '0';
|
|
75
|
+
if (op === 'search') {
|
|
76
|
+
const limit = positiveInt(p.get('limit'));
|
|
77
|
+
return {
|
|
78
|
+
ok: true,
|
|
79
|
+
...mod.search(ctx, p.get('q') ?? '', {
|
|
80
|
+
// 非法/缺省时整个键缺席,而不是传 `limit: undefined` —— `'limit' in opts`
|
|
81
|
+
// 与 `opts.limit === undefined` 不是一回事,对端可能按前者走钳位分支。
|
|
82
|
+
...(limit === undefined ? {} : { limit }),
|
|
83
|
+
redact,
|
|
84
|
+
}),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (op === 'session') {
|
|
88
|
+
const sessionId = p.get('session');
|
|
89
|
+
if (sessionId === null || sessionId === '') {
|
|
90
|
+
return { ok: true, session: null, rows: [] };
|
|
91
|
+
}
|
|
92
|
+
const tail = positiveInt(p.get('tail'));
|
|
93
|
+
const detail = mod.sessionDetail(ctx, sessionId, {
|
|
94
|
+
// collector 侧:tail 为正整数才切尾窗,键缺席即全量(#14-B 的整会话臂依赖这点)。
|
|
95
|
+
...(tail === undefined ? {} : { tail }),
|
|
96
|
+
redact,
|
|
97
|
+
});
|
|
98
|
+
return { ok: true, ...detail };
|
|
99
|
+
}
|
|
100
|
+
if (op === 'sessions') {
|
|
101
|
+
// 这两个是位置参数,undefined 与“没传”在 JS 里同义,无需上面对象字面量那套处置。
|
|
102
|
+
const offset = positiveInt(p.get('offset')) ?? 0;
|
|
103
|
+
const limit = positiveInt(p.get('limit'));
|
|
104
|
+
return { ok: true, ...mod.listSessions(ctx, offset, limit) };
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`collector 未知 op: ${op === '' ? '(missing)' : op}`);
|
|
107
|
+
}
|
|
108
|
+
/** 只接受「正整数」形式的参数;非法/缺省一律 undefined(交由调用点决定缺席语义)。 */
|
|
109
|
+
function positiveInt(raw) {
|
|
110
|
+
if (raw === null || raw.trim() === '')
|
|
111
|
+
return undefined;
|
|
112
|
+
const n = Number(raw);
|
|
113
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined;
|
|
114
|
+
}
|
package/dist/clients/drsc.d.ts
CHANGED
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DrscClient(T8.4 读切,drsc-redact-memory-wiring task_18):memory 读历史
|
|
3
|
-
* 面的 dsh-reference-session-collector HTTP 客户端。
|
|
4
|
-
*
|
|
5
|
-
* 两个读点(R7):
|
|
6
|
-
* - 蒸馏背景窗(recentBySession 等价供给)→ `op=session&tail=N&redact=1`
|
|
7
|
-
* (collector task_11 尾窗升序 + task_12 redact 臂);
|
|
8
|
-
* - 工具面 conversation_search → `op=search&redact=1`(命中基于原文 fts,
|
|
9
|
-
* 返回 snippet 取脱敏锚定文本)。
|
|
10
|
-
*
|
|
11
|
-
* 加上 issue #14-B 的**重建事实源**:`op=sessions` 枚举 + `op=session`(**不带 tail**)
|
|
12
|
-
* 逐会话拉全量——对全量重建来说“尾窗”不够用,而 collector 侧不带 tail 即 `ORDER BY seq ASC`
|
|
13
|
-
* 无 LIMIT。此路径同样恒带 `redact=1`。
|
|
14
|
-
*
|
|
15
|
-
* 硬依赖纪律(对齐 T7.3 裁定):collector 缺席/超时/非 2xx/ok=false 一律抛
|
|
16
|
-
* {@link DrscClientError}——**绝不回退本地 l0**(表已退役,无退路);调用方
|
|
17
|
-
* 分口径处理:蒸馏背景窗空窗降级(runner),工具面显式报错(tools)。
|
|
18
|
-
*
|
|
19
|
-
* 同源 Host 解析(steward drs-source 的 resolveDrsBaseUrl 同款):
|
|
20
|
-
* 显式 baseUrl > 首个 trustedHosts 条目(补 http://)> 宿主实际监听 origin
|
|
21
|
-
* (issue #20:Desktop App 走随机端口,写死 8080 永远对不上)> `http://127.0.0.1:8080`
|
|
22
|
-
* (宿主缺省口)。Host 头**只回显被选作基址的那个 trustedHosts 条目**:collector
|
|
23
|
-
* 信任门是精确匹配,条目省略端口而 URL 带端口会失配——所以基址来自 origin 兜底时
|
|
24
|
-
* 索性不回显(等价于旧版 trustedHosts 为空时的形状),而不是拿另一个 authority 去撞门。
|
|
25
|
-
* trustedHosts / origin 均惰性读取(webStartup、webServer 可能晚于本插件就绪),
|
|
26
|
-
* 每次请求现算,无缓存陈旧面。
|
|
27
|
-
*/
|
|
28
1
|
import type { ConversationMessage } from '../types.js';
|
|
29
2
|
/** collector API 路径(与其 DRS_API_PATH 字面一致;不跨包 import)。 */
|
|
30
3
|
export declare const DRSC_API_PATH = "/api/dsh-reference-session-collector";
|
|
@@ -105,8 +78,17 @@ export interface DrscClientOptions {
|
|
|
105
78
|
* 下线,故与 trustedHosts 同款每次现取;取不到即返回 undefined → 落回 8080 老行为。
|
|
106
79
|
*/
|
|
107
80
|
origin?: () => string | undefined;
|
|
108
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* 注入面(单测);缺省全局 fetch。**显式传入即表示要走 HTTP**(见 getJson 分流)。
|
|
83
|
+
*/
|
|
109
84
|
fetchImpl?: typeof fetch;
|
|
85
|
+
/**
|
|
86
|
+
* 进程内读面(issue #20 追加修复,缺省 {@link callCollectorRead}):直调
|
|
87
|
+
* collector 导出的只读查询函数。宿主对所有注册路由有 pre-dispatch 鉴权
|
|
88
|
+
* (插件服务端无 cookie 可用,真机实测 401),HTTP 路在桌面宿主上走不通;
|
|
89
|
+
* 只有显式给了 `baseUrl`(跨机/外部 collector)时才保留 HTTP 退路。
|
|
90
|
+
*/
|
|
91
|
+
read?: (query: string) => Promise<Record<string, unknown>>;
|
|
110
92
|
/** 单请求超时 ms;缺省 {@link DRSC_DEFAULT_TIMEOUT_MS}。 */
|
|
111
93
|
timeoutMs?: number;
|
|
112
94
|
}
|
|
@@ -119,6 +101,11 @@ export declare class DrscClient {
|
|
|
119
101
|
private base;
|
|
120
102
|
/** origin 探针失败(服务未就绪/属性读抛)不得影响请求:视作取不到,落老行为。 */
|
|
121
103
|
private safeOrigin;
|
|
104
|
+
/**
|
|
105
|
+
* 一次读:优先进程内直调,失败/缺席一律包成 DrscClientError(与 HTTP 路同口径,
|
|
106
|
+
* 调用方的「显式报错 vs 空窗降级」分面处理不变)。
|
|
107
|
+
*/
|
|
108
|
+
private readInproc;
|
|
122
109
|
/** 一个 GET 的 JSON 包络(非 2xx / ok!==true / 超时 / 断网一律抛 DrscClientError)。 */
|
|
123
110
|
private getJson;
|
|
124
111
|
/**
|
package/dist/clients/drsc.js
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DrscClient(T8.4 读切,drsc-redact-memory-wiring task_18):memory 读历史
|
|
3
|
+
* 面的 dsh-reference-session-collector HTTP 客户端。
|
|
4
|
+
*
|
|
5
|
+
* 两个读点(R7):
|
|
6
|
+
* - 蒸馏背景窗(recentBySession 等价供给)→ `op=session&tail=N&redact=1`
|
|
7
|
+
* (collector task_11 尾窗升序 + task_12 redact 臂);
|
|
8
|
+
* - 工具面 conversation_search → `op=search&redact=1`(命中基于原文 fts,
|
|
9
|
+
* 返回 snippet 取脱敏锚定文本)。
|
|
10
|
+
*
|
|
11
|
+
* 加上 issue #14-B 的**重建事实源**:`op=sessions` 枚举 + `op=session`(**不带 tail**)
|
|
12
|
+
* 逐会话拉全量——对全量重建来说“尾窗”不够用,而 collector 侧不带 tail 即 `ORDER BY seq ASC`
|
|
13
|
+
* 无 LIMIT。此路径同样恒带 `redact=1`。
|
|
14
|
+
*
|
|
15
|
+
* 硬依赖纪律(对齐 T7.3 裁定):collector 缺席/超时/非 2xx/ok=false 一律抛
|
|
16
|
+
* {@link DrscClientError}——**绝不回退本地 l0**(表已退役,无退路);调用方
|
|
17
|
+
* 分口径处理:蒸馏背景窗空窗降级(runner),工具面显式报错(tools)。
|
|
18
|
+
*
|
|
19
|
+
* 同源 Host 解析(steward drs-source 的 resolveDrsBaseUrl 同款):
|
|
20
|
+
* 显式 baseUrl > 首个 trustedHosts 条目(补 http://)> 宿主实际监听 origin
|
|
21
|
+
* (issue #20:Desktop App 走随机端口,写死 8080 永远对不上)> `http://127.0.0.1:8080`
|
|
22
|
+
* (宿主缺省口)。Host 头**只回显被选作基址的那个 trustedHosts 条目**:collector
|
|
23
|
+
* 信任门是精确匹配,条目省略端口而 URL 带端口会失配——所以基址来自 origin 兜底时
|
|
24
|
+
* 索性不回显(等价于旧版 trustedHosts 为空时的形状),而不是拿另一个 authority 去撞门。
|
|
25
|
+
* trustedHosts / origin 均惰性读取(webStartup、webServer 可能晚于本插件就绪),
|
|
26
|
+
* 每次请求现算,无缓存陈旧面。
|
|
27
|
+
*/
|
|
28
|
+
import { callCollectorRead } from './drsc-inproc.js';
|
|
1
29
|
/** collector API 路径(与其 DRS_API_PATH 字面一致;不跨包 import)。 */
|
|
2
30
|
export const DRSC_API_PATH = '/api/dsh-reference-session-collector';
|
|
3
31
|
/** 客户端缺省超时:loopback 正常 <10ms,5s 是给冷启动 GC/断点的天花板。 */
|
|
@@ -125,8 +153,36 @@ export class DrscClient {
|
|
|
125
153
|
return undefined;
|
|
126
154
|
}
|
|
127
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* 一次读:优先进程内直调,失败/缺席一律包成 DrscClientError(与 HTTP 路同口径,
|
|
158
|
+
* 调用方的「显式报错 vs 空窗降级」分面处理不变)。
|
|
159
|
+
*/
|
|
160
|
+
async readInproc(query) {
|
|
161
|
+
const read = this.options.read ?? callCollectorRead;
|
|
162
|
+
try {
|
|
163
|
+
const body = await read(query);
|
|
164
|
+
// body 已是 Record<string, unknown>,不必也不应再转 DrscEnvelope(TS2352)。
|
|
165
|
+
if (body === null || typeof body !== 'object' || body.ok !== true) {
|
|
166
|
+
throw new Error('返回包络缺 ok:true');
|
|
167
|
+
}
|
|
168
|
+
return body;
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
if (err instanceof DrscClientError)
|
|
172
|
+
throw err;
|
|
173
|
+
throw new DrscClientError(`collector 进程内读失败(${query}): ${err instanceof Error ? err.message : String(err)}`, {
|
|
174
|
+
cause: err,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
128
178
|
/** 一个 GET 的 JSON 包络(非 2xx / ok!==true / 超时 / 断网一律抛 DrscClientError)。 */
|
|
129
179
|
async getJson(query) {
|
|
180
|
+
// 分流规则:只有「显式 HTTP 意图」才走 fetch —— 即配了 baseUrl(跨机/自托管
|
|
181
|
+
// collector)或调用方自己注入了 fetchImpl(单测面)。两者都没给时走进程内:
|
|
182
|
+
// 桌面宿主上插件服务端的 HTTP 必被 pre-dispatch 鉴权拦成 401(#20 真机实证)。
|
|
183
|
+
const explicit = this.options.baseUrl?.trim() ?? '';
|
|
184
|
+
if (explicit === '' && this.options.fetchImpl === undefined)
|
|
185
|
+
return this.readInproc(query);
|
|
130
186
|
const { url, hostHeader } = this.base();
|
|
131
187
|
let response;
|
|
132
188
|
try {
|
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.21",
|
|
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 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
|