dsh-prime-memory 0.21.0 → 0.22.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/dist/client.js +1581 -1150
- package/dist/config.d.ts +149 -0
- package/dist/config.js +35 -0
- package/dist/conflict-service.d.ts +8 -0
- package/dist/conflict-service.js +19 -0
- package/dist/contract.d.ts +131 -0
- package/dist/domain-gate.d.ts +8 -0
- package/dist/domain-gate.js +30 -0
- package/dist/graph/constraints.d.ts +4 -2
- package/dist/graph/constraints.js +4 -2
- package/dist/hooks/recall.d.ts +2 -0
- package/dist/hooks/recall.js +21 -2
- package/dist/hooks/skill-recall.d.ts +25 -0
- package/dist/hooks/skill-recall.js +72 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.js +26 -1
- package/dist/memport/format.d.ts +70 -0
- package/dist/memport/format.js +305 -0
- package/dist/memport/prefilter.d.ts +62 -0
- package/dist/memport/prefilter.js +132 -0
- package/dist/memport/scan.d.ts +13 -0
- package/dist/memport/scan.js +100 -0
- package/dist/metadata-validators.d.ts +22 -0
- package/dist/metadata-validators.js +45 -0
- package/dist/pipeline/l1.js +16 -2
- package/dist/pipeline/ruminate.d.ts +3 -0
- package/dist/pipeline/ruminate.js +50 -1
- package/dist/pipeline/slot-migrate.d.ts +45 -0
- package/dist/pipeline/slot-migrate.js +45 -0
- package/dist/projection/recall.d.ts +74 -0
- package/dist/projection/recall.js +148 -0
- package/dist/projection/slots.d.ts +5 -0
- package/dist/projection/slots.js +9 -2
- package/dist/prompts/graph-projection.js +3 -0
- package/dist/prompts/l1-extraction.d.ts +4 -1
- package/dist/prompts/l1-extraction.js +27 -4
- package/dist/settings.js +106 -11
- package/dist/skills/crystallize.d.ts +41 -0
- package/dist/skills/crystallize.js +75 -0
- package/dist/stats.d.ts +18 -1
- package/dist/stats.js +184 -5
- package/dist/store/file-versions.d.ts +2 -0
- package/dist/store/file-versions.js +2 -0
- package/dist/store/l1.d.ts +2 -0
- package/dist/store/l1.js +4 -0
- package/dist/store/memory-sources.d.ts +59 -0
- package/dist/store/memory-sources.js +214 -0
- package/dist/store/session-recalled.d.ts +25 -0
- package/dist/store/session-recalled.js +163 -0
- package/dist/store/skill-store.d.ts +99 -0
- package/dist/store/skill-store.js +330 -0
- package/dist/store/sqlite.d.ts +5 -0
- package/dist/store/sqlite.js +30 -4
- package/dist/tools/index.d.ts +0 -13
- package/dist/tools/index.js +58 -7
- package/dist/tools/skills.d.ts +13 -0
- package/dist/tools/skills.js +130 -0
- package/dist/types.d.ts +6 -0
- package/dist/util/maturity.d.ts +27 -0
- package/dist/util/maturity.js +26 -0
- package/dist/wing-backfill.js +7 -4
- package/dsh.plugin.json +2 -2
- package/package.json +14 -14
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 模型可调用工具:memory_search(L1)、conversation_search(L0)、memory_read_scene(L2/L3)、
|
|
3
|
-
* 高权限 memory_add / memory_delete。
|
|
4
|
-
*
|
|
5
|
-
* 会话档位联动:execute 的 exec.agent 即发起调用的 agent(agent.id === sessionId),
|
|
6
|
-
* memory_search 按会话档位过滤族(auto 不过滤,纯档只查本族);off 档下读类工具统一
|
|
7
|
-
* 返回提示(本会话已对记忆系统隐身)。conversation_search 检索范围保持全库。
|
|
8
|
-
* 只写会话(注入覆盖=关)同款拒读,notice 区分文案——写入走捕获钩子不经工具,
|
|
9
|
-
* 拒读不影响"只写"语义。
|
|
10
|
-
*
|
|
11
|
-
* 写删工具受 live.memoryMutate 高权限门控(默认 false):显式记忆指令语义——
|
|
12
|
-
* 用户说"记得 X" → memory_add;说"忘了/删除该记忆" → memory_delete。
|
|
13
|
-
*/
|
|
14
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
2
|
import type { MemoryConfig } from '../config.js';
|
|
16
3
|
import type { LiveSettingsHandle } from '../settings.js';
|
package/dist/tools/index.js
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型可调用工具:memory_search(L1)、conversation_search(L0)、memory_read_scene(L2/L3)、
|
|
3
|
+
* 高权限 memory_add / memory_delete。
|
|
4
|
+
*
|
|
5
|
+
* 会话档位联动:execute 的 exec.agent 即发起调用的 agent(agent.id === sessionId),
|
|
6
|
+
* memory_search 按会话档位过滤族(auto 不过滤,纯档只查本族);off 档下读类工具统一
|
|
7
|
+
* 返回提示(本会话已对记忆系统隐身)。conversation_search 检索范围保持全库。
|
|
8
|
+
* 只写会话(注入覆盖=关)同款拒读,notice 区分文案——写入走捕获钩子不经工具,
|
|
9
|
+
* 拒读不影响"只写"语义。
|
|
10
|
+
*
|
|
11
|
+
* 写删工具受 live.memoryMutate 高权限门控(默认 false):显式记忆指令语义——
|
|
12
|
+
* 用户说"记得 X" → memory_add;说"忘了/删除该记忆" → memory_delete。
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
1
15
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
16
|
import { RECEIPTS_QUERY_LIMIT_MAX, dimensionOf, toReceiptView } from '../store/receipts.js';
|
|
3
17
|
import { listConflictPairs, renderConflictResolution, renderConflicts, resolveConflictPair } from '../conflict-service.js';
|
|
@@ -491,7 +505,14 @@ roomRegistry) {
|
|
|
491
505
|
* 时间轴同时写顶层字段(时间增强列)与 metadata(列迁移前的兼容层,
|
|
492
506
|
* 也是面板与图谱时间锚的读取点);`cf`/`rw` 落在 metadata.conflict/rewritten。
|
|
493
507
|
*/
|
|
494
|
-
|
|
508
|
+
/** 外部导入幂等键:sha1(source_id \0 稳定身份)。身份取 dedupe_key,缺省用内容规范化。 */
|
|
509
|
+
const computeExtKey = (sourceId, item, content) => {
|
|
510
|
+
const identity = typeof item.dedupe_key === 'string' && item.dedupe_key.trim()
|
|
511
|
+
? item.dedupe_key.trim()
|
|
512
|
+
: content.replace(/\s+/g, ' ').toLowerCase();
|
|
513
|
+
return createHash('sha1').update(`${sourceId}\u0000${identity}`, 'utf8').digest('hex');
|
|
514
|
+
};
|
|
515
|
+
function buildRecord(item, sceneName, now, workspaceId, sourceId) {
|
|
495
516
|
const content = String(item.content ?? '').trim();
|
|
496
517
|
const type = ADD_TYPES.includes(String(item.type ?? '')) ? String(item.type) : 'episodic';
|
|
497
518
|
const family = type.startsWith('work') ? 'work' : 'chat';
|
|
@@ -536,6 +557,7 @@ roomRegistry) {
|
|
|
536
557
|
...(validFrom !== undefined ? { validFrom } : {}),
|
|
537
558
|
...(validTo !== undefined ? { validTo } : {}),
|
|
538
559
|
...(persistence !== undefined ? { persistence } : {}),
|
|
560
|
+
...(sourceId && sourceId.trim() ? { extKey: computeExtKey(sourceId.trim(), item, content) } : {}),
|
|
539
561
|
};
|
|
540
562
|
}
|
|
541
563
|
/** 时间轴/溯源字段的可选入参(两个写工具共用同一形状)。 */
|
|
@@ -618,6 +640,10 @@ roomRegistry) {
|
|
|
618
640
|
},
|
|
619
641
|
...writeFields,
|
|
620
642
|
priority: { type: 'number', description: '优先级 0-100(缺省 80)' },
|
|
643
|
+
dedupe_key: {
|
|
644
|
+
type: 'string',
|
|
645
|
+
description: '可选稳定身份(外部包内该条的稳定 id);与顶层 source_id 组合算幂等键,缺省用内容规范化哈希',
|
|
646
|
+
},
|
|
621
647
|
},
|
|
622
648
|
},
|
|
623
649
|
},
|
|
@@ -625,12 +651,17 @@ roomRegistry) {
|
|
|
625
651
|
type: 'string',
|
|
626
652
|
description: `归属场景名(缺省 ${DEFAULT_IMPORT_SCENE};建议用"${DEFAULT_IMPORT_SCENE}/<来源>")`,
|
|
627
653
|
},
|
|
654
|
+
source_id: {
|
|
655
|
+
type: 'string',
|
|
656
|
+
description: '来源标识(如 "workbuddy"/"claude-code"/外部包名);非空时启用幂等——同一来源重复导入相同 dedupe_key/内容会被跳过并计入 deduped',
|
|
657
|
+
},
|
|
628
658
|
},
|
|
629
659
|
output: {
|
|
630
660
|
schema: {
|
|
631
661
|
type: 'object',
|
|
632
662
|
properties: {
|
|
633
663
|
written: { type: 'number' },
|
|
664
|
+
deduped: { type: 'number' },
|
|
634
665
|
ids: { type: 'array', items: { type: 'string' } },
|
|
635
666
|
skipped: {
|
|
636
667
|
type: 'array',
|
|
@@ -653,15 +684,16 @@ roomRegistry) {
|
|
|
653
684
|
},
|
|
654
685
|
execute: async (args, exec) => {
|
|
655
686
|
if (!live.get().memoryMutate) {
|
|
656
|
-
return { written: 0, ids: [], skipped: [], notice: MUTATE_OFF_NOTICE };
|
|
687
|
+
return { written: 0, deduped: 0, ids: [], skipped: [], notice: MUTATE_OFF_NOTICE };
|
|
657
688
|
}
|
|
658
689
|
const raw = Array.isArray(args.records) ? args.records : [];
|
|
659
690
|
if (raw.length === 0) {
|
|
660
|
-
return { written: 0, ids: [], skipped: [], notice: 'records 为空,未写入' };
|
|
691
|
+
return { written: 0, deduped: 0, ids: [], skipped: [], notice: 'records 为空,未写入' };
|
|
661
692
|
}
|
|
662
693
|
if (raw.length > MAX_IMPORT) {
|
|
663
694
|
return {
|
|
664
695
|
written: 0,
|
|
696
|
+
deduped: 0,
|
|
665
697
|
ids: [],
|
|
666
698
|
skipped: [],
|
|
667
699
|
notice: `单次上限 ${MAX_IMPORT} 条(收到 ${raw.length} 条),请拆批导入`,
|
|
@@ -670,10 +702,13 @@ roomRegistry) {
|
|
|
670
702
|
const scene = typeof args.scene === 'string' && args.scene.trim()
|
|
671
703
|
? args.scene.trim().slice(0, 120)
|
|
672
704
|
: DEFAULT_IMPORT_SCENE;
|
|
705
|
+
const sourceId = typeof args.source_id === 'string' ? args.source_id.trim() : '';
|
|
673
706
|
const now = Date.now();
|
|
674
707
|
const seen = new Set();
|
|
708
|
+
const seenExt = new Set();
|
|
675
709
|
const records = [];
|
|
676
710
|
const skipped = [];
|
|
711
|
+
let deduped = 0;
|
|
677
712
|
raw.forEach((item, index) => {
|
|
678
713
|
const content = String(item?.content ?? '').trim();
|
|
679
714
|
if (!content) {
|
|
@@ -686,14 +721,30 @@ roomRegistry) {
|
|
|
686
721
|
return;
|
|
687
722
|
}
|
|
688
723
|
seen.add(key);
|
|
689
|
-
|
|
724
|
+
// 幂等(轨3-P0):带 source_id 时按 ext_key 拦截重复——先查库内、再查本批,
|
|
725
|
+
// 命中即计入 deduped 并跳过(不写 JSONL、不写检索库)。无 source_id 保持既有逐字行为。
|
|
726
|
+
if (sourceId) {
|
|
727
|
+
const extKey = computeExtKey(sourceId, item, content);
|
|
728
|
+
if (seenExt.has(extKey)) {
|
|
729
|
+
deduped++;
|
|
730
|
+
skipped.push({ index, reason: '与本批前面的记录外部键重复(幂等跳过)' });
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (stores.l1.findIdByExtKey(extKey)) {
|
|
734
|
+
deduped++;
|
|
735
|
+
skipped.push({ index, reason: '库内已存在同外部键记录(幂等跳过)' });
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
seenExt.add(extKey);
|
|
739
|
+
}
|
|
740
|
+
const record = buildRecord(item, scene, now, workspaceIdOf(exec), sourceId);
|
|
690
741
|
applyManualRedaction(record, cfg, logger);
|
|
691
742
|
records.push(record);
|
|
692
743
|
});
|
|
693
744
|
if (records.length > 0)
|
|
694
745
|
await stores.l1.appendNew(records);
|
|
695
|
-
logger.info(`[memory] 批量导入 ${records.length} 条(
|
|
696
|
-
return { written: records.length, ids: records.map((r) => r.id), skipped };
|
|
746
|
+
logger.info(`[memory] 批量导入 ${records.length} 条(幂等跳过 ${deduped} 条,场景 ${scene}${sourceId ? `,来源 ${sourceId}` : ''})`);
|
|
747
|
+
return { written: records.length, deduped, ids: records.map((r) => r.id), skipped };
|
|
697
748
|
},
|
|
698
749
|
})));
|
|
699
750
|
// memory_delete:显式"忘了 X"——按语义检索命中后删除(高权限门控)。
|
|
@@ -1222,7 +1273,7 @@ roomRegistry) {
|
|
|
1222
1273
|
if (family === null) {
|
|
1223
1274
|
return { enabled: false, total: 0, items: [], notice: blockNoticeOf(exec) };
|
|
1224
1275
|
}
|
|
1225
|
-
return listConflictPairs({ l1: stores.l1, conflictFreezeEnabled: live.get().conflictFreeze === true }, { limit: typeof args.limit === 'number' ? args.limit : undefined });
|
|
1276
|
+
return listConflictPairs({ l1: stores.l1, conflictFreezeEnabled: live.get().conflictFreeze === true, decayHalfLifeDays: cfg.recall?.decayHalfLifeDays }, { limit: typeof args.limit === 'number' ? args.limit : undefined });
|
|
1226
1277
|
},
|
|
1227
1278
|
})));
|
|
1228
1279
|
// ── memory_conflicts_rejected: §C 丢弃留痕的读出口 ──
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 技能工具面(轨2)。**整块仅在 cfg.skills.enabled 时注册**——默认关时零表面积
|
|
3
|
+
* (对齐 benchControl"生产部署零表面积"先例,避免注册了却被门控的空壳工具污染模型工具列表)。
|
|
4
|
+
*
|
|
5
|
+
* 读工具 list/read;写工具 pin/archive 过 live.memoryMutate 高权限门(与 memory_add 同门)。
|
|
6
|
+
* 技能不是 memory `type`,这些工具只操作 SkillStore sidecar,不触碰 l1_records / ADD_TYPES / prompt 词表。
|
|
7
|
+
*/
|
|
8
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
9
|
+
import type { MemoryConfig } from '../config.js';
|
|
10
|
+
import type { LiveSettingsHandle } from '../settings.js';
|
|
11
|
+
import type { SkillStore } from '../store/skill-store.js';
|
|
12
|
+
import type { MemoryLogger } from '../types.js';
|
|
13
|
+
export declare function registerSkillTools(ctx: Context, cfg: MemoryConfig, skills: SkillStore, logger: MemoryLogger, live: LiveSettingsHandle): void;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
const MUTATE_OFF_NOTICE = '记忆写删未开放:请在记忆库面板开启「高权限模式」后,模型才能改动技能。';
|
|
3
|
+
export function registerSkillTools(ctx, cfg, skills, logger, live) {
|
|
4
|
+
if (!(cfg.skills.enabled === true || live.get().skillsEnabled === true))
|
|
5
|
+
return; // 默认关:部署 cfg 或看板 live 任一开才注册
|
|
6
|
+
// memory_skill_list:列全部技能摘要(含 status/sessionCount,candidate 也列出供审阅)。
|
|
7
|
+
ctx.tools.register(defineTool({
|
|
8
|
+
name: 'memory_skill_list',
|
|
9
|
+
description: '列出已沉淀的技能(程序性记忆)摘要:id/标题/状态/档位/覆盖会话数。用于查看技能库现状与候选晋升情况。',
|
|
10
|
+
parameters: {},
|
|
11
|
+
output: {
|
|
12
|
+
schema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
skills: {
|
|
16
|
+
type: 'array',
|
|
17
|
+
items: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
id: { type: 'string' },
|
|
21
|
+
title: { type: 'string' },
|
|
22
|
+
status: { type: 'string' },
|
|
23
|
+
tier: { type: 'string' },
|
|
24
|
+
pinned: { type: 'boolean' },
|
|
25
|
+
sessionCount: { type: 'number' },
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
notice: { type: 'string' },
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
render: (_a, v) => [{ type: 'text', text: v.notice ?? `共 ${v.skills?.length ?? 0} 个技能` }],
|
|
35
|
+
},
|
|
36
|
+
execute: async () => {
|
|
37
|
+
const list = skills.list().map((s) => ({
|
|
38
|
+
id: s.id,
|
|
39
|
+
title: s.title,
|
|
40
|
+
status: s.status,
|
|
41
|
+
tier: s.injectionTier,
|
|
42
|
+
pinned: s.pinned,
|
|
43
|
+
sessionCount: skills.sessionCount(s.id),
|
|
44
|
+
}));
|
|
45
|
+
return { skills: list, notice: `共 ${list.length} 个技能` };
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
// memory_skill_read:取某技能完整正文 + 步骤。
|
|
49
|
+
ctx.tools.register(defineTool({
|
|
50
|
+
name: 'memory_skill_read',
|
|
51
|
+
description: '读取一条技能的完整正文与步骤清单(按 id)。用于展开注入里被截断/excerpt 档的技能细节。',
|
|
52
|
+
parameters: { id: { type: 'string', required: true, description: '技能 id(来自 memory_skill_list 或注入头)' } },
|
|
53
|
+
output: {
|
|
54
|
+
schema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
id: { type: 'string' },
|
|
58
|
+
title: { type: 'string' },
|
|
59
|
+
body: { type: 'string' },
|
|
60
|
+
steps: { type: 'array', items: { type: 'string' } },
|
|
61
|
+
notice: { type: 'string' },
|
|
62
|
+
},
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
},
|
|
65
|
+
render: (_a, v) => [
|
|
66
|
+
{
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: v.notice ?? [`【${v.title}】`, v.body, ...(v.steps || []).map((s) => ` - ${s}`)].join('\n'),
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
execute: async (args) => {
|
|
73
|
+
const id = String(args?.id ?? '').trim();
|
|
74
|
+
const s = skills.get(id);
|
|
75
|
+
if (!s)
|
|
76
|
+
return { id, title: '', body: '', steps: [], notice: `未找到技能 ${id}` };
|
|
77
|
+
return { id: s.id, title: s.title, body: s.body, steps: s.steps };
|
|
78
|
+
},
|
|
79
|
+
}));
|
|
80
|
+
// memory_skill_pin:pin/unpin(高权限门)。pinned 技能不受闲置归档影响。
|
|
81
|
+
ctx.tools.register(defineTool({
|
|
82
|
+
name: 'memory_skill_pin',
|
|
83
|
+
description: '固定/取消固定一条技能(按 id)。固定后不参与闲置自动归档。仅用户显式要求时用;需高权限模式。',
|
|
84
|
+
parameters: {
|
|
85
|
+
id: { type: 'string', required: true, description: '技能 id' },
|
|
86
|
+
pinned: { type: 'boolean', required: true, description: 'true=固定,false=取消固定' },
|
|
87
|
+
},
|
|
88
|
+
output: {
|
|
89
|
+
schema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: { notice: { type: 'string' } },
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
},
|
|
94
|
+
render: (_a, v) => [{ type: 'text', text: v.notice ?? '' }],
|
|
95
|
+
},
|
|
96
|
+
execute: async (args) => {
|
|
97
|
+
if (!live.get().memoryMutate)
|
|
98
|
+
return { notice: MUTATE_OFF_NOTICE };
|
|
99
|
+
const id = String(args?.id ?? '').trim();
|
|
100
|
+
const changed = await skills.update(id, { pinned: args?.pinned === true });
|
|
101
|
+
return { notice: changed ? `已${args?.pinned === true ? '固定' : '取消固定'}技能 ${id}` : `未找到技能 ${id} 或无变化` };
|
|
102
|
+
},
|
|
103
|
+
}));
|
|
104
|
+
// memory_skill_archive:归档(高权限门);走 sidecar 生命周期,不触碰 l1_records 退场。
|
|
105
|
+
ctx.tools.register(defineTool({
|
|
106
|
+
name: 'memory_skill_archive',
|
|
107
|
+
description: '归档一条技能(按 id,可给原因)。归档后不再注入,但记录保留可恢复。仅用户显式要求时用;需高权限模式。',
|
|
108
|
+
parameters: {
|
|
109
|
+
id: { type: 'string', required: true, description: '技能 id' },
|
|
110
|
+
reason: { type: 'string', description: '归档原因(可选,供审计)' },
|
|
111
|
+
},
|
|
112
|
+
output: {
|
|
113
|
+
schema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: { notice: { type: 'string' } },
|
|
116
|
+
additionalProperties: false,
|
|
117
|
+
},
|
|
118
|
+
render: (_a, v) => [{ type: 'text', text: v.notice ?? '' }],
|
|
119
|
+
},
|
|
120
|
+
execute: async (args) => {
|
|
121
|
+
if (!live.get().memoryMutate)
|
|
122
|
+
return { notice: MUTATE_OFF_NOTICE };
|
|
123
|
+
const id = String(args?.id ?? '').trim();
|
|
124
|
+
const done = await skills.archive(id, String(args?.reason ?? 'manual').slice(0, 120) || 'manual');
|
|
125
|
+
if (done)
|
|
126
|
+
logger.info(`[memory] 技能归档 ${id}`);
|
|
127
|
+
return { notice: done ? `已归档技能 ${id}` : `未找到技能 ${id} 或已归档` };
|
|
128
|
+
},
|
|
129
|
+
}));
|
|
130
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -226,6 +226,12 @@ export interface MemoryRecord {
|
|
|
226
226
|
repoKeyOwner?: string;
|
|
227
227
|
/** 记录级 applicability(ADR-0015 T1.10):'' 未声明(不围栏)| 'this-repo' | 'cross-project';显式不从 family 推导。 */
|
|
228
228
|
applicability?: string;
|
|
229
|
+
/**
|
|
230
|
+
* 外部导入幂等键(轨3-P0):`sha1(source_id \0 身份)`;非外部导入(内部蒸馏/add)= undefined/''。
|
|
231
|
+
* 非空值受 `l1_records` 上部分唯一索引约束——同一 (来源, 内容/稳定身份) 重复导入结构性不可能。
|
|
232
|
+
* 与 `scene_name`/`metadata.origin` 不同:这是**存储层强制去重键**,不是展示用的溯源标签。
|
|
233
|
+
*/
|
|
234
|
+
extKey?: string;
|
|
229
235
|
}
|
|
230
236
|
/** L2 场景块摘要(META 解析结果)。 */
|
|
231
237
|
export interface SceneSummary {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 成熟度(maturity)合成——G3,单一事实源的纯函数。
|
|
3
|
+
*
|
|
4
|
+
* 把三个**已存在**的信号合成一个可解释的置信档,供面板显示(后续可作召回排序/矛盾旁注):
|
|
5
|
+
* - `priority`(L1 真实列,0-100):写入时的重要性先验;
|
|
6
|
+
* - `usage.used`(recall-usage,进 topN 被注入才计):被反复用到 = 越"确立";
|
|
7
|
+
* - `agingWeight`(读路径衰减系数 0.5~1,null=关):新鲜度。
|
|
8
|
+
*
|
|
9
|
+
* 刻意**读时派生、不落库**(Agent B/C 结论):落库会在每轮 recordAttempt 触发双写 + 值随时间陈旧,
|
|
10
|
+
* 且顶层列不进快照哈希=隐形改写。三输入即时可得,纯函数零 IO。
|
|
11
|
+
*
|
|
12
|
+
* 权重/阈值集中为常量,便于回归与调参;缺 usage(未装配)按 0 强化处理(不虚构成熟度)。
|
|
13
|
+
*/
|
|
14
|
+
export type Maturity = 'low' | 'mid' | 'high';
|
|
15
|
+
export interface MaturityUsage {
|
|
16
|
+
used: number;
|
|
17
|
+
}
|
|
18
|
+
export interface MaturityInput {
|
|
19
|
+
priority: number;
|
|
20
|
+
usage?: MaturityUsage | null;
|
|
21
|
+
/** 读路径老化权重(0.5~1);null = 衰减关闭 → 视为 1(不惩罚)。 */
|
|
22
|
+
agingWeight?: number | null;
|
|
23
|
+
}
|
|
24
|
+
/** 合成分值(0~1),导出供测试与未来排序复用。 */
|
|
25
|
+
export declare function maturityScore(i: MaturityInput): number;
|
|
26
|
+
/** 三档成熟度:high ≥0.66、mid ≥0.40、否则 low。 */
|
|
27
|
+
export declare function computeMaturity(i: MaturityInput): Maturity;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const W_PRIORITY = 0.45;
|
|
2
|
+
const W_USAGE = 0.3;
|
|
3
|
+
const W_AGING = 0.25;
|
|
4
|
+
/** used 达到该数即满强化(再高不加分)。 */
|
|
5
|
+
const USAGE_SATURATE = 5;
|
|
6
|
+
const HIGH_THRESHOLD = 0.66;
|
|
7
|
+
const MID_THRESHOLD = 0.4;
|
|
8
|
+
function clamp01(x) {
|
|
9
|
+
return Math.max(0, Math.min(1, x));
|
|
10
|
+
}
|
|
11
|
+
/** 合成分值(0~1),导出供测试与未来排序复用。 */
|
|
12
|
+
export function maturityScore(i) {
|
|
13
|
+
const p = clamp01(i.priority / 100);
|
|
14
|
+
const u = i.usage ? clamp01(i.usage.used / USAGE_SATURATE) : 0;
|
|
15
|
+
const a = i.agingWeight == null ? 1 : clamp01(i.agingWeight);
|
|
16
|
+
return W_PRIORITY * p + W_USAGE * u + W_AGING * a;
|
|
17
|
+
}
|
|
18
|
+
/** 三档成熟度:high ≥0.66、mid ≥0.40、否则 low。 */
|
|
19
|
+
export function computeMaturity(i) {
|
|
20
|
+
const s = maturityScore(i);
|
|
21
|
+
if (s >= HIGH_THRESHOLD)
|
|
22
|
+
return 'high';
|
|
23
|
+
if (s >= MID_THRESHOLD)
|
|
24
|
+
return 'mid';
|
|
25
|
+
return 'low';
|
|
26
|
+
}
|
package/dist/wing-backfill.js
CHANGED
|
@@ -4,8 +4,10 @@ import { WING_FALLBACK } from './types.js';
|
|
|
4
4
|
import { parseJsonLogged } from './llm.js';
|
|
5
5
|
import { isWingId, normTags } from './metadata-validators.js';
|
|
6
6
|
import { yieldLoop } from './util/yield.js';
|
|
7
|
-
/**
|
|
8
|
-
const
|
|
7
|
+
/** 单次任务处理上限默认值(可被 cfg.backfill.maxRecords 覆盖;分批触发,可重复执行)。 */
|
|
8
|
+
const DEFAULT_MAX_RECORDS = 300;
|
|
9
|
+
/** 墙钟预算默认值 ms(可被 cfg.backfill.timeBudgetMs 覆盖):超时剩留待再次触发。 */
|
|
10
|
+
const DEFAULT_TIME_BUDGET_MS = 120_000;
|
|
9
11
|
/** 每块条数(一次 LLM 调用判定的记录数)。 */
|
|
10
12
|
const CHUNK = 20;
|
|
11
13
|
const state = { running: false, updated: 0, failed: 0 };
|
|
@@ -31,14 +33,15 @@ export function startWingBackfill(deps) {
|
|
|
31
33
|
}
|
|
32
34
|
async function run(deps) {
|
|
33
35
|
const { ctx, cfg, backend, logger } = deps;
|
|
36
|
+
const MAX_RECORDS = cfg.backfill?.maxRecords ?? DEFAULT_MAX_RECORDS;
|
|
37
|
+
const TIME_BUDGET_MS = cfg.backfill?.timeBudgetMs ?? DEFAULT_TIME_BUDGET_MS;
|
|
34
38
|
const halls = normWingEnabled(cfg.hall?.enabled);
|
|
35
39
|
if (halls.length === 0) {
|
|
36
40
|
logger.warn('[memory] wing 回填跳过:wing.enabled 为空(打标功能已关闭)');
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
39
|
-
//
|
|
43
|
+
// 墙钟预算(cfg.backfill.timeBudgetMs,默认 120s):超时即停,剩余留待再次触发(任务本身设计为可重复执行)。
|
|
40
44
|
// 保证后台回填不长时间占据事件循环,设置面板的状态 RPC 始终优先。
|
|
41
|
-
const TIME_BUDGET_MS = 120_000;
|
|
42
45
|
const startedAt = Date.now();
|
|
43
46
|
const candidates = [...halls, WING_FALLBACK].join(' / ');
|
|
44
47
|
// 未打标集:游标分批扫描轻量投影(不一次性全量反序列化含正文的记录)。
|
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.
|
|
4
|
+
"version": "0.22.2",
|
|
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.
|
|
16
|
+
"dsh": ">=0.1.0-rc.2"
|
|
17
17
|
},
|
|
18
18
|
"components": {
|
|
19
19
|
"host": "dist/index.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-prime-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.2",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org",
|
|
6
6
|
"access": "public",
|
|
@@ -24,8 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"client": {
|
|
26
26
|
"inject": [
|
|
27
|
-
"@deepseek-ai/dsh-client-ui-primitives"
|
|
28
|
-
"@deepseek-ai/dsh-client-connection"
|
|
27
|
+
"@deepseek-ai/dsh-client-ui-primitives"
|
|
29
28
|
],
|
|
30
29
|
"platform": "web"
|
|
31
30
|
}
|
|
@@ -57,11 +56,12 @@
|
|
|
57
56
|
"build:smoke": "tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution nodenext --target es2022 --strict --skipLibCheck --esModuleInterop",
|
|
58
57
|
"lint": "eslint src client/src scripts tests",
|
|
59
58
|
"test": "vitest run",
|
|
60
|
-
"wing-migrate": "npm run build && node scripts/wing-migrate.mjs"
|
|
59
|
+
"wing-migrate": "npm run build && node scripts/wing-migrate.mjs",
|
|
60
|
+
"prepack": "npm run build"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=22.16.0",
|
|
64
|
-
"dsh": ">=0.
|
|
64
|
+
"dsh": ">=0.1.0-rc.2"
|
|
65
65
|
},
|
|
66
66
|
"keywords": [
|
|
67
67
|
"dsh",
|
|
@@ -83,15 +83,15 @@
|
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
85
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
86
|
-
"@deepseek-ai/dsh-agent": "
|
|
87
|
-
"@deepseek-ai/dsh-client-connection": "
|
|
88
|
-
"@deepseek-ai/dsh-client-ui-primitives": "
|
|
89
|
-
"@deepseek-ai/dsh-home-paths": "
|
|
90
|
-
"@deepseek-ai/dsh-llm": "
|
|
91
|
-
"@deepseek-ai/dsh-session": "
|
|
92
|
-
"@deepseek-ai/dsh-settings": "
|
|
93
|
-
"@deepseek-ai/dsh-system-prompt": "
|
|
94
|
-
"@deepseek-ai/dsh-tools": "
|
|
86
|
+
"@deepseek-ai/dsh-agent": "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",
|
|
87
|
+
"@deepseek-ai/dsh-client-connection": "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",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-primitives": "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",
|
|
89
|
+
"@deepseek-ai/dsh-home-paths": "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",
|
|
90
|
+
"@deepseek-ai/dsh-llm": "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",
|
|
91
|
+
"@deepseek-ai/dsh-session": "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",
|
|
92
|
+
"@deepseek-ai/dsh-settings": "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",
|
|
93
|
+
"@deepseek-ai/dsh-system-prompt": "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",
|
|
94
|
+
"@deepseek-ai/dsh-tools": "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"
|
|
95
95
|
},
|
|
96
96
|
"peerDependenciesMeta": {
|
|
97
97
|
"@deepseek-ai/dsh-agent": {
|