dsh-prime-memory 0.26.0 → 0.26.2
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.en.md +20 -0
- package/CHANGELOG.ja.md +20 -0
- package/CHANGELOG.ko.md +20 -0
- package/CHANGELOG.md +24 -0
- package/README.de.md +1 -1
- package/README.en.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.it.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/dist/client.js +73 -16
- package/dist/config.d.ts +9 -0
- package/dist/config.js +6 -0
- package/dist/contract.d.ts +16 -2
- package/dist/index.js +3 -3
- package/dist/keyword-backfill.d.ts +17 -1
- package/dist/keyword-backfill.js +72 -5
- package/dist/llm.d.ts +9 -0
- package/dist/llm.js +7 -4
- package/dist/memport/format.js +1 -1
- package/dist/pipeline/md-sync.js +1 -1
- package/dist/prompts/examples.js +1 -1
- package/dist/redistill-backfill.d.ts +16 -1
- package/dist/redistill-backfill.js +74 -6
- package/dist/room-review.js +2 -0
- package/dist/settings.js +2 -0
- package/dist/stats.js +25 -5
- package/dist/store/l1-snapshot.js +1 -1
- package/dist/tools/index.d.ts +10 -1
- package/dist/tools/index.js +7 -1
- package/dist/tools/skills.d.ts +7 -4
- package/dist/tools/skills.js +9 -11
- package/dist/util/md-name.js +1 -0
- package/dist/util/md-normalize.js +1 -1
- package/dist/wing-backfill.d.ts +10 -1
- package/dist/wing-backfill.js +62 -5
- package/dsh.plugin.json +2 -2
- package/package.json +11 -11
package/dist/keyword-backfill.js
CHANGED
|
@@ -29,7 +29,42 @@ const state = {
|
|
|
29
29
|
running: false, updated: 0, failed: 0,
|
|
30
30
|
chunk: 1, candidates: 0, processed: 0,
|
|
31
31
|
scanning: false, batchesDone: 0, batchTotal: 0, concurrency: DEFAULT_CONCURRENCY, deferred: 0,
|
|
32
|
+
cancelled: false, scanned: 0, scanTotal: 0,
|
|
32
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* 人工中止信号。置位后:①池子不再派新活;②已发出的请求先拿**宽限**(cancelGraceMs)
|
|
36
|
+
* 自然收工(token 不白费、已写回的全入账);③**宽限到期强杀**在飞请求——
|
|
37
|
+
* 没有③的话「中止」最坏要等满 callTimeoutMs(240s) 才生效,等于没中止。
|
|
38
|
+
*/
|
|
39
|
+
let abortRequested = false;
|
|
40
|
+
/** 本轮在飞请求的强杀器(runOnce 入口新建)。 */
|
|
41
|
+
let runAbort = null;
|
|
42
|
+
/** 中止宽限计时器(一次性)。 */
|
|
43
|
+
let graceTimer = null;
|
|
44
|
+
/** 本轮生效的中止宽限(启动时从 cfg 读定,已钳位)。 */
|
|
45
|
+
let graceMs = 15_000;
|
|
46
|
+
/**
|
|
47
|
+
* 中止宽限钳位。**硬界 30s**(用户定:中止必须在 30s 内强停)——无论 YAML 写了多少,
|
|
48
|
+
* 都不会超过这个值;未配置/非法取默认 15s。
|
|
49
|
+
*/
|
|
50
|
+
function clampGraceMs(raw) {
|
|
51
|
+
const n = typeof raw === 'number' && Number.isFinite(raw) ? Math.round(raw) : 15_000;
|
|
52
|
+
return Math.min(30_000, Math.max(0, n));
|
|
53
|
+
}
|
|
54
|
+
/** 请求中止本轮(空闲时无副作用);返回后中止是**有界**的:最多等 graceMs。 */
|
|
55
|
+
export function cancelKeywordBackfill() {
|
|
56
|
+
if (!state.running)
|
|
57
|
+
return { cancelled: false, running: false };
|
|
58
|
+
abortRequested = true;
|
|
59
|
+
if (graceTimer === null) {
|
|
60
|
+
graceTimer = setTimeout(() => {
|
|
61
|
+
graceTimer = null;
|
|
62
|
+
if (state.running)
|
|
63
|
+
runAbort?.abort();
|
|
64
|
+
}, Math.max(0, graceMs));
|
|
65
|
+
}
|
|
66
|
+
return { cancelled: true, running: true };
|
|
67
|
+
}
|
|
33
68
|
export function keywordBackfillState() {
|
|
34
69
|
return { ...state };
|
|
35
70
|
}
|
|
@@ -61,6 +96,8 @@ export function startKeywordBackfill(deps) {
|
|
|
61
96
|
state.batchesDone = 0;
|
|
62
97
|
state.batchTotal = 0;
|
|
63
98
|
state.deferred = 0;
|
|
99
|
+
state.cancelled = false;
|
|
100
|
+
abortRequested = false;
|
|
64
101
|
state.concurrency = clampConcurrency(deps.cfg.backfill?.concurrency);
|
|
65
102
|
void runKeywordBackfillOnce(deps).finally(() => {
|
|
66
103
|
state.running = false;
|
|
@@ -76,12 +113,21 @@ export function startKeywordBackfill(deps) {
|
|
|
76
113
|
*/
|
|
77
114
|
export async function runKeywordBackfillOnce(deps) {
|
|
78
115
|
const { cfg, backend, logger } = deps;
|
|
116
|
+
// 新一轮入口清中止信号(同 redistill:直调 runOnce 的路径不经过 startX 的复位)。
|
|
117
|
+
abortRequested = false;
|
|
118
|
+
state.cancelled = false;
|
|
119
|
+
if (graceTimer !== null) {
|
|
120
|
+
clearTimeout(graceTimer);
|
|
121
|
+
graceTimer = null;
|
|
122
|
+
}
|
|
123
|
+
runAbort = new AbortController();
|
|
79
124
|
if (!cfg.keyword?.enabled) {
|
|
80
125
|
logger.info('[memory] keyword 回填跳过:keyword.enabled = false(功能关,不回填)');
|
|
81
126
|
return;
|
|
82
127
|
}
|
|
83
128
|
const MAX_RECORDS = cfg.backfill?.maxRecords ?? DEFAULT_MAX_RECORDS;
|
|
84
129
|
const TIME_BUDGET_MS = cfg.backfill?.timeBudgetMs ?? DEFAULT_TIME_BUDGET_MS;
|
|
130
|
+
graceMs = clampGraceMs(cfg.backfill?.cancelGraceMs);
|
|
85
131
|
const LIMIT = clampConcurrency(cfg.backfill?.concurrency);
|
|
86
132
|
state.concurrency = LIMIT;
|
|
87
133
|
const startedAt = Date.now();
|
|
@@ -91,6 +137,8 @@ export async function runKeywordBackfillOnce(deps) {
|
|
|
91
137
|
let scanned = 0;
|
|
92
138
|
state.scanning = true;
|
|
93
139
|
for (let offset = 0;; offset += PAGE) {
|
|
140
|
+
if (abortRequested)
|
|
141
|
+
break; // 中止:扫描段也当场退出(不把最后一页白扫完)
|
|
94
142
|
const page = await backend.allLite(PAGE, offset);
|
|
95
143
|
if (page.length === 0)
|
|
96
144
|
break;
|
|
@@ -101,6 +149,8 @@ export async function runKeywordBackfillOnce(deps) {
|
|
|
101
149
|
}
|
|
102
150
|
scanned += page.length;
|
|
103
151
|
state.candidates = pendingIds.length;
|
|
152
|
+
state.scanned = scanned;
|
|
153
|
+
state.scanTotal = total;
|
|
104
154
|
await yieldLoop();
|
|
105
155
|
}
|
|
106
156
|
state.scanning = false;
|
|
@@ -118,9 +168,11 @@ export async function runKeywordBackfillOnce(deps) {
|
|
|
118
168
|
state.batchesDone++;
|
|
119
169
|
return;
|
|
120
170
|
}
|
|
121
|
-
const labeled = await labelKeywordOne(deps.ctx, cfg, logger, rec, callTimeoutMs);
|
|
171
|
+
const labeled = await labelKeywordOne(deps.ctx, cfg, logger, rec, callTimeoutMs, runAbort?.signal);
|
|
122
172
|
if (!labeled) {
|
|
123
|
-
|
|
173
|
+
// 中止强杀导致的失败不计入 failed(不是模型/网络的错,是中止信号)
|
|
174
|
+
if (!abortRequested)
|
|
175
|
+
state.failed++;
|
|
124
176
|
}
|
|
125
177
|
else {
|
|
126
178
|
const meta = { ...(rec.metadata ?? {}) };
|
|
@@ -150,6 +202,8 @@ export async function runKeywordBackfillOnce(deps) {
|
|
|
150
202
|
let budgetHit = false;
|
|
151
203
|
const work = async () => {
|
|
152
204
|
for (;;) {
|
|
205
|
+
if (abortRequested)
|
|
206
|
+
return; // 中止:不再取新活(在飞的那条自然跑完)
|
|
153
207
|
if (budgetHit || Date.now() - startedAt > TIME_BUDGET_MS) {
|
|
154
208
|
budgetHit = true;
|
|
155
209
|
return;
|
|
@@ -168,12 +222,17 @@ export async function runKeywordBackfillOnce(deps) {
|
|
|
168
222
|
for (;;) {
|
|
169
223
|
const target = Math.max(1, Math.min(readLimit(), batch.length));
|
|
170
224
|
state.concurrency = target; // 面板「并行 N」跟实时值走
|
|
171
|
-
while (!budgetHit && inFlight.size < target && cursor < batch.length)
|
|
225
|
+
while (!budgetHit && !abortRequested && inFlight.size < target && cursor < batch.length)
|
|
172
226
|
spawn();
|
|
173
227
|
if (inFlight.size === 0)
|
|
174
228
|
break;
|
|
175
229
|
await Promise.race(inFlight);
|
|
176
230
|
}
|
|
231
|
+
if (abortRequested) {
|
|
232
|
+
state.cancelled = true;
|
|
233
|
+
state.deferred += Math.max(0, batch.length - state.processed);
|
|
234
|
+
logger.info(`[memory] keyword 回填:已按请求中止(在飞请求跑完即收工),本轮完成 ${state.processed} 条,剩 ${state.deferred} 条留待再触发`);
|
|
235
|
+
}
|
|
177
236
|
if (budgetHit) {
|
|
178
237
|
state.deferred += Math.max(0, batch.length - state.processed);
|
|
179
238
|
logger.info(`[memory] keyword 回填:墙钟预算(${Math.round(TIME_BUDGET_MS / 1000)}s)用尽,累计留待再触发 ${state.deferred} 条`);
|
|
@@ -186,7 +245,9 @@ export async function runKeywordBackfillOnce(deps) {
|
|
|
186
245
|
* 而逐条只花秒级。记账(state.failed 等)全部归调用方——标注器不再动全局状态,
|
|
187
246
|
* 否则同一条会被两处各计一次。
|
|
188
247
|
*/
|
|
189
|
-
export async function labelKeywordOne(ctx, cfg, logger, rec, callTimeoutMs = DEFAULT_CALL_TIMEOUT_MS
|
|
248
|
+
export async function labelKeywordOne(ctx, cfg, logger, rec, callTimeoutMs = DEFAULT_CALL_TIMEOUT_MS,
|
|
249
|
+
/** 本轮中止信号(可选):与单次超时**取或**,宽限到期后由它当场掐断请求。 */
|
|
250
|
+
externalSignal) {
|
|
190
251
|
const user = `记忆:\n标题:${rec.name ?? ''}\n正文:${rec.content.slice(0, 900)}`;
|
|
191
252
|
const t0 = Date.now();
|
|
192
253
|
const secs = () => Math.round((Date.now() - t0) / 10);
|
|
@@ -196,8 +257,14 @@ export async function labelKeywordOne(ctx, cfg, logger, rec, callTimeoutMs = DEF
|
|
|
196
257
|
user,
|
|
197
258
|
maxTokens: LABEL_MAX_TOKENS,
|
|
198
259
|
layer: 'l1-extract',
|
|
260
|
+
// 关键词/情景标注是直答任务:显式关思考,否则思考模型会把 512 预算烧在
|
|
261
|
+
// reasoning 上、正文吐 0 字符(真机 156/162 次 `llm empty output` 皆为此)。
|
|
262
|
+
reasoningEffort: 'off',
|
|
199
263
|
logger,
|
|
200
|
-
signal:
|
|
264
|
+
signal: (() => {
|
|
265
|
+
const t = AbortSignal.timeout(callTimeoutMs > 0 ? callTimeoutMs : DEFAULT_CALL_TIMEOUT_MS);
|
|
266
|
+
return externalSignal ? AbortSignal.any([t, externalSignal]) : t;
|
|
267
|
+
})(),
|
|
201
268
|
});
|
|
202
269
|
logger.debug?.(`[memory] keyword 单条耗时 ${(Date.now() - t0) / 1000}s(id=${rec.id})`);
|
|
203
270
|
const parsed = parseJsonLogged(raw, 'keyword 回填', logger);
|
package/dist/llm.d.ts
CHANGED
|
@@ -17,6 +17,15 @@ export interface LlmCallOptions {
|
|
|
17
17
|
maxTokens?: number;
|
|
18
18
|
temperature?: number;
|
|
19
19
|
signal?: AbortSignal;
|
|
20
|
+
/**
|
|
21
|
+
* **调用级思考档位覆盖**(可选)。非空时优先于路由/层链/全局解析出的档位。
|
|
22
|
+
*
|
|
23
|
+
* 为什么需要:回填批扫(整形/关键词/wing/room)是「重排 + 拎标识」的直答任务,
|
|
24
|
+
* 不需要推理;而思考模型默认开思考,会把整个 maxTokens 预算烧在 reasoning 上、
|
|
25
|
+
* 正文吐 0 字符(真机:156/162 次 `llm empty output` 都是这么来的)。
|
|
26
|
+
* 显式传 `off` 后预算全留给答案。档位仍过一遍模型能力钳制:模型不支持 off 时不传。
|
|
27
|
+
*/
|
|
28
|
+
reasoningEffort?: string;
|
|
20
29
|
/** 诊断日志(缺失时静默,不影响调用) */
|
|
21
30
|
logger?: MemoryLogger;
|
|
22
31
|
}
|
package/dist/llm.js
CHANGED
|
@@ -393,8 +393,10 @@ async function callRoute(ctx, cfg, opts, route) {
|
|
|
393
393
|
const { provider, model } = route;
|
|
394
394
|
const signal = opts.signal ?? AbortSignal.timeout(cfg.llm.timeoutMs);
|
|
395
395
|
// 档位按模型能力决策(跨供应商 effort 兼容):不支持的档位不传 + 告警一次,
|
|
396
|
-
// 空配置 = 自动(模型默认 → high);路由的档位候选已在链解析时定好(条目 > 全局)
|
|
397
|
-
|
|
396
|
+
// 空配置 = 自动(模型默认 → high);路由的档位候选已在链解析时定好(条目 > 全局)。
|
|
397
|
+
// 调用级覆盖(opts.reasoningEffort)优先——批扫靠它关掉思考,避免预算被 reasoning 吃光。
|
|
398
|
+
const routeEffort = opts.reasoningEffort ?? route.effort;
|
|
399
|
+
const effort = await planDistillEffort(ctx, provider, model, routeEffort, opts.logger);
|
|
398
400
|
// 输入预算兜底:任何蒸馏调用的用户 prompt 不超过 maxInputChars
|
|
399
401
|
// (L1 已在数据层分块,这里是 L2/L3 与异常场景的最后一道网)
|
|
400
402
|
const user = opts.user.length > cfg.llm.maxInputChars
|
|
@@ -406,9 +408,10 @@ async function callRoute(ctx, cfg, opts, route) {
|
|
|
406
408
|
// 配置本身就是高档时不再放大(防 ×16 双乘)
|
|
407
409
|
const baseMaxTokens = opts.maxTokens ?? cfg.llm.maxTokens;
|
|
408
410
|
const highTiers = HIGH_EFFORT_TIERS;
|
|
409
|
-
|
|
411
|
+
// 触发档位也跟调用级覆盖走:显式 off 时不得被配置里的高档触发而 ×4。
|
|
412
|
+
const triggerEffort = opts.reasoningEffort ?? (opts.layer
|
|
410
413
|
? layerEffortTrigger(cfg, layerKeyFor(opts.layer))
|
|
411
|
-
: cfg.llm.primaryEffort || cfg.llm.reasoningEffort;
|
|
414
|
+
: cfg.llm.primaryEffort || cfg.llm.reasoningEffort);
|
|
412
415
|
const maxTokens = highTiers.includes(effort.effort) && !highTiers.includes(triggerEffort)
|
|
413
416
|
? layerMaxTokens(baseMaxTokens, 'high')
|
|
414
417
|
: baseMaxTokens;
|
package/dist/memport/format.js
CHANGED
|
@@ -178,7 +178,7 @@ export function parseBundle(text) {
|
|
|
178
178
|
declaredSha: null,
|
|
179
179
|
bodyLines: [],
|
|
180
180
|
};
|
|
181
|
-
const clean = text.replace(
|
|
181
|
+
const clean = text.replace(/^\uFEFF/, '');
|
|
182
182
|
const rawLines = clean.split(/\r?\n/);
|
|
183
183
|
const body = [];
|
|
184
184
|
let seqLine = 0;
|
package/dist/pipeline/md-sync.js
CHANGED
package/dist/prompts/examples.js
CHANGED
|
@@ -83,7 +83,7 @@ export const EXTRACTION_FEW_SHOT = `
|
|
|
83
83
|
${TITLE_GOOD_EXAMPLES.map(renderGoodExample).join('\n\n')}
|
|
84
84
|
|
|
85
85
|
【不要这样】
|
|
86
|
-
${TITLE_BAD_EXAMPLES.map((b) => `- ✗ ${b.title}
|
|
86
|
+
${TITLE_BAD_EXAMPLES.map((b) => `- ✗ ${b.title}\u3000→ ${b.why}`).join('\n')}
|
|
87
87
|
`;
|
|
88
88
|
/**
|
|
89
89
|
* 各类型的"前因后果"要素表(照 G2 `NARRATIVE_CLAUSE` 写,不另造一套定义)。
|
|
@@ -81,7 +81,20 @@ export interface RedistillBackfillState {
|
|
|
81
81
|
concurrency: number;
|
|
82
82
|
/** 预算/上限用尽未处理、留下次的条数。 */
|
|
83
83
|
deferred: number;
|
|
84
|
+
/** 本轮被人工中止(进端点 cancel)——与“预算用尽”分开报文案。 */
|
|
85
|
+
cancelled: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* 扫描期已扫行数(活跃集逐页扫)/ 总行数(本模块拿不到总数,恒 0 = 未知)。
|
|
88
|
+
* 为何要暴露:扫描段原本在面板上只是一个静态「正在扫描…」,分不清在跑还是卡死。
|
|
89
|
+
*/
|
|
90
|
+
scanned: number;
|
|
91
|
+
scanTotal: number;
|
|
84
92
|
}
|
|
93
|
+
/** 请求中止本轮(空闲时无副作用);返回后中止是**有界**的:最多等 graceMs(≤60s)。 */
|
|
94
|
+
export declare function cancelRedistillBackfill(): {
|
|
95
|
+
cancelled: boolean;
|
|
96
|
+
running: boolean;
|
|
97
|
+
};
|
|
85
98
|
export declare function redistillBackfillState(): RedistillBackfillState;
|
|
86
99
|
/**
|
|
87
100
|
* 空心标题检测:某个 `## 节名` 到下一标题之间没有实质文字。
|
|
@@ -130,4 +143,6 @@ export interface ReshapeOneResult {
|
|
|
130
143
|
* 导出不带 ctx/cfg 以外的参数,供单测注入桩 LLM。旧名 `reshapeChunk`(多条一次)已废:
|
|
131
144
|
* 合并请求把爆炸半径从 1 条放大到 20 条,而实测逐条只花 2.6s,合并毫无收益。
|
|
132
145
|
*/
|
|
133
|
-
export declare function reshapeOne(ctx: Context, cfg: MemoryConfig, logger: MemoryLogger, rec: MemoryRecord, callTimeoutMs?: number
|
|
146
|
+
export declare function reshapeOne(ctx: Context, cfg: MemoryConfig, logger: MemoryLogger, rec: MemoryRecord, callTimeoutMs?: number,
|
|
147
|
+
/** 本轮中止信号(可选):与单次超时**取或**,宽限到期后由它当场掐断请求。 */
|
|
148
|
+
externalSignal?: AbortSignal): Promise<ReshapeOneResult | null>;
|
|
@@ -49,7 +49,42 @@ const state = {
|
|
|
49
49
|
running: false, updated: 0, skipped: 0, failed: 0,
|
|
50
50
|
chunk: 1, candidates: 0, processed: 0,
|
|
51
51
|
scanning: false, batchesDone: 0, batchTotal: 0, concurrency: DEFAULT_CONCURRENCY, deferred: 0,
|
|
52
|
+
cancelled: false, scanned: 0, scanTotal: 0,
|
|
52
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* 人工中止信号。置位后:①池子不再派新活;②已发出的请求先拿**宽限**(cancelGraceMs)
|
|
56
|
+
* 自然收工(token 不白费、已写回的全入账);③**宽限到期强杀**在飞请求——
|
|
57
|
+
* 没有③的话「中止」最坏要等满 callTimeoutMs(240s) 才生效,等于没中止。
|
|
58
|
+
*/
|
|
59
|
+
let abortRequested = false;
|
|
60
|
+
/** 本轮在飞请求的强杀器(runOnce 入口新建)。 */
|
|
61
|
+
let runAbort = null;
|
|
62
|
+
/** 中止宽限计时器(一次性)。 */
|
|
63
|
+
let graceTimer = null;
|
|
64
|
+
/** 本轮生效的中止宽限(启动时从 cfg 读定,已钳位)。 */
|
|
65
|
+
let graceMs = 15_000;
|
|
66
|
+
/**
|
|
67
|
+
* 中止宽限钳位。**硬界 30s**(用户定:中止必须在 30s 内强停)——无论 YAML 写了多少,
|
|
68
|
+
* 都不会超过这个值;未配置/非法取默认 15s。
|
|
69
|
+
*/
|
|
70
|
+
function clampGraceMs(raw) {
|
|
71
|
+
const n = typeof raw === 'number' && Number.isFinite(raw) ? Math.round(raw) : 15_000;
|
|
72
|
+
return Math.min(30_000, Math.max(0, n));
|
|
73
|
+
}
|
|
74
|
+
/** 请求中止本轮(空闲时无副作用);返回后中止是**有界**的:最多等 graceMs(≤60s)。 */
|
|
75
|
+
export function cancelRedistillBackfill() {
|
|
76
|
+
if (!state.running)
|
|
77
|
+
return { cancelled: false, running: false };
|
|
78
|
+
abortRequested = true;
|
|
79
|
+
if (graceTimer === null) {
|
|
80
|
+
graceTimer = setTimeout(() => {
|
|
81
|
+
graceTimer = null;
|
|
82
|
+
if (state.running)
|
|
83
|
+
runAbort?.abort();
|
|
84
|
+
}, Math.max(0, graceMs));
|
|
85
|
+
}
|
|
86
|
+
return { cancelled: true, running: true };
|
|
87
|
+
}
|
|
53
88
|
export function redistillBackfillState() {
|
|
54
89
|
return { ...state };
|
|
55
90
|
}
|
|
@@ -150,6 +185,8 @@ export function startRedistillBackfill(deps) {
|
|
|
150
185
|
state.batchesDone = 0;
|
|
151
186
|
state.batchTotal = 0;
|
|
152
187
|
state.deferred = 0;
|
|
188
|
+
state.cancelled = false;
|
|
189
|
+
abortRequested = false;
|
|
153
190
|
state.concurrency = clampConcurrency(deps.cfg.backfill?.concurrency);
|
|
154
191
|
void runRedistillBackfillOnce(deps).finally(() => {
|
|
155
192
|
state.running = false;
|
|
@@ -170,6 +207,15 @@ export function startRedistillBackfill(deps) {
|
|
|
170
207
|
*/
|
|
171
208
|
export async function runRedistillBackfillOnce(deps) {
|
|
172
209
|
const { cfg, l1, logger } = deps;
|
|
210
|
+
// 新一轮入口清中止信号:abortRequested 是模块级,直调 runOnce 的路径(单测/未来调用方)
|
|
211
|
+
// 不经过 startX 的复位,上一轮的中止信号会粘住这一轮。
|
|
212
|
+
abortRequested = false;
|
|
213
|
+
state.cancelled = false;
|
|
214
|
+
if (graceTimer !== null) {
|
|
215
|
+
clearTimeout(graceTimer);
|
|
216
|
+
graceTimer = null;
|
|
217
|
+
}
|
|
218
|
+
runAbort = new AbortController();
|
|
173
219
|
if (!cfg.redistill?.enabled) {
|
|
174
220
|
logger.info('[memory] redistill 回填跳过:redistill.enabled = false(功能关,不整形)');
|
|
175
221
|
return;
|
|
@@ -180,6 +226,7 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
180
226
|
}
|
|
181
227
|
const MAX_RECORDS = cfg.backfill?.maxRecords ?? DEFAULT_MAX_RECORDS;
|
|
182
228
|
const TIME_BUDGET_MS = cfg.backfill?.timeBudgetMs ?? DEFAULT_TIME_BUDGET_MS;
|
|
229
|
+
graceMs = clampGraceMs(cfg.backfill?.cancelGraceMs);
|
|
183
230
|
const LIMIT = clampConcurrency(cfg.backfill?.concurrency);
|
|
184
231
|
state.concurrency = LIMIT;
|
|
185
232
|
const startedAt = Date.now();
|
|
@@ -191,6 +238,8 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
191
238
|
let scanned = 0;
|
|
192
239
|
state.scanning = true;
|
|
193
240
|
for (let offset = 0;; offset += PAGE) {
|
|
241
|
+
if (abortRequested)
|
|
242
|
+
break; // 中止:扫描段也当场退出(不把最后一页白扫完)
|
|
194
243
|
const page = l1.activeForSkeleton(PAGE, offset);
|
|
195
244
|
if (page.length === 0)
|
|
196
245
|
break;
|
|
@@ -205,6 +254,7 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
205
254
|
targets.push(r.id);
|
|
206
255
|
}
|
|
207
256
|
state.candidates = targets.length;
|
|
257
|
+
state.scanned = scanned;
|
|
208
258
|
await yieldLoop();
|
|
209
259
|
}
|
|
210
260
|
state.scanning = false;
|
|
@@ -228,9 +278,11 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
228
278
|
state.batchesDone++;
|
|
229
279
|
return;
|
|
230
280
|
}
|
|
231
|
-
const item = await reshapeOne(deps.ctx, cfg, logger, rec, callTimeoutMs);
|
|
281
|
+
const item = await reshapeOne(deps.ctx, cfg, logger, rec, callTimeoutMs, runAbort?.signal);
|
|
232
282
|
if (!item) {
|
|
233
|
-
|
|
283
|
+
// 中止强杀导致的失败不计入 failed(不是模型/网络的错,是中止信号)
|
|
284
|
+
if (!abortRequested)
|
|
285
|
+
state.failed++;
|
|
234
286
|
}
|
|
235
287
|
else if (reshapePassesGate(rec.type, rec.content, item.content)) {
|
|
236
288
|
const meta = { ...(rec.metadata ?? {}) };
|
|
@@ -280,6 +332,8 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
280
332
|
let budgetHit = false;
|
|
281
333
|
const work = async () => {
|
|
282
334
|
for (;;) {
|
|
335
|
+
if (abortRequested)
|
|
336
|
+
return; // 中止:不再取新活(在飞的那条自然跑完)
|
|
283
337
|
if (budgetHit || Date.now() - startedAt > TIME_BUDGET_MS) {
|
|
284
338
|
budgetHit = true;
|
|
285
339
|
return;
|
|
@@ -298,12 +352,17 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
298
352
|
for (;;) {
|
|
299
353
|
const target = Math.max(1, Math.min(readLimit(), batch.length));
|
|
300
354
|
state.concurrency = target; // 面板「并行 N」跟着实时值走,不再停在启动值上骗人
|
|
301
|
-
while (!budgetHit && inFlight.size < target && cursor < batch.length)
|
|
355
|
+
while (!budgetHit && !abortRequested && inFlight.size < target && cursor < batch.length)
|
|
302
356
|
spawn();
|
|
303
357
|
if (inFlight.size === 0)
|
|
304
358
|
break;
|
|
305
359
|
await Promise.race(inFlight);
|
|
306
360
|
}
|
|
361
|
+
if (abortRequested) {
|
|
362
|
+
state.cancelled = true;
|
|
363
|
+
state.deferred += Math.max(0, batch.length - state.processed);
|
|
364
|
+
logger.info(`[memory] redistill 回填:已按请求中止(在飞请求跑完即收工),本轮完成 ${state.processed} 条,剩 ${state.deferred} 条留待再触发`);
|
|
365
|
+
}
|
|
307
366
|
if (budgetHit) {
|
|
308
367
|
state.deferred += Math.max(0, batch.length - state.processed);
|
|
309
368
|
logger.info(`[memory] redistill 回填:墙钟预算(${Math.round(TIME_BUDGET_MS / 1000)}s)用尽,累计留待再触发 ${state.deferred} 条`);
|
|
@@ -315,7 +374,9 @@ export async function runRedistillBackfillOnce(deps) {
|
|
|
315
374
|
* 导出不带 ctx/cfg 以外的参数,供单测注入桩 LLM。旧名 `reshapeChunk`(多条一次)已废:
|
|
316
375
|
* 合并请求把爆炸半径从 1 条放大到 20 条,而实测逐条只花 2.6s,合并毫无收益。
|
|
317
376
|
*/
|
|
318
|
-
export async function reshapeOne(ctx, cfg, logger, rec, callTimeoutMs = DEFAULT_CALL_TIMEOUT_MS
|
|
377
|
+
export async function reshapeOne(ctx, cfg, logger, rec, callTimeoutMs = DEFAULT_CALL_TIMEOUT_MS,
|
|
378
|
+
/** 本轮中止信号(可选):与单次超时**取或**,宽限到期后由它当场掐断请求。 */
|
|
379
|
+
externalSignal) {
|
|
319
380
|
const user = `记忆:\n类型:${rec.type}\n标题:${rec.name ?? ''}\n当前正文:${rec.content.slice(0, 1200)}`;
|
|
320
381
|
// 每条都记耗时:本轮排查的就是“单条到底要多久”,没这个数就只能猜并发/超时该怎么配。
|
|
321
382
|
const t0 = Date.now();
|
|
@@ -326,9 +387,16 @@ export async function reshapeOne(ctx, cfg, logger, rec, callTimeoutMs = DEFAULT_
|
|
|
326
387
|
user,
|
|
327
388
|
maxTokens: RESHAPE_MAX_TOKENS,
|
|
328
389
|
layer: 'l1-extract',
|
|
390
|
+
// 整形是「重排 + 拎标识」的直答任务:显式关思考,否则思考模型会把预算烧在
|
|
391
|
+
// reasoning 上吐 0 字符(真机 156/162 次 `llm empty output` 皆为此)。
|
|
392
|
+
reasoningEffort: 'off',
|
|
329
393
|
logger,
|
|
330
|
-
// 自己给超时信号:沿用 cfg.llm.timeoutMs(120s)
|
|
331
|
-
|
|
394
|
+
// 自己给超时信号:沿用 cfg.llm.timeoutMs(120s) 会在回退链上被掐死;
|
|
395
|
+
// 再与「本轮中止」取或——宽限到期后由它当场掐断。
|
|
396
|
+
signal: (() => {
|
|
397
|
+
const t = AbortSignal.timeout(callTimeoutMs > 0 ? callTimeoutMs : DEFAULT_CALL_TIMEOUT_MS);
|
|
398
|
+
return externalSignal ? AbortSignal.any([t, externalSignal]) : t;
|
|
399
|
+
})(),
|
|
332
400
|
});
|
|
333
401
|
logger.info(`[memory] redistill 单条耗时 ${secs()}s(id=${rec.id})`);
|
|
334
402
|
const parsed = parseJsonLogged(raw, 'redistill 回填', logger);
|
package/dist/room-review.js
CHANGED
|
@@ -53,6 +53,8 @@ export async function roomCandidateOne(ctx, cfg, logger, rec, roomCatalog) {
|
|
|
53
53
|
user,
|
|
54
54
|
maxTokens: 1024,
|
|
55
55
|
layer: 'l1-extract',
|
|
56
|
+
// 归类是直答任务:显式关思考(不然思考模型把预算烧在 reasoning 上吐 0 字符)。
|
|
57
|
+
reasoningEffort: 'off',
|
|
56
58
|
logger,
|
|
57
59
|
});
|
|
58
60
|
const parsed = parseJsonLogged(raw, 'Room 候选标注', logger);
|
package/dist/settings.js
CHANGED
|
@@ -94,6 +94,7 @@ const ALWAYS_ON = {
|
|
|
94
94
|
toolRuminate: true,
|
|
95
95
|
toolConflict: true,
|
|
96
96
|
toolMutate: true,
|
|
97
|
+
toolSkills: true,
|
|
97
98
|
};
|
|
98
99
|
/** 自带自定义设置页(settings.section 顶层「记忆」分节,client 半挂载):
|
|
99
100
|
* 关掉宿主按 volatile 字段自动生成的表单页,避免同一个插件出现两份设置入口。
|
|
@@ -338,5 +339,6 @@ function resolveSettings(value) {
|
|
|
338
339
|
toolRuminate: v.toolRuminate !== false,
|
|
339
340
|
toolConflict: v.toolConflict !== false,
|
|
340
341
|
toolMutate: v.toolMutate !== false,
|
|
342
|
+
toolSkills: v.toolSkills !== false,
|
|
341
343
|
};
|
|
342
344
|
}
|
package/dist/stats.js
CHANGED
|
@@ -32,9 +32,9 @@ import { WING_CATALOG, WING_FALLBACK } from './types.js';
|
|
|
32
32
|
import { isRoomSlug } from './metadata-validators.js';
|
|
33
33
|
import { InProcMemoryBackend } from './store/memory-backend.js';
|
|
34
34
|
import { isWingCorner } from './store/session-modes.js';
|
|
35
|
-
import { startWingBackfill, wingBackfillState } from './wing-backfill.js';
|
|
36
|
-
import { startKeywordBackfill, keywordBackfillState } from './keyword-backfill.js';
|
|
37
|
-
import { startRedistillBackfill, redistillBackfillState } from './redistill-backfill.js';
|
|
35
|
+
import { startWingBackfill, wingBackfillState, cancelWingBackfill } from './wing-backfill.js';
|
|
36
|
+
import { startKeywordBackfill, keywordBackfillState, cancelKeywordBackfill } from './keyword-backfill.js';
|
|
37
|
+
import { startRedistillBackfill, redistillBackfillState, cancelRedistillBackfill } from './redistill-backfill.js';
|
|
38
38
|
import { errDetail } from './util/filelog.js';
|
|
39
39
|
import { snapshotTokenCost } from './token-cost.js';
|
|
40
40
|
import { computeMaturity } from './util/maturity.js';
|
|
@@ -66,6 +66,9 @@ function backfillProgress(s) {
|
|
|
66
66
|
batchTotal: s.batchTotal,
|
|
67
67
|
concurrency: s.concurrency,
|
|
68
68
|
deferred: s.deferred,
|
|
69
|
+
cancelled: s.cancelled,
|
|
70
|
+
scanned: s.scanned,
|
|
71
|
+
scanTotal: s.scanTotal,
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
/**
|
|
@@ -1156,6 +1159,11 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1156
1159
|
return v;
|
|
1157
1160
|
}
|
|
1158
1161
|
case 'dsh-memory/wing-backfill': {
|
|
1162
|
+
// cancel=true:请求中止本轮(不硬杀在飞请求,跑完当前条即收工)。
|
|
1163
|
+
if (payload?.cancel === true) {
|
|
1164
|
+
cancelWingBackfill();
|
|
1165
|
+
return { started: false, running: wingBackfillState().running };
|
|
1166
|
+
}
|
|
1159
1167
|
// probe=true 只读快照:不启动(UI 离开区域再回来时恢复在途任务用)。
|
|
1160
1168
|
if (payload?.probe === true) {
|
|
1161
1169
|
return { started: false, running: wingBackfillState().running };
|
|
@@ -1172,6 +1180,12 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1172
1180
|
// 门控看生效配置(live.keywordEnabled 覆盖部署 cfg.keyword.enabled);关时不启动。
|
|
1173
1181
|
const ec = effectiveCfg(cfg, live);
|
|
1174
1182
|
const st0 = keywordBackfillState();
|
|
1183
|
+
// cancel=true:请求中止本轮(不硬杀在飞请求,跑完当前条即收工)。
|
|
1184
|
+
if (payload?.cancel === true) {
|
|
1185
|
+
cancelKeywordBackfill();
|
|
1186
|
+
const st = keywordBackfillState();
|
|
1187
|
+
return { started: false, running: st.running, enabled: ec.keyword?.enabled === true, updated: st.updated, failed: st.failed, progress: backfillProgress(st) };
|
|
1188
|
+
}
|
|
1175
1189
|
// probe=true 只读快照:不启动(UI 离开区域再回来时恢复在途任务用)。
|
|
1176
1190
|
if (payload?.probe === true) {
|
|
1177
1191
|
return { started: false, running: st0.running, enabled: ec.keyword?.enabled === true, updated: st0.updated, failed: st0.failed, progress: backfillProgress(st0) };
|
|
@@ -1193,6 +1207,12 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1193
1207
|
// 门控看生效配置(live.redistillEnabled 覆盖部署 cfg.redistill.enabled);关时不启动。
|
|
1194
1208
|
const ec = effectiveCfg(cfg, live);
|
|
1195
1209
|
const st0 = redistillBackfillState();
|
|
1210
|
+
// cancel=true:请求中止本轮(不硬杀在飞请求,跑完当前条即收工)。
|
|
1211
|
+
if (payload?.cancel === true) {
|
|
1212
|
+
cancelRedistillBackfill();
|
|
1213
|
+
const st = redistillBackfillState();
|
|
1214
|
+
return { started: false, running: st.running, enabled: ec.redistill?.enabled === true, updated: st.updated, skipped: st.skipped, failed: st.failed, progress: backfillProgress(st) };
|
|
1215
|
+
}
|
|
1196
1216
|
// probe=true 只读快照:不启动(UI 离开区域再回来时恢复在途任务用)。
|
|
1197
1217
|
if (payload?.probe === true) {
|
|
1198
1218
|
return { started: false, running: st0.running, enabled: ec.redistill?.enabled === true, updated: st0.updated, skipped: st0.skipped, failed: st0.failed, progress: backfillProgress(st0) };
|
|
@@ -1311,7 +1331,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1311
1331
|
supported: live?.supported ?? false,
|
|
1312
1332
|
settings: sanitizeSettings(s ?? {
|
|
1313
1333
|
enabled: true, capture: true, distill: true, recall: true,
|
|
1314
|
-
toolRoom: true, toolGraph: true, toolRuminate: true, toolConflict: true, toolMutate: true,
|
|
1334
|
+
toolRoom: true, toolGraph: true, toolRuminate: true, toolConflict: true, toolMutate: true, toolSkills: true,
|
|
1315
1335
|
reasoningEffort: '', distillProvider: '', distillModel: '', distillChain: [],
|
|
1316
1336
|
distillBudgets: { extract: 0, dedup: 0, l2: 0, l3: 0, graph: 0 }, distillMaxInputChars: 0,
|
|
1317
1337
|
distillLayerChains: { l1: [], l2: [], l3: [] },
|
|
@@ -1364,7 +1384,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1364
1384
|
const patch = (payload ?? {});
|
|
1365
1385
|
const clean = {};
|
|
1366
1386
|
// 布尔开关组:memoryMutate(高权限写删门)与主开关同列;conflictFreeze(§C 人工冲突裁决)
|
|
1367
|
-
for (const key of ['enabled', 'capture', 'distill', 'recall', 'memoryMutate', 'conflictFreeze', 'skillsEnabled', 'keywordEnabled', 'redistillEnabled', 'mdSyncEnabled', 'memportEnabled', 'toolRoom', 'toolGraph', 'toolRuminate', 'toolConflict', 'toolMutate']) {
|
|
1387
|
+
for (const key of ['enabled', 'capture', 'distill', 'recall', 'memoryMutate', 'conflictFreeze', 'skillsEnabled', 'keywordEnabled', 'redistillEnabled', 'mdSyncEnabled', 'memportEnabled', 'toolRoom', 'toolGraph', 'toolRuminate', 'toolConflict', 'toolMutate', 'toolSkills']) {
|
|
1368
1388
|
if (typeof patch[key] === 'boolean')
|
|
1369
1389
|
clean[key] = patch[key];
|
|
1370
1390
|
}
|
|
@@ -388,7 +388,7 @@ export async function exportThenPurge(db, dataDir, ids, reason, logger, now = ne
|
|
|
388
388
|
// 门禁第二半——**批次级投影**。不再拿整库哈希当门禁:那会把“本批逐字未动、但库里有
|
|
389
389
|
// 别的东西被并发改过”误判成不安全,而事实恰好相反——要删的那批在快照里完整可信,
|
|
390
390
|
// 删错了能原样回灌。整库漂移仍计数(它是要给人看的信息),但它不拦住删除。
|
|
391
|
-
let subset
|
|
391
|
+
let subset; // try 内先赋值后读,初始空数组从未被用
|
|
392
392
|
let otherChanged = 0;
|
|
393
393
|
try {
|
|
394
394
|
const want = new Set(ids);
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ import type { SceneStore } from '../store/scenes.js';
|
|
|
10
10
|
import type { SessionModeStore } from '../store/session-modes.js';
|
|
11
11
|
import type { MemoryFamily, MemoryLogger } from '../types.js';
|
|
12
12
|
import type { RoomRegistryStore } from '../store/rooms-registry.js';
|
|
13
|
+
/**
|
|
14
|
+
* 封印登记面:把某个工具组的延迟注册交给 {@link registerMemoryTools} 的封印机制。
|
|
15
|
+
* 组名见 `toolSealed` 的 switch;登记后是否真正注册由当前封印状态决定,
|
|
16
|
+
* 并在 `loader/volatile-update` 时随 `applyToolSeals` 重放(热更)。
|
|
17
|
+
*/
|
|
18
|
+
export interface ToolSealRegistry {
|
|
19
|
+
/** 登记一个工具组的一次性注册器;`make()` 须返回 `ctx.tools.register` 的 disposer。 */
|
|
20
|
+
reg(group: string, make: () => () => void): void;
|
|
21
|
+
}
|
|
13
22
|
export declare function registerMemoryTools(ctx: Context, cfg: MemoryConfig, stores: {
|
|
14
23
|
l0: L0Store;
|
|
15
24
|
l1: L1Store;
|
|
@@ -25,4 +34,4 @@ export declare function registerMemoryTools(ctx: Context, cfg: MemoryConfig, sto
|
|
|
25
34
|
/** 反刍控制器(可选:未装配时 ruminate 工具返回未启用提示)。 */
|
|
26
35
|
ruminate?: RuminateController,
|
|
27
36
|
/** Room 注册表(分类管理 beta.4;缺省 = memory_room_admin 返回未装配提示)。 */
|
|
28
|
-
roomRegistry?: RoomRegistryStore):
|
|
37
|
+
roomRegistry?: RoomRegistryStore): ToolSealRegistry;
|
package/dist/tools/index.js
CHANGED
|
@@ -52,7 +52,7 @@ ruminate,
|
|
|
52
52
|
/** Room 注册表(分类管理 beta.4;缺省 = memory_room_admin 返回未装配提示)。 */
|
|
53
53
|
roomRegistry) {
|
|
54
54
|
if (!cfg.tools)
|
|
55
|
-
return;
|
|
55
|
+
return { reg: () => { } };
|
|
56
56
|
// ── 工具分组封印(v0.21.0):sealable 组的注册经 reg() 登记,按 live 开关动态
|
|
57
57
|
// 注册/反注册。封印 = 从模型工具列表消失(反注册),不是执行期拒绝;开关经
|
|
58
58
|
// 设置页 volatile 热更(loader/volatile-update)即时生效,无需重启。
|
|
@@ -60,6 +60,9 @@ roomRegistry) {
|
|
|
60
60
|
// memory_receipts)与槽位三件(slots.ts)不参与封印。
|
|
61
61
|
const toolDisposers = new Map();
|
|
62
62
|
const toolMakers = new Map();
|
|
63
|
+
// 技能组是「功能开关 + 封印」双条件:功能关(部署 cfg.skills.enabled 与 live.skillsEnabled 取或)或
|
|
64
|
+
// 封印(toolSkills===false)任一满足即不注册——功能关时保持零表面积,开门后仍可单独封印。
|
|
65
|
+
const skillsFeatureOn = () => cfg.skills.enabled === true || live.get().skillsEnabled === true;
|
|
63
66
|
const toolSealed = (group) => {
|
|
64
67
|
const snap = live.get();
|
|
65
68
|
switch (group) {
|
|
@@ -68,6 +71,7 @@ roomRegistry) {
|
|
|
68
71
|
case 'ruminate': return snap.toolRuminate === false;
|
|
69
72
|
case 'conflict': return snap.toolConflict === false;
|
|
70
73
|
case 'mutate': return snap.toolMutate === false;
|
|
74
|
+
case 'skills': return !skillsFeatureOn() || snap.toolSkills === false;
|
|
71
75
|
default: return false;
|
|
72
76
|
}
|
|
73
77
|
};
|
|
@@ -1412,6 +1416,8 @@ roomRegistry) {
|
|
|
1412
1416
|
ctx.on('loader/volatile-update', () => applyToolSeals());
|
|
1413
1417
|
}
|
|
1414
1418
|
logger.info('[memory] 工具已注册: memory_search / conversation_search / memory_read_scene / memory_receipts / memory_search_graph / memory_expand_graph_node,及高权限 memory_add/memory_import/memory_delete / memory_resolve_conflict / memory_ruminate / memory_ruminate_cancel / memory_ruminate_status');
|
|
1419
|
+
// 技能组(registerSkillTools)在注册期经本面登记,随 volatile 热更重放封印。
|
|
1420
|
+
return { reg };
|
|
1415
1421
|
}
|
|
1416
1422
|
/** 凭证回溯的人类可读渲染(含"还有多少条没显示")。 */
|
|
1417
1423
|
function renderReceipts(dimension, items, total) {
|
package/dist/tools/skills.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 技能工具面(轨2
|
|
3
|
-
*
|
|
2
|
+
* 技能工具面(轨2)。**经宿主封印机制登记**(`seals.reg('skills', …)`),不再自行判门——
|
|
3
|
+
* 是否真正注册由 `registerMemoryTools` 的 `toolSealed('skills')` 决定:
|
|
4
|
+
* - 功能关(部署 `cfg.skills.enabled` 与 live `skillsEnabled` 取或为 false)⇒ 封印,零表面积;
|
|
5
|
+
* - 功能开但 `live.toolSkills === false` ⇒ 封印,从模型工具列表移除。
|
|
6
|
+
* 两者都随 `loader/volatile-update` 热更(面板拨开关即时注册/反注册,无需重启)。
|
|
4
7
|
*
|
|
5
8
|
* 读工具 list/read;写工具 pin/archive 过 live.memoryMutate 高权限门(与 memory_add 同门)。
|
|
6
9
|
* 技能不是 memory `type`,这些工具只操作 SkillStore sidecar,不触碰 l1_records / ADD_TYPES / prompt 词表。
|
|
7
10
|
*/
|
|
8
11
|
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
-
import type { MemoryConfig } from '../config.js';
|
|
10
12
|
import type { LiveSettingsHandle } from '../settings.js';
|
|
11
13
|
import type { SkillStore } from '../store/skill-store.js';
|
|
12
14
|
import type { MemoryLogger } from '../types.js';
|
|
13
|
-
|
|
15
|
+
import type { ToolSealRegistry } from './index.js';
|
|
16
|
+
export declare function registerSkillTools(ctx: Context, skills: SkillStore, logger: MemoryLogger, live: LiveSettingsHandle, seals: ToolSealRegistry): void;
|