dsh-prime-memory 0.22.1 → 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.
Files changed (62) hide show
  1. package/dist/client.js +1581 -1153
  2. package/dist/config.d.ts +149 -0
  3. package/dist/config.js +35 -0
  4. package/dist/conflict-service.d.ts +8 -0
  5. package/dist/conflict-service.js +19 -0
  6. package/dist/contract.d.ts +131 -0
  7. package/dist/domain-gate.d.ts +8 -0
  8. package/dist/domain-gate.js +30 -0
  9. package/dist/graph/constraints.d.ts +4 -2
  10. package/dist/graph/constraints.js +4 -2
  11. package/dist/hooks/recall.d.ts +2 -0
  12. package/dist/hooks/recall.js +20 -1
  13. package/dist/hooks/skill-recall.d.ts +25 -0
  14. package/dist/hooks/skill-recall.js +72 -0
  15. package/dist/index.d.ts +98 -0
  16. package/dist/index.js +26 -1
  17. package/dist/memport/format.d.ts +70 -0
  18. package/dist/memport/format.js +305 -0
  19. package/dist/memport/prefilter.d.ts +62 -0
  20. package/dist/memport/prefilter.js +132 -0
  21. package/dist/memport/scan.d.ts +13 -0
  22. package/dist/memport/scan.js +100 -0
  23. package/dist/metadata-validators.d.ts +22 -0
  24. package/dist/metadata-validators.js +45 -0
  25. package/dist/pipeline/l1.js +16 -2
  26. package/dist/pipeline/ruminate.d.ts +3 -0
  27. package/dist/pipeline/ruminate.js +50 -1
  28. package/dist/pipeline/slot-migrate.d.ts +45 -0
  29. package/dist/pipeline/slot-migrate.js +45 -0
  30. package/dist/projection/recall.d.ts +74 -0
  31. package/dist/projection/recall.js +148 -0
  32. package/dist/projection/slots.d.ts +5 -0
  33. package/dist/prompts/graph-projection.js +3 -0
  34. package/dist/prompts/l1-extraction.d.ts +4 -1
  35. package/dist/prompts/l1-extraction.js +27 -4
  36. package/dist/settings.js +3 -0
  37. package/dist/skills/crystallize.d.ts +41 -0
  38. package/dist/skills/crystallize.js +75 -0
  39. package/dist/stats.d.ts +18 -1
  40. package/dist/stats.js +184 -5
  41. package/dist/store/file-versions.d.ts +2 -0
  42. package/dist/store/file-versions.js +2 -0
  43. package/dist/store/l1.d.ts +2 -0
  44. package/dist/store/l1.js +4 -0
  45. package/dist/store/memory-sources.d.ts +59 -0
  46. package/dist/store/memory-sources.js +214 -0
  47. package/dist/store/session-recalled.d.ts +25 -0
  48. package/dist/store/session-recalled.js +163 -0
  49. package/dist/store/skill-store.d.ts +99 -0
  50. package/dist/store/skill-store.js +330 -0
  51. package/dist/store/sqlite.d.ts +5 -0
  52. package/dist/store/sqlite.js +30 -4
  53. package/dist/tools/index.d.ts +0 -13
  54. package/dist/tools/index.js +58 -7
  55. package/dist/tools/skills.d.ts +13 -0
  56. package/dist/tools/skills.js +130 -0
  57. package/dist/types.d.ts +6 -0
  58. package/dist/util/maturity.d.ts +27 -0
  59. package/dist/util/maturity.js +26 -0
  60. package/dist/wing-backfill.js +7 -4
  61. package/dsh.plugin.json +1 -1
  62. package/package.json +1 -1
@@ -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
+ }
@@ -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
- /** 单次任务处理上限(524 条存量一次跑完约为 LLM 成本大头;分批触发,可重复执行)。 */
8
- const MAX_RECORDS = 300;
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.22.1",
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 分层蒸馏记忆插件:自动捕获对话、抽取原子记忆、整合场景块、蒸馏核心画像,并在模型步骤前自动召回注入。"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prime-memory",
3
- "version": "0.22.1",
3
+ "version": "0.22.2",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",