dsh-prime-memory 0.26.9 → 0.26.11
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.de.md +1638 -0
- package/CHANGELOG.en.md +15 -0
- package/CHANGELOG.es.md +1641 -0
- package/CHANGELOG.fr.md +1643 -0
- package/CHANGELOG.it.md +1643 -0
- package/CHANGELOG.ja.md +15 -0
- package/CHANGELOG.ko.md +15 -0
- package/CHANGELOG.md +62 -0
- package/CHANGELOG.ru.md +1640 -0
- package/README.de.md +1 -1
- package/README.en.md +3 -3
- 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 +17 -4
- package/README.ru.md +1 -1
- package/dist/bench-control.js +3 -1
- package/dist/client.js +121 -12
- package/dist/clients/drsc.d.ts +34 -0
- package/dist/clients/drsc.js +56 -11
- package/dist/config.d.ts +10 -1
- package/dist/config.js +37 -18
- package/dist/contract.d.ts +48 -1
- package/dist/graph/gate.d.ts +17 -0
- package/dist/graph/gate.js +11 -0
- package/dist/graph/preview.d.ts +5 -0
- package/dist/graph/preview.js +2 -0
- package/dist/index.js +1 -1
- package/dist/pipeline/fact-source.d.ts +127 -0
- package/dist/pipeline/fact-source.js +192 -0
- package/dist/pipeline/rebuild-preserve.d.ts +31 -2
- package/dist/pipeline/rebuild-preserve.js +40 -14
- package/dist/pipeline/rebuild.d.ts +47 -13
- package/dist/pipeline/rebuild.js +153 -57
- package/dist/pipeline/runner.d.ts +4 -1
- package/dist/pipeline/runner.js +58 -21
- package/dist/pipeline/workspace-relink.js +7 -2
- package/dist/room-review.d.ts +1 -1
- package/dist/room-review.js +10 -5
- package/dist/settings.js +4 -0
- package/dist/stats.js +10 -3
- package/dist/store/graph-store.d.ts +20 -0
- package/dist/store/graph-store.js +50 -1
- package/dist/store/sqlite.d.ts +42 -0
- package/dist/store/sqlite.js +98 -0
- package/dist/tools/index.js +4 -3
- package/dsh.plugin.json +2 -2
- package/package.json +26 -21
package/dist/pipeline/runner.js
CHANGED
|
@@ -3,6 +3,7 @@ import { GRAPH_PRIORITY_NEW } from '../graph/types.js';
|
|
|
3
3
|
import { NoDistillRouteError } from '../llm.js';
|
|
4
4
|
import { startValidityBackfill } from '../validity-backfill.js';
|
|
5
5
|
import { drscRowToConversationMessage } from '../clients/drsc.js';
|
|
6
|
+
import { graphFeatureOn } from '../graph/gate.js';
|
|
6
7
|
import { trace } from '../store/trace.js';
|
|
7
8
|
import { emptyPending, freshWarmup, groupPendingBySession, loadPending, PENDING_MODES, pendingPathFor, savePending, } from '../store/pending.js';
|
|
8
9
|
import { errDetail } from '../util/filelog.js';
|
|
@@ -41,6 +42,8 @@ export function pickNextTaskIndex(tasks) {
|
|
|
41
42
|
* primaryEffort;非链模式旧键对静态回退条目盖章接管(存量兼容)。
|
|
42
43
|
* - 输出预算/输入预算/通道覆盖/远程嵌入覆盖:非零/非空运行时值直接注入对应子树。
|
|
43
44
|
*/
|
|
45
|
+
// 外部(tools / index)用的同一判据从这里取,避免两处各写一份。
|
|
46
|
+
export { graphFeatureOn };
|
|
44
47
|
export function effectiveCfg(cfg, live) {
|
|
45
48
|
const s = live?.get();
|
|
46
49
|
const eff = s?.reasoningEffort ?? '';
|
|
@@ -145,6 +148,12 @@ export function effectiveCfg(cfg, live) {
|
|
|
145
148
|
const vtOverride = s?.validityTiering !== undefined
|
|
146
149
|
? { validity: { ...(cfg.validity ?? {}), tieringEnabled: cfg.validity?.tieringEnabled === true || s.validityTiering === true } }
|
|
147
150
|
: null;
|
|
151
|
+
// 知识图谱投影(issue #16):live.graphEnabled 与部署 cfg.graph.enabled 取或。
|
|
152
|
+
// 这里**直接复用 graphFeatureOn**,而不是再手写一遍“部署开关 或 面板开关”:
|
|
153
|
+
// 判据一旦有两份,将来改一处漏一处就会出现“面板显示开、覆盖算出来却是关”类的分叉。
|
|
154
|
+
const grOverride = s?.graphEnabled !== undefined
|
|
155
|
+
? { graph: { ...(cfg.graph ?? { enabled: false }), enabled: graphFeatureOn(cfg, live) } }
|
|
156
|
+
: null;
|
|
148
157
|
// md 投射/后缀收敛的单次动作护栏运行时覆盖(0 = 跟随部署 cfg.memExport.maxRecords)。
|
|
149
158
|
// 只覆盖条数护栏,不动 enabled/root——开关与根路径仍属部署配置。
|
|
150
159
|
const mdx = s?.mdExportMaxRecords ?? 0;
|
|
@@ -153,7 +162,7 @@ export function effectiveCfg(cfg, live) {
|
|
|
153
162
|
// 不动 maxRecords/timeBudgetMs/callTimeoutMs——那三个仍属部署护栏,要改得改 YAML。
|
|
154
163
|
const bf = s?.backfillConcurrency ?? 0;
|
|
155
164
|
const backfillOverride = bf > 0 ? { backfill: { ...(cfg.backfill ?? {}), concurrency: bf } } : null;
|
|
156
|
-
if (!override && !budgets && !maxInput && !fallbacksTakeover && !chainEffort && !chainFallbacks && !effortInject && !layerChains && !hasChannel && !embedOverride && !cfOverride && !memExportOverride && !backfillOverride && !kwOverride && !rdOverride && !mdsOverride && !mpOverride && !vtOverride)
|
|
165
|
+
if (!override && !budgets && !maxInput && !fallbacksTakeover && !chainEffort && !chainFallbacks && !effortInject && !layerChains && !hasChannel && !embedOverride && !cfOverride && !memExportOverride && !backfillOverride && !kwOverride && !rdOverride && !mdsOverride && !mpOverride && !vtOverride && !grOverride)
|
|
157
166
|
return cfg;
|
|
158
167
|
return {
|
|
159
168
|
...cfg,
|
|
@@ -164,6 +173,7 @@ export function effectiveCfg(cfg, live) {
|
|
|
164
173
|
...(mdsOverride ?? {}),
|
|
165
174
|
...(mpOverride ?? {}),
|
|
166
175
|
...(vtOverride ?? {}),
|
|
176
|
+
...(grOverride ?? {}),
|
|
167
177
|
...(memExportOverride ?? {}),
|
|
168
178
|
...(backfillOverride ?? {}),
|
|
169
179
|
llm: {
|
|
@@ -266,20 +276,22 @@ export class MemoryRunner {
|
|
|
266
276
|
const graphs = this.stores.graph;
|
|
267
277
|
if (graphs) {
|
|
268
278
|
graphs.recoverRunning();
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
// **无条件**挂启动补跑,开关在 tick 内判:
|
|
280
|
+
// 若在这里就判门注册,面板热开启后存量要等重启才补(issue #16 的半开态)。
|
|
281
|
+
this.ctx.effect(() => {
|
|
282
|
+
const timer = setTimeout(() => {
|
|
283
|
+
if (this.stopped)
|
|
284
|
+
return;
|
|
285
|
+
if (!graphFeatureOn(this.cfg, this.live))
|
|
286
|
+
return;
|
|
287
|
+
const jobs = graphs.queueMissing(9000);
|
|
288
|
+
if (jobs > 0) {
|
|
289
|
+
this.logger.info(`[memory] 图谱存量补投影:${jobs} 个任务入队`);
|
|
290
|
+
this.maybeQueueGraphTask();
|
|
291
|
+
}
|
|
292
|
+
}, STARTUP_RETRY_DELAY_MS);
|
|
293
|
+
return () => clearTimeout(timer);
|
|
294
|
+
});
|
|
283
295
|
}
|
|
284
296
|
}
|
|
285
297
|
/** 启动补跑:对每个非空桶的每个会话切片入队一次蒸馏尝试(受 live 开关与
|
|
@@ -468,12 +480,28 @@ export class MemoryRunner {
|
|
|
468
480
|
*/
|
|
469
481
|
startGraphBackfillTimer() {
|
|
470
482
|
const graphs = this.stores.graph;
|
|
471
|
-
|
|
483
|
+
// 只依赖 store 存在:**不能**在注册时判 graph 开关 —— timer 只在 init 注册一次,
|
|
484
|
+
// 那样运行时开启后存量永远补不到(issue #16 的实证半开态)。开关改在 tick 内判。
|
|
485
|
+
if (!graphs)
|
|
472
486
|
return;
|
|
487
|
+
// 热开启即时补一批:否则用户拨开开关要等一个周期(30 分钟)才看到图谱动起来。
|
|
488
|
+
this.ctx.on('loader/volatile-update', (paths) => {
|
|
489
|
+
if (!paths.some((p) => p[0] === 'live'))
|
|
490
|
+
return;
|
|
491
|
+
if (!graphFeatureOn(this.cfg, this.live))
|
|
492
|
+
return;
|
|
493
|
+
const jobs = graphs.queueMissing(500);
|
|
494
|
+
if (jobs > 0) {
|
|
495
|
+
this.logger.info(`[memory] 图谱热开启补投影:${jobs} 个任务入队`);
|
|
496
|
+
this.maybeQueueGraphTask();
|
|
497
|
+
}
|
|
498
|
+
});
|
|
473
499
|
this.ctx.effect(() => {
|
|
474
500
|
const timer = setInterval(() => {
|
|
475
501
|
if (this.stopped)
|
|
476
502
|
return;
|
|
503
|
+
if (!graphFeatureOn(this.cfg, this.live))
|
|
504
|
+
return;
|
|
477
505
|
const jobs = graphs.queueMissing(500);
|
|
478
506
|
if (jobs > 0) {
|
|
479
507
|
this.logger.debug?.(`[memory] 图谱周期补投影:${jobs} 个任务入队`);
|
|
@@ -522,8 +550,14 @@ export class MemoryRunner {
|
|
|
522
550
|
* 用户后续开启由启动/周期补投影兜底(与 distill 分支同一口径)。
|
|
523
551
|
*/
|
|
524
552
|
enqueueGraphFor(recordIds) {
|
|
525
|
-
if (recordIds.length === 0 || !this.stores.graph
|
|
553
|
+
if (recordIds.length === 0 || !this.stores.graph)
|
|
554
|
+
return;
|
|
555
|
+
if (!graphFeatureOn(this.cfg, this.live)) {
|
|
556
|
+
// 关闭期写入也要记账:`graph_projected_records` 只按 record_id+version 记账、无内容版本,
|
|
557
|
+
// 若不抹掉,重开后 queueMissing 会认为“这条已投过”而永不补 → 图谱内容永久陈旧。
|
|
558
|
+
this.stores.graph.invalidateProjected(recordIds);
|
|
526
559
|
return;
|
|
560
|
+
}
|
|
527
561
|
this.stores.graph.queueGraphProjection([...recordIds], GRAPH_PRIORITY_NEW);
|
|
528
562
|
this.maybeQueueGraphTask();
|
|
529
563
|
}
|
|
@@ -535,7 +569,9 @@ export class MemoryRunner {
|
|
|
535
569
|
*/
|
|
536
570
|
reprojectGraph(recordIds) {
|
|
537
571
|
const graphs = this.stores.graph;
|
|
538
|
-
if (!graphs ||
|
|
572
|
+
if (!graphs || recordIds.length === 0)
|
|
573
|
+
return { queued: 0, jobIds: [] };
|
|
574
|
+
if (!graphFeatureOn(this.cfg, this.live))
|
|
539
575
|
return { queued: 0, jobIds: [] };
|
|
540
576
|
const r = graphs.forceReprojectRecord(recordIds);
|
|
541
577
|
if (r.queued > 0)
|
|
@@ -544,7 +580,8 @@ export class MemoryRunner {
|
|
|
544
580
|
}
|
|
545
581
|
/**
|
|
546
582
|
* 图谱投影泵入队判定:
|
|
547
|
-
* -
|
|
583
|
+
* - 双门:`graphFeatureOn`(部署 cfg.graph.enabled **或** 面板 live.graphEnabled)
|
|
584
|
+
* + 运行时 live.enabled/distill,任一为假不入队;
|
|
548
585
|
* - 占位:同一时刻至多一个 graph 任务在队列(靠 graphPumpQueued 标志),永不与
|
|
549
586
|
* live 抢位(pickNextTaskIndex 保证 live > graph);
|
|
550
587
|
* - 退避/attempts 封顶:由 GraphStore.claimNext 的 WHERE 过滤与 fail 转 dead 承接,
|
|
@@ -554,7 +591,7 @@ export class MemoryRunner {
|
|
|
554
591
|
const graphs = this.stores.graph;
|
|
555
592
|
if (!graphs || this.stopped || this.graphPumpQueued)
|
|
556
593
|
return;
|
|
557
|
-
if (!this.cfg.
|
|
594
|
+
if (!graphFeatureOn(this.cfg, this.live))
|
|
558
595
|
return;
|
|
559
596
|
const liveNow = this.live.get();
|
|
560
597
|
if (!(liveNow.enabled && liveNow.distill))
|
|
@@ -663,7 +700,7 @@ export class MemoryRunner {
|
|
|
663
700
|
if ((opts?.force || sliceLen >= effective) && !backedOff) {
|
|
664
701
|
newRecords = await this.extractSessionSlice(sessionId, mode, cfg, effective, opts);
|
|
665
702
|
// 新记录入投影队列(优先级恒高于存量补投影)并拉起图谱泵;入队本身廉价,
|
|
666
|
-
//
|
|
703
|
+
// 开关关时由 enqueueGraphFor 抹投影凭证(避免重开后永久陈旧),重开由补投影兜底。
|
|
667
704
|
// (写面 R2:分支本体抽成 enqueueGraphFor,与工具三路径同源,不再两处漂移。)
|
|
668
705
|
this.enqueueGraphFor(newRecords.map((r) => r.id));
|
|
669
706
|
}
|
|
@@ -13,8 +13,13 @@ export async function relinkWorkspaces(deps) {
|
|
|
13
13
|
const report = {
|
|
14
14
|
dryRun, scanned: 0, matched: 0, unchanged: 0, toWrite: 0, written: 0, remaining: 0, byWorkspace: {},
|
|
15
15
|
};
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
// 空路由表 = **未配置**(issue #13 把默认值改成 `[]`)。这里必须显式拒绕:
|
|
17
|
+
// 旧行为是返回全零报告,面板显示“扫 0 / 剩 0” —— 看起来像“已经做完了”,
|
|
18
|
+
// 真正的原因(一条路由都没配)反而看不见。
|
|
19
|
+
if (routes.length === 0) {
|
|
20
|
+
throw new Error('未配置任何内容路由规则(workspaceSource.routes 为空):'
|
|
21
|
+
+ '先在配置里写下至少一条 {path, anyOf},再执行归属回填');
|
|
22
|
+
}
|
|
18
23
|
let offset = 0;
|
|
19
24
|
let batch = [];
|
|
20
25
|
// 计数在此结算:`flush()` 会清空 batch,调用点再拿 `batch.length` 必然归零。
|
package/dist/room-review.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export interface RoomCandidate {
|
|
|
75
75
|
id: string;
|
|
76
76
|
rooms: string[];
|
|
77
77
|
}
|
|
78
|
-
/** 解析标注器输出的逐条候选(容错:id 配对失败丢弃;
|
|
78
|
+
/** 解析标注器输出的逐条候选(容错:id 配对失败丢弃;isRoomSlug 校验(两级制);目录内优先,≤3)。 */
|
|
79
79
|
export declare function parseRoomCandidateItems(parsed: ReadonlyArray<{
|
|
80
80
|
id: string;
|
|
81
81
|
rooms: readonly string[];
|
package/dist/room-review.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRoomSlug } from './metadata-validators.js';
|
|
2
2
|
import { callLLM, parseJsonLogged } from './llm.js';
|
|
3
3
|
export const ROOM_CANDIDATES_KEY = 'roomCandidates';
|
|
4
4
|
export const ROOM_REVIEW_KEY = 'roomReview';
|
|
5
|
-
/** 解析标注器输出的逐条候选(容错:id 配对失败丢弃;
|
|
5
|
+
/** 解析标注器输出的逐条候选(容错:id 配对失败丢弃;isRoomSlug 校验(两级制);目录内优先,≤3)。 */
|
|
6
6
|
export function parseRoomCandidateItems(parsed, ids, roomCatalog) {
|
|
7
7
|
const idSet = new Set(ids);
|
|
8
8
|
const catalogSet = new Set(roomCatalog.map((r) => r.room));
|
|
@@ -13,7 +13,9 @@ export function parseRoomCandidateItems(parsed, ids, roomCatalog) {
|
|
|
13
13
|
const cleaned = [];
|
|
14
14
|
for (const r of item.rooms) {
|
|
15
15
|
const slug = typeof r === 'string' ? r.trim().toLowerCase() : '';
|
|
16
|
-
|
|
16
|
+
// 两级制(`major` 或 `major/minor`):与 rooms-registry 同一判据。用 isTag 会把
|
|
17
|
+
// `mpu/qos` 这类合法子类静默丢掉——注册表收得下、写回却挂不上(issue #12)。
|
|
18
|
+
if (!slug || !isRoomSlug(slug))
|
|
17
19
|
continue;
|
|
18
20
|
if (!cleaned.includes(slug))
|
|
19
21
|
cleaned.push(slug);
|
|
@@ -35,7 +37,8 @@ export function roomCandidateSystemPrompt(roomCatalog) {
|
|
|
35
37
|
return ('你是记忆库的 Room 归类器。给你一条记忆与现有 Room 词表(带计数),' +
|
|
36
38
|
'为这条记忆选出它最可能归属的 1-3 个 Room slug。规则:\n' +
|
|
37
39
|
'1. **优先从现有 Room 词表里选**(保持分类收敛,避免碎片化);\n' +
|
|
38
|
-
'2. 词表确实没有合适主题时,才提议最多 2 个新 slug(
|
|
40
|
+
'2. 词表确实没有合适主题时,才提议最多 2 个新 slug(小写字母/数字/连字符;' +
|
|
41
|
+
'两级制写成 `大类/子类` 形式,如 `mpu/qos`,全串至多一个斜杠);\n' +
|
|
39
42
|
'3. 完全无法判断时返回空数组(宁缺毋滥,不硬猜)。\n' +
|
|
40
43
|
`现有 Room 词表:\n${catalog}\n\n` +
|
|
41
44
|
'输出 JSON 对象:{"rooms":["slug1","slug2"]}。只输出 JSON,不要多余文字。');
|
|
@@ -264,7 +267,9 @@ export function confirmReview(l1, id, rooms) {
|
|
|
264
267
|
if (typeof r !== 'string')
|
|
265
268
|
continue;
|
|
266
269
|
const slug = r.trim().toLowerCase();
|
|
267
|
-
|
|
270
|
+
// 同 parseRoomCandidateItems:走两级制判据,否则 `mpu/qos` 会被判非法、
|
|
271
|
+
// confirm 返回 null 且错误文案误导成"id 不存在"(issue #12)。
|
|
272
|
+
if (!slug || !isRoomSlug(slug) || cleaned.includes(slug))
|
|
268
273
|
continue;
|
|
269
274
|
cleaned.push(slug);
|
|
270
275
|
}
|
package/dist/settings.js
CHANGED
|
@@ -82,6 +82,8 @@ const ALWAYS_ON = {
|
|
|
82
82
|
memoryMutate: false,
|
|
83
83
|
conflictFreeze: false,
|
|
84
84
|
skillsEnabled: false,
|
|
85
|
+
// 知识图谱投影:默认关(部署 cfg.graph.enabled 仍可强制开;这里只做追加开)
|
|
86
|
+
graphEnabled: false,
|
|
85
87
|
keywordEnabled: false,
|
|
86
88
|
redistillEnabled: false,
|
|
87
89
|
mdSyncEnabled: false,
|
|
@@ -329,6 +331,8 @@ function resolveSettings(value) {
|
|
|
329
331
|
conflictFreeze: v.conflictFreeze === true,
|
|
330
332
|
// 轨2 技能结晶:严格 === true(默认关,新功能默认关)
|
|
331
333
|
skillsEnabled: v.skillsEnabled === true,
|
|
334
|
+
// 知识图谱投影:严格 === true(默认关);与部署 graph.enabled 取或后生效
|
|
335
|
+
graphEnabled: v.graphEnabled === true,
|
|
332
336
|
// 存量回填/进阶功能启用开关:严格 === true(默认关)
|
|
333
337
|
keywordEnabled: v.keywordEnabled === true,
|
|
334
338
|
redistillEnabled: v.redistillEnabled === true,
|
package/dist/stats.js
CHANGED
|
@@ -1119,9 +1119,13 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1119
1119
|
return v;
|
|
1120
1120
|
}
|
|
1121
1121
|
if (kind === 'records') {
|
|
1122
|
-
|
|
1122
|
+
// 与 list-records 同口径(见下方 tagSel):Room slug 是自生长的两级制形状,
|
|
1123
|
+
// 非法形状在这里就拒,不做“静默返回空 CSV”。
|
|
1124
|
+
const tag = typeof p.tag === 'string' ? p.tag.trim().slice(0, 64) : '';
|
|
1123
1125
|
if (!tag)
|
|
1124
1126
|
throw new Error('kind=records 需要 tag(目标 Room slug)');
|
|
1127
|
+
if (!isRoomSlug(tag))
|
|
1128
|
+
throw new Error('tag 非法(两级制:major 或 major/minor,各段小写字母数字连字符)');
|
|
1125
1129
|
const header = 'id,type,scene_name,updated_at,tags,content';
|
|
1126
1130
|
const lines = [header];
|
|
1127
1131
|
let n = 0;
|
|
@@ -1351,6 +1355,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1351
1355
|
memoryMutate: false,
|
|
1352
1356
|
conflictFreeze: live?.get()?.conflictFreeze === true,
|
|
1353
1357
|
skillsEnabled: live?.get()?.skillsEnabled === true,
|
|
1358
|
+
graphEnabled: live?.get()?.graphEnabled === true,
|
|
1354
1359
|
keywordEnabled: live?.get()?.keywordEnabled === true,
|
|
1355
1360
|
redistillEnabled: live?.get()?.redistillEnabled === true,
|
|
1356
1361
|
mdSyncEnabled: live?.get()?.mdSyncEnabled === true,
|
|
@@ -1402,7 +1407,7 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
1402
1407
|
const patch = (payload ?? {});
|
|
1403
1408
|
const clean = {};
|
|
1404
1409
|
// 布尔开关组:memoryMutate(高权限写删门)与主开关同列;conflictFreeze(§C 人工冲突裁决)
|
|
1405
|
-
for (const key of ['enabled', 'capture', 'distill', 'recall', 'memoryMutate', 'conflictFreeze', 'skillsEnabled', 'keywordEnabled', 'redistillEnabled', 'mdSyncEnabled', 'memportEnabled', 'toolRoom', 'toolGraph', 'toolRuminate', 'toolConflict', 'toolMutate', 'toolSkills']) {
|
|
1410
|
+
for (const key of ['enabled', 'capture', 'distill', 'recall', 'memoryMutate', 'conflictFreeze', 'skillsEnabled', 'graphEnabled', 'keywordEnabled', 'redistillEnabled', 'mdSyncEnabled', 'memportEnabled', 'toolRoom', 'toolGraph', 'toolRuminate', 'toolConflict', 'toolMutate', 'toolSkills']) {
|
|
1406
1411
|
if (typeof patch[key] === 'boolean')
|
|
1407
1412
|
clean[key] = patch[key];
|
|
1408
1413
|
}
|
|
@@ -2117,7 +2122,9 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
2117
2122
|
throw new Error('蒸馏开关已关闭,请先开启蒸馏再重建');
|
|
2118
2123
|
if (!cfg.extract.enabled)
|
|
2119
2124
|
throw new Error('部署配置已停用蒸馏(extract.enabled=false),无法重建');
|
|
2120
|
-
|
|
2125
|
+
// force 只跨“水位落差”的二次确认;“L0 无正文而检索库非空”仍由 start() 硬拒。
|
|
2126
|
+
const p = (payload ?? {});
|
|
2127
|
+
const result = rebuild.start(p.force === true);
|
|
2121
2128
|
deps.logger.info('[memory] 收到重建指令(设置页按钮)');
|
|
2122
2129
|
return result;
|
|
2123
2130
|
}
|
|
@@ -163,6 +163,18 @@ export declare class GraphStore {
|
|
|
163
163
|
queued: number;
|
|
164
164
|
jobIds: string[];
|
|
165
165
|
};
|
|
166
|
+
/**
|
|
167
|
+
* 只抹“已投影”凭证,**不排队**(issue #16 关闭期防护)。
|
|
168
|
+
*
|
|
169
|
+
* 关闭期写路径不入队,但记录内容已经变了;而 `graph_projected_records` 只按
|
|
170
|
+
* `record_id + projector_version` 记账、**不记内容版本** —— 不抹掉的话,重开后
|
|
171
|
+
* `queueMissing` 会认为“这条投过了”而永不补,图谱就永久陈旧(只能靠人工
|
|
172
|
+
* `memory_reproject`)。
|
|
173
|
+
*
|
|
174
|
+
* 不在这里入队:关闭期入队会造成“投了永不泵”的陈旧队列(与 `reprojectGraph`
|
|
175
|
+
* 关闭时回零同源),重开时由 `queueMissing` 统一补。
|
|
176
|
+
*/
|
|
177
|
+
invalidateProjected(recordIds: readonly string[]): void;
|
|
166
178
|
/**
|
|
167
179
|
* 确定性边提案落库面(graph-write-plane R4 兜底支路):把"工具传入的显式边提案"
|
|
168
180
|
* 当作一次确定性投影输入,经 `applyGraphProjection` **纯函数**校验后在同一事务内
|
|
@@ -236,6 +248,14 @@ export declare class GraphStore {
|
|
|
236
248
|
* 返回新建 job 数。
|
|
237
249
|
*/
|
|
238
250
|
queueMissing(limit: number): number;
|
|
251
|
+
/**
|
|
252
|
+
* 尚未投影(当前版本)且无在途 mapping 的 L1 条数。
|
|
253
|
+
*
|
|
254
|
+
* 面板“首次开启投影大概要花多少”的分母(issue #16):本方法必须与 `queueMissing`
|
|
255
|
+
* 用**同一段 WHERE** —— 两处口径一旦分叉,面板报的数字就不是实际会被拉起的量。
|
|
256
|
+
* 只读聚合,不写库。
|
|
257
|
+
*/
|
|
258
|
+
countMissing(): number;
|
|
239
259
|
/**
|
|
240
260
|
* 删除传播(L1 批量删除后的惰性墓碑):来源在 L1 已全部不存在的 active/disputed
|
|
241
261
|
* 节点与边标 archived(保留行,expand 可见墓碑)。挂在 MemoryDb.deleteL1Batch
|
|
@@ -529,7 +529,7 @@ export class GraphStore {
|
|
|
529
529
|
catch (err) {
|
|
530
530
|
this.logger?.warn(`${TAG} 预览 job 计数失败(计数回零): ${err instanceof Error ? err.message : String(err)}`);
|
|
531
531
|
}
|
|
532
|
-
const r = buildGraphPreview(nodes, edges, { seedId, pendingJobs, jobsTotal, deadJobs });
|
|
532
|
+
const r = buildGraphPreview(nodes, edges, { seedId, pendingJobs, jobsTotal, deadJobs, missing: this.countMissing() });
|
|
533
533
|
if (mode === 'full')
|
|
534
534
|
return { ...r, full: buildFullView(nodes, edges) };
|
|
535
535
|
return r;
|
|
@@ -650,6 +650,33 @@ export class GraphStore {
|
|
|
650
650
|
return { queued: 0, jobIds: [] };
|
|
651
651
|
}
|
|
652
652
|
}
|
|
653
|
+
/**
|
|
654
|
+
* 只抹“已投影”凭证,**不排队**(issue #16 关闭期防护)。
|
|
655
|
+
*
|
|
656
|
+
* 关闭期写路径不入队,但记录内容已经变了;而 `graph_projected_records` 只按
|
|
657
|
+
* `record_id + projector_version` 记账、**不记内容版本** —— 不抹掉的话,重开后
|
|
658
|
+
* `queueMissing` 会认为“这条投过了”而永不补,图谱就永久陈旧(只能靠人工
|
|
659
|
+
* `memory_reproject`)。
|
|
660
|
+
*
|
|
661
|
+
* 不在这里入队:关闭期入队会造成“投了永不泵”的陈旧队列(与 `reprojectGraph`
|
|
662
|
+
* 关闭时回零同源),重开时由 `queueMissing` 统一补。
|
|
663
|
+
*/
|
|
664
|
+
invalidateProjected(recordIds) {
|
|
665
|
+
if (!this.db || recordIds.length === 0)
|
|
666
|
+
return;
|
|
667
|
+
const ids = [...new Set(recordIds)];
|
|
668
|
+
try {
|
|
669
|
+
this.tx(() => {
|
|
670
|
+
for (const chunk of chunkOf(ids)) {
|
|
671
|
+
const ph = chunk.map(() => '?').join(',');
|
|
672
|
+
this.db.prepare(`DELETE FROM graph_projected_records WHERE record_id IN (${ph})`).run(...chunk);
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
catch (err) {
|
|
677
|
+
this.logger?.warn(`${TAG} 投影凭证失效失败(忽略): ${err instanceof Error ? err.message : String(err)}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
653
680
|
/**
|
|
654
681
|
* 确定性边提案落库面(graph-write-plane R4 兜底支路):把"工具传入的显式边提案"
|
|
655
682
|
* 当作一次确定性投影输入,经 `applyGraphProjection` **纯函数**校验后在同一事务内
|
|
@@ -947,6 +974,28 @@ export class GraphStore {
|
|
|
947
974
|
return 0;
|
|
948
975
|
}
|
|
949
976
|
}
|
|
977
|
+
/**
|
|
978
|
+
* 尚未投影(当前版本)且无在途 mapping 的 L1 条数。
|
|
979
|
+
*
|
|
980
|
+
* 面板“首次开启投影大概要花多少”的分母(issue #16):本方法必须与 `queueMissing`
|
|
981
|
+
* 用**同一段 WHERE** —— 两处口径一旦分叉,面板报的数字就不是实际会被拉起的量。
|
|
982
|
+
* 只读聚合,不写库。
|
|
983
|
+
*/
|
|
984
|
+
countMissing() {
|
|
985
|
+
if (!this.db)
|
|
986
|
+
return 0;
|
|
987
|
+
try {
|
|
988
|
+
const row = this.db
|
|
989
|
+
.prepare(`SELECT COUNT(*) AS n FROM l1_records r
|
|
990
|
+
WHERE NOT EXISTS (SELECT 1 FROM graph_projected_records p WHERE p.record_id = r.record_id AND p.projector_version = ?)
|
|
991
|
+
AND NOT EXISTS (SELECT 1 FROM graph_job_records m WHERE m.record_id = r.record_id)`)
|
|
992
|
+
.get(GRAPH_PROJECTOR_VERSION);
|
|
993
|
+
return Number(row?.n ?? 0);
|
|
994
|
+
}
|
|
995
|
+
catch {
|
|
996
|
+
return 0;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
950
999
|
// ============================
|
|
951
1000
|
// 删除传播(B10)
|
|
952
1001
|
// ============================
|
package/dist/store/sqlite.d.ts
CHANGED
|
@@ -623,6 +623,23 @@ export declare class MemoryDb {
|
|
|
623
623
|
* 使"无锚点"与"锚点为空"在类型层就是两件事。
|
|
624
624
|
*/
|
|
625
625
|
private toL0Record;
|
|
626
|
+
/**
|
|
627
|
+
* 有 L0 正文的会话 id,按**首条消息时间升序**(流式重建的会话游标)。
|
|
628
|
+
*
|
|
629
|
+
* 为什么不是直接 `listL0All()`:那个接口全量拉正文(3 万条约 88MB)且整个重建期
|
|
630
|
+
* 常驻。按会话走 `idx_l0_session_id` 点查,峰值降到单会话量级。
|
|
631
|
+
* 排序与 `groupL0Sessions` 的“会话按首条时间升序”同一口径(情境链按时间衔接)。
|
|
632
|
+
*/
|
|
633
|
+
l0SessionIds(): string[];
|
|
634
|
+
/** 某会话的全部 L0 正文(时间升序;走 idx_l0_session_id,只占单会话内存)。 */
|
|
635
|
+
l0MessagesBySession(sessionId: string): L0MessageRecord[];
|
|
636
|
+
/**
|
|
637
|
+
* 仅取 L0 消息 id 集(post-watermark 判据用)。
|
|
638
|
+
*
|
|
639
|
+
* 刻意**不读 `message_text`**:判据只需要 id,而正文才是内存大头。
|
|
640
|
+
* 2.5 万条 id 约 1.5MB,比全量快照小一个数量级。
|
|
641
|
+
*/
|
|
642
|
+
l0MessageIds(): Set<string>;
|
|
626
643
|
/** L0 全量列举(重建快照用;按时间升序,事务一致性避开 JSONL 追加竞态)。 */
|
|
627
644
|
listL0All(): L0MessageRecord[];
|
|
628
645
|
/** 重建成本预估(一次全表聚合:会话数 / 消息数 / 字符量)。 */
|
|
@@ -631,6 +648,31 @@ export declare class MemoryDb {
|
|
|
631
648
|
messages: number;
|
|
632
649
|
chars: number;
|
|
633
650
|
};
|
|
651
|
+
/**
|
|
652
|
+
* 重建水位探针(issue #14)。
|
|
653
|
+
*
|
|
654
|
+
* 回答三个问题:
|
|
655
|
+
* - 本地 L0 **覆盖到什么时候**(`l0Iso`):L0 正文已于 T8.4 写停,这个值是
|
|
656
|
+
* **常量**,不会随使用前移;
|
|
657
|
+
* - 检索库现有多少条、其中多少条**晚于**该水位(`l1After`):**仅用于面板提示**
|
|
658
|
+
* “本次重建只覆盖到 X”,**绝不用作拒绝门槛** —— L1 的 `created_time` 每次
|
|
659
|
+
* 蒸馏都会前移,拿它做阈值会把重建永久锁死;
|
|
660
|
+
* - `l0SessionCount`:面板口径(多少个会话的正文在快照里)。
|
|
661
|
+
*
|
|
662
|
+
* 注意 `l0_conversations` **没有 `created_time` 列**,只有 `recorded_at`(TEXT)
|
|
663
|
+
* 与 `timestamp`(INTEGER ms):主用前者(与 L1 同为 ISO 文本可直接比),
|
|
664
|
+
* 空串回退后者。两列都建了索引,`MAX` 走覆盖索引。
|
|
665
|
+
*
|
|
666
|
+
* L0 全空时 `l0Iso` 为 `null`,此时 `l1After` 取 `l1Total`(没有水位而 L1 有记录
|
|
667
|
+
* = 一条都重蒸不出来),而不是 0 —— 否则面板会把“最危险的情况”显示成“无影响”。
|
|
668
|
+
*/
|
|
669
|
+
rebuildWatermark(): {
|
|
670
|
+
l0Iso: string | null;
|
|
671
|
+
l0Ms: number | null;
|
|
672
|
+
l0SessionCount: number;
|
|
673
|
+
l1Total: number;
|
|
674
|
+
l1After: number;
|
|
675
|
+
};
|
|
634
676
|
/** 向量表行数(backfill 判据:与元数据行数的差值即缺失向量数;不可用时返回 -1)。 */
|
|
635
677
|
countL1Vec(): number;
|
|
636
678
|
countL0Vec(): number;
|
package/dist/store/sqlite.js
CHANGED
|
@@ -2405,6 +2405,63 @@ export class MemoryDb {
|
|
|
2405
2405
|
rec.step = r.step;
|
|
2406
2406
|
return rec;
|
|
2407
2407
|
}
|
|
2408
|
+
/**
|
|
2409
|
+
* 有 L0 正文的会话 id,按**首条消息时间升序**(流式重建的会话游标)。
|
|
2410
|
+
*
|
|
2411
|
+
* 为什么不是直接 `listL0All()`:那个接口全量拉正文(3 万条约 88MB)且整个重建期
|
|
2412
|
+
* 常驻。按会话走 `idx_l0_session_id` 点查,峰值降到单会话量级。
|
|
2413
|
+
* 排序与 `groupL0Sessions` 的“会话按首条时间升序”同一口径(情境链按时间衔接)。
|
|
2414
|
+
*/
|
|
2415
|
+
l0SessionIds() {
|
|
2416
|
+
if (this.degraded)
|
|
2417
|
+
return [];
|
|
2418
|
+
try {
|
|
2419
|
+
const rows = this.db
|
|
2420
|
+
.prepare('SELECT session_id FROM l0_conversations GROUP BY session_id ORDER BY MIN(timestamp) ASC')
|
|
2421
|
+
.all();
|
|
2422
|
+
return rows.map((r) => r.session_id).filter((s) => typeof s === 'string' && s !== '');
|
|
2423
|
+
}
|
|
2424
|
+
catch (err) {
|
|
2425
|
+
this.logger?.warn(`${TAG} L0 会话列表失败(返回空): ${err instanceof Error ? err.message : String(err)}`);
|
|
2426
|
+
return [];
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
/** 某会话的全部 L0 正文(时间升序;走 idx_l0_session_id,只占单会话内存)。 */
|
|
2430
|
+
l0MessagesBySession(sessionId) {
|
|
2431
|
+
if (this.degraded || !sessionId)
|
|
2432
|
+
return [];
|
|
2433
|
+
try {
|
|
2434
|
+
const rows = this.db
|
|
2435
|
+
.prepare('SELECT record_id, session_id, role, message_text, recorded_at, timestamp, turn, step FROM l0_conversations WHERE session_id = ? ORDER BY timestamp ASC')
|
|
2436
|
+
.all(sessionId);
|
|
2437
|
+
return rows.map((r) => this.toL0Record(r));
|
|
2438
|
+
}
|
|
2439
|
+
catch (err) {
|
|
2440
|
+
this.logger?.warn(`${TAG} L0 会话读取失败(session=${sessionId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
2441
|
+
return [];
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
/**
|
|
2445
|
+
* 仅取 L0 消息 id 集(post-watermark 判据用)。
|
|
2446
|
+
*
|
|
2447
|
+
* 刻意**不读 `message_text`**:判据只需要 id,而正文才是内存大头。
|
|
2448
|
+
* 2.5 万条 id 约 1.5MB,比全量快照小一个数量级。
|
|
2449
|
+
*/
|
|
2450
|
+
l0MessageIds() {
|
|
2451
|
+
const out = new Set();
|
|
2452
|
+
if (this.degraded)
|
|
2453
|
+
return out;
|
|
2454
|
+
try {
|
|
2455
|
+
for (const r of this.db.prepare('SELECT record_id FROM l0_conversations').iterate()) {
|
|
2456
|
+
if (typeof r.record_id === 'string' && r.record_id !== '')
|
|
2457
|
+
out.add(r.record_id);
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
catch (err) {
|
|
2461
|
+
this.logger?.warn(`${TAG} L0 id 集读取失败(按空处理): ${err instanceof Error ? err.message : String(err)}`);
|
|
2462
|
+
}
|
|
2463
|
+
return out;
|
|
2464
|
+
}
|
|
2408
2465
|
/** L0 全量列举(重建快照用;按时间升序,事务一致性避开 JSONL 追加竞态)。 */
|
|
2409
2466
|
listL0All() {
|
|
2410
2467
|
if (this.degraded)
|
|
@@ -2434,6 +2491,47 @@ export class MemoryDb {
|
|
|
2434
2491
|
return { sessions: 0, messages: 0, chars: 0 };
|
|
2435
2492
|
}
|
|
2436
2493
|
}
|
|
2494
|
+
/**
|
|
2495
|
+
* 重建水位探针(issue #14)。
|
|
2496
|
+
*
|
|
2497
|
+
* 回答三个问题:
|
|
2498
|
+
* - 本地 L0 **覆盖到什么时候**(`l0Iso`):L0 正文已于 T8.4 写停,这个值是
|
|
2499
|
+
* **常量**,不会随使用前移;
|
|
2500
|
+
* - 检索库现有多少条、其中多少条**晚于**该水位(`l1After`):**仅用于面板提示**
|
|
2501
|
+
* “本次重建只覆盖到 X”,**绝不用作拒绝门槛** —— L1 的 `created_time` 每次
|
|
2502
|
+
* 蒸馏都会前移,拿它做阈值会把重建永久锁死;
|
|
2503
|
+
* - `l0SessionCount`:面板口径(多少个会话的正文在快照里)。
|
|
2504
|
+
*
|
|
2505
|
+
* 注意 `l0_conversations` **没有 `created_time` 列**,只有 `recorded_at`(TEXT)
|
|
2506
|
+
* 与 `timestamp`(INTEGER ms):主用前者(与 L1 同为 ISO 文本可直接比),
|
|
2507
|
+
* 空串回退后者。两列都建了索引,`MAX` 走覆盖索引。
|
|
2508
|
+
*
|
|
2509
|
+
* L0 全空时 `l0Iso` 为 `null`,此时 `l1After` 取 `l1Total`(没有水位而 L1 有记录
|
|
2510
|
+
* = 一条都重蒸不出来),而不是 0 —— 否则面板会把“最危险的情况”显示成“无影响”。
|
|
2511
|
+
*/
|
|
2512
|
+
rebuildWatermark() {
|
|
2513
|
+
const empty = { l0Iso: null, l0Ms: null, l0SessionCount: 0, l1Total: 0, l1After: 0 };
|
|
2514
|
+
if (this.degraded)
|
|
2515
|
+
return empty;
|
|
2516
|
+
try {
|
|
2517
|
+
const l0 = this.db
|
|
2518
|
+
.prepare('SELECT COUNT(DISTINCT session_id) AS s, MAX(recorded_at) AS iso, MAX(timestamp) AS ms FROM l0_conversations')
|
|
2519
|
+
.get();
|
|
2520
|
+
const ms = typeof l0?.ms === 'number' ? l0.ms : null;
|
|
2521
|
+
const rawIso = typeof l0?.iso === 'string' ? l0.iso.trim() : '';
|
|
2522
|
+
const iso = rawIso !== '' ? rawIso : ms === null ? null : new Date(ms).toISOString();
|
|
2523
|
+
const total = this.db.prepare('SELECT COUNT(*) AS n FROM l1_records').get();
|
|
2524
|
+
const l1Total = total?.n ?? 0;
|
|
2525
|
+
const l1After = iso
|
|
2526
|
+
? this.db.prepare('SELECT COUNT(*) AS n FROM l1_records WHERE created_time > ?').get(iso).n ?? 0
|
|
2527
|
+
: l1Total;
|
|
2528
|
+
return { l0Iso: iso, l0Ms: ms, l0SessionCount: l0?.s ?? 0, l1Total, l1After };
|
|
2529
|
+
}
|
|
2530
|
+
catch (err) {
|
|
2531
|
+
this.logger?.warn(`${TAG} 重建水位探针失败(按空处理): ${err instanceof Error ? err.message : String(err)}`);
|
|
2532
|
+
return empty;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2437
2535
|
/** 向量表行数(backfill 判据:与元数据行数的差值即缺失向量数;不可用时返回 -1)。 */
|
|
2438
2536
|
countL1Vec() {
|
|
2439
2537
|
if (this.degraded || !this.stmtSearchL1Vec)
|
package/dist/tools/index.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { createHash } from 'node:crypto';
|
|
15
15
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
16
16
|
import { DOWNGRADE_TARGET, familyOf, isKnownType, typeEnumSep } from '../memory-types.js';
|
|
17
|
+
import { graphFeatureOn } from '../graph/gate.js';
|
|
17
18
|
import { drscRoleOf } from '../clients/drsc.js';
|
|
18
19
|
import { RECEIPTS_QUERY_LIMIT_MAX, dimensionOf, toReceiptView, buildReceipts, newRunId } from '../store/receipts.js';
|
|
19
20
|
import { persistReceiptsSafely } from '../store/receipts.js';
|
|
@@ -1049,7 +1050,7 @@ graphWrite) {
|
|
|
1049
1050
|
},
|
|
1050
1051
|
})));
|
|
1051
1052
|
// ── 图谱工具(读;受与 memory_search 同款的档位/注入拒读门 + 族过滤) ──
|
|
1052
|
-
const GRAPH_OFF_NOTICE = '
|
|
1053
|
+
const GRAPH_OFF_NOTICE = '知识图谱未启用:请在概览页开启「知识图谱」(或部署配置 graph.enabled),当前没有任何可用的知识图谱。';
|
|
1053
1054
|
// memory_search_graph: 图谱节点检索(紧凑节点卡)
|
|
1054
1055
|
reg('graph', () => ctx.tools.register(defineTool({
|
|
1055
1056
|
name: 'memory_search_graph',
|
|
@@ -1203,7 +1204,7 @@ graphWrite) {
|
|
|
1203
1204
|
},
|
|
1204
1205
|
execute: async (args) => {
|
|
1205
1206
|
const graph = stores.graph;
|
|
1206
|
-
if (!graph || !cfg
|
|
1207
|
+
if (!graph || !graphFeatureOn(cfg, live))
|
|
1207
1208
|
return { queued: 0, job_ids: [], skipped_ids: [], notice: GRAPH_OFF_NOTICE };
|
|
1208
1209
|
if (!graphWrite)
|
|
1209
1210
|
return { queued: 0, job_ids: [], skipped_ids: [], notice: '图谱写面未接线(runner 未装配),无法入队。' };
|
|
@@ -1273,7 +1274,7 @@ graphWrite) {
|
|
|
1273
1274
|
if (!live.get().memoryMutate)
|
|
1274
1275
|
return { notice: MUTATE_OFF_NOTICE };
|
|
1275
1276
|
const graph = stores.graph;
|
|
1276
|
-
if (!graph || !cfg
|
|
1277
|
+
if (!graph || !graphFeatureOn(cfg, live))
|
|
1277
1278
|
return { notice: GRAPH_OFF_NOTICE };
|
|
1278
1279
|
if (!graphWrite)
|
|
1279
1280
|
return { notice: '图谱写面未接线(runner 未装配),无法迁移。' };
|
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.11",
|
|
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 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"engines": {
|
|
16
|
-
"dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2"
|
|
16
|
+
"dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-rc.1 || 0.2.1-rc.2 || 0.2.1-rc.3"
|
|
17
17
|
},
|
|
18
18
|
"components": {
|
|
19
19
|
"host": "dist/index.js",
|