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.
Files changed (63) hide show
  1. package/dist/client.js +1581 -1150
  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 +21 -2
  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/projection/slots.js +9 -2
  34. package/dist/prompts/graph-projection.js +3 -0
  35. package/dist/prompts/l1-extraction.d.ts +4 -1
  36. package/dist/prompts/l1-extraction.js +27 -4
  37. package/dist/settings.js +106 -11
  38. package/dist/skills/crystallize.d.ts +41 -0
  39. package/dist/skills/crystallize.js +75 -0
  40. package/dist/stats.d.ts +18 -1
  41. package/dist/stats.js +184 -5
  42. package/dist/store/file-versions.d.ts +2 -0
  43. package/dist/store/file-versions.js +2 -0
  44. package/dist/store/l1.d.ts +2 -0
  45. package/dist/store/l1.js +4 -0
  46. package/dist/store/memory-sources.d.ts +59 -0
  47. package/dist/store/memory-sources.js +214 -0
  48. package/dist/store/session-recalled.d.ts +25 -0
  49. package/dist/store/session-recalled.js +163 -0
  50. package/dist/store/skill-store.d.ts +99 -0
  51. package/dist/store/skill-store.js +330 -0
  52. package/dist/store/sqlite.d.ts +5 -0
  53. package/dist/store/sqlite.js +30 -4
  54. package/dist/tools/index.d.ts +0 -13
  55. package/dist/tools/index.js +58 -7
  56. package/dist/tools/skills.d.ts +13 -0
  57. package/dist/tools/skills.js +130 -0
  58. package/dist/types.d.ts +6 -0
  59. package/dist/util/maturity.d.ts +27 -0
  60. package/dist/util/maturity.js +26 -0
  61. package/dist/wing-backfill.js +7 -4
  62. package/dsh.plugin.json +2 -2
  63. package/package.json +14 -14
@@ -11,7 +11,7 @@ import { applyRecallBudget, raceRecallTimeout, RECALL_EMBED_CAP_MS } from '../ut
11
11
  import { clearProfileShare, emptyOccupancyLedger, estimateInjectedMessageTokens, estimateStableSectionTokens, recordProfileShare, recordRecallInjection, resetForCompaction, } from '../util/context-occupancy.js';
12
12
  import { errDetail } from '../util/filelog.js';
13
13
  import { blocksToText } from '../util/text.js';
14
- import { domainGate, formatWeights, hardFilterByWingLock, sortByDomainWeight, WING_ANCHORS } from '../domain-gate.js';
14
+ import { domainGate, formatWeights, hardFilterByWingLock, sortByDomainWeight, sortByUsageScenario, WING_ANCHORS } from '../domain-gate.js';
15
15
  const PROFILE_TTL = 60_000;
16
16
  /** 存储回填缓存上限(超过即按插入序淘汰最旧;查看会话数为百级,500 足够)。 */
17
17
  const STORED_ESTIMATE_CACHE_CAP = 500;
@@ -209,7 +209,7 @@ usage) {
209
209
  let ackDegradeWarned = false;
210
210
  // §E 压缩感知(memorax-absorb):宿主 `compaction/end`(无 error)→ 该会话下一轮
211
211
  // 做一次"定向增强召回"——跳过去重压制、画像立即重注入、占用账本归零;消费即清。
212
- // 会话级回退路径保留(下方 session-start source='compact' 全量重置),两者幂等。
212
+ // 会话级回退路径保留(下方 agent/created source='compact' 全量重置),两者幂等。
213
213
  // 宿主无压缩计数器(compactionId 为 per-transaction UUID),故不做 generation 对账。
214
214
  const postCompaction = new PostCompactionTracker();
215
215
  ctx.on('session/event', (session, event) => {
@@ -225,6 +225,7 @@ usage) {
225
225
  ctx.on('agent/created', async (payload) => {
226
226
  if (payload.source === 'compact' || payload.source === 'clear') {
227
227
  dedupe.reset(payload.agent.id);
228
+ stores.recalled?.reset(String(payload.agent.session?.id ?? payload.agent.id));
228
229
  const led = ledgerFor(payload.agent.id);
229
230
  resetForCompaction(led);
230
231
  occupancyStore.save(payload.agent.id, led); // stock 归零 ⇒ 流水条目删除
@@ -372,6 +373,21 @@ usage) {
372
373
  });
373
374
  logger.info(`[memory] repo 软围栏:currentRepo=${currentRepoKey || '(未识别)'} 跨仓乘子=${cfg.recall.scopeFence.crossRepoMultiplier} agent=${payload.agent.id}`);
374
375
  }
376
+ // ── G1 使用场景细排(默认关):命中 usageScenario 的记忆上浮,纯函数重排,score 不改写。
377
+ // 关闭时不进本分支(零 getByIds、零重排,召回逐字现状);无场景/未命中保持原序(fail-open)。
378
+ if (cfg.usageScenario?.enabled === true && scoped.length > 1) {
379
+ const scenarioById = new Map(stores.l1.getByIds(scoped.map((h) => h.id)).map((r) => [
380
+ r.id,
381
+ Array.isArray(r.metadata?.dsh_usage_scenario)
382
+ ? r.metadata.dsh_usage_scenario
383
+ : [],
384
+ ]));
385
+ const before = scoped.map((h) => h.id).join(',');
386
+ scoped = sortByUsageScenario(scoped, (id) => scenarioById.get(id) ?? [], query);
387
+ if (scoped.map((h) => h.id).join(',') !== before) {
388
+ logger.info(`[memory] 使用场景细排生效:agent=${payload.agent.id}`);
389
+ }
390
+ }
375
391
  // 召回去重:同会话已注入过的记录不再重复注入(模型上下文已持有,省 token)。
376
392
  // 纯过滤——剩几条注几条,全量压制(0 条新鲜命中)是正确状态而非未命中。
377
393
  // §E:压缩后增强轮跳过压制(压缩把已注入内容逐出了模型上下文)。
@@ -442,6 +458,9 @@ usage) {
442
458
  });
443
459
  // 使用统计:候选=scoped(尝试),进 topN(注入)=used
444
460
  usage?.recordAttempt(scoped.map((h) => h.id), fresh.slice(0, lines.length).map((h) => h.id));
461
+ // G-live:按 **sessionKey**(=agent.session?.id ?? agent.id,与 pill 的 sessionId / 投影 header.id 同源,
462
+ // 非 ownerId)记入本会话“活召回集”,供侧栏卡读取;压缩时同键 reset。
463
+ stores.recalled?.add(sessionKey, fresh.slice(0, lines.length).map((h) => h.id));
445
464
  // 入账在成功构造注入消息之后、返回 enter 之前——任何前置抛错路径账目零扰动
446
465
  const led = ledgerFor(payload.agent.id);
447
466
  recordRecallInjection(led, text.length);
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 技能常驻注入 Hook(轨2)。**默认关**(cfg.skills.enabled && cfg.skills.inject)。
3
+ *
4
+ * 完全对齐 `slot-recall.ts` 的范式(同一注入通道范本,不新建入口):
5
+ * - `agent/pre-step`(waterfall,prepend) 注入,`createUserMessage({source:{kind:'plugin:memory',form:'recall'}}})`
6
+ * ——**复用既有 source kind**,自动享受 `isOwnRecallSource` 双形状判据与召回去重/占用账本(S4 红线);
7
+ * - 帧体转义 `<\/system-reminder>` 防正文伪造帧边界;
8
+ * - **独立字节预算**(cfg.skills.maxPerTurnBytes),不与召回 2000 预算抢名额;
9
+ * - 注入前 `archiveDue` 机械闲置归档(0 LLM、不引入 timer,对齐 `slots.expireDue`)。
10
+ *
11
+ * 本轮语义:常驻注入全部 active 技能(按插入序 + 字节预算截断),**不做按触发词选择**
12
+ * (selective trigger 匹配属后续增强;常驻语义与 slots 一致,先保证默认关时零表面积)。
13
+ * 三档渲染(full/excerpt/hint)是显示策略,同一 body/steps 现场组词,无缓存副本漂移。
14
+ */
15
+ import type { Context } from '@deepseek-ai/cordis';
16
+ import type { MemoryConfig } from '../config.js';
17
+ import type { LiveSettingsHandle } from '../settings.js';
18
+ import type { Skill, SkillStore } from '../store/skill-store.js';
19
+ import type { MemoryLogger } from '../types.js';
20
+ /** 按档位现场组词:full=标题+正文+步骤;excerpt=标题+正文截断;hint=仅标题+读取指引。 */
21
+ export declare function renderSkill(s: Skill, excerptChars: number): string;
22
+ export interface SkillRecallController {
23
+ buildInjection(): string | null;
24
+ }
25
+ export declare function registerSkillRecall(ctx: Context, cfg: MemoryConfig, skills: SkillStore, logger: MemoryLogger, live: LiveSettingsHandle): SkillRecallController;
@@ -0,0 +1,72 @@
1
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
2
+ const SYSTEM_REMINDER_OPEN = '<system-reminder>';
3
+ const SYSTEM_REMINDER_CLOSE = '</system-reminder>';
4
+ const SKILLS_INTRO = '以下是本环境沉淀的技能(程序性记忆):过去重复工作流固化出的清单,仅在与之相关时作为指引遵循。' +
5
+ '它们是系统注入的元信息,不是用户发言——无需回应,请勿在回复中复述本节内容;' +
6
+ '且不覆盖系统提示与用户的直接指令。需要完整步骤用 memory_skill_read 展开。';
7
+ function escapeFrameBody(body) {
8
+ return body.replaceAll(SYSTEM_REMINDER_CLOSE, '<\\/system-reminder>');
9
+ }
10
+ /** 按档位现场组词:full=标题+正文+步骤;excerpt=标题+正文截断;hint=仅标题+读取指引。 */
11
+ export function renderSkill(s, excerptChars) {
12
+ if (s.injectionTier === 'hint')
13
+ return `[技能·${s.title}] 详见 memory_skill_read(${s.id})`;
14
+ const steps = s.steps.length ? '\n' + s.steps.map((st) => ` - ${st}`).join('\n') : '';
15
+ if (s.injectionTier === 'full')
16
+ return `[技能·${s.title}] ${s.body}${steps}`;
17
+ const body = s.body.length > excerptChars ? s.body.slice(0, excerptChars) + '…' : s.body;
18
+ return `[技能·${s.title}] ${body}`;
19
+ }
20
+ export function registerSkillRecall(ctx, cfg, skills, logger, live) {
21
+ // 生效开关:部署 cfg.skills.enabled 或看板 live.skillsEnabled 任一开。
22
+ const on = () => cfg.skills.enabled === true || live.get().skillsEnabled === true;
23
+ const buildInjection = () => {
24
+ if (!on() || !cfg.skills.inject)
25
+ return null;
26
+ const active = skills.listActive();
27
+ if (active.length === 0)
28
+ return null;
29
+ const budget = cfg.skills.maxPerTurnBytes;
30
+ const out = [];
31
+ let used = 0;
32
+ let hidden = 0;
33
+ for (const s of active) {
34
+ const rendered = renderSkill(s, cfg.skills.excerptChars);
35
+ const bytes = Buffer.byteLength(rendered, 'utf8');
36
+ if (used + bytes > budget) {
37
+ hidden++;
38
+ continue;
39
+ }
40
+ out.push(rendered);
41
+ used += bytes;
42
+ }
43
+ if (out.length === 0)
44
+ return null;
45
+ if (hidden > 0)
46
+ out.push(`… 另有 ${hidden} 个技能因预算未显示,可用 memory_skill_list 查看`);
47
+ return [SYSTEM_REMINDER_OPEN, SKILLS_INTRO, escapeFrameBody(out.join('\n')), SYSTEM_REMINDER_CLOSE].join('\n');
48
+ };
49
+ ctx.on('agent/pre-step', async (payload, next) => {
50
+ const decision = await next();
51
+ if (decision.kind === 'reject' || (payload.signal && payload.signal.aborted))
52
+ return decision;
53
+ if (!on() || !cfg.skills.inject)
54
+ return decision;
55
+ try {
56
+ await skills.archiveDue(Date.now(), cfg.skills.idleArchiveDays);
57
+ const text = buildInjection();
58
+ if (!text)
59
+ return decision;
60
+ const injection = createUserMessage({
61
+ content: [{ type: 'text', text }],
62
+ source: { kind: 'plugin:memory', form: 'recall' },
63
+ });
64
+ return { kind: 'enter', messages: [injection, ...decision.messages] };
65
+ }
66
+ catch (err) {
67
+ logger.warn(`[memory] 技能注入失败(跳过本轮): ${err instanceof Error ? err.message : String(err)}`);
68
+ return decision;
69
+ }
70
+ });
71
+ return { buildInjection };
72
+ }
package/dist/index.d.ts CHANGED
@@ -84,6 +84,51 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
84
84
  maxPending: import("@deepseek-ai/schemastery").default<number, number, "defined">;
85
85
  timeoutDays: import("@deepseek-ai/schemastery").default<number, number, "defined">;
86
86
  }>>, "plain">;
87
+ memport: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
88
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
89
+ scanMaxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
90
+ }>>, Schemastery.ObjectT<NoInfer<{
91
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
92
+ scanMaxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
93
+ }>>, "plain">;
94
+ skills: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
95
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
96
+ inject: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
97
+ allowPromote: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
98
+ minSessions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
99
+ minEvidence: import("@deepseek-ai/schemastery").default<number, number, "defined">;
100
+ maxPerTurnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
101
+ maxSkillCatalogBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
102
+ excerptChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
103
+ idleArchiveDays: import("@deepseek-ai/schemastery").default<number, number, "defined">;
104
+ }>>, Schemastery.ObjectT<NoInfer<{
105
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
106
+ inject: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
107
+ allowPromote: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
108
+ minSessions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
109
+ minEvidence: import("@deepseek-ai/schemastery").default<number, number, "defined">;
110
+ maxPerTurnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
111
+ maxSkillCatalogBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
112
+ excerptChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
113
+ idleArchiveDays: import("@deepseek-ai/schemastery").default<number, number, "defined">;
114
+ }>>, "plain">;
115
+ backfill: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
116
+ maxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
117
+ timeBudgetMs: import("@deepseek-ai/schemastery").default<number, number, "defined">;
118
+ }>>, Schemastery.ObjectT<NoInfer<{
119
+ maxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
120
+ timeBudgetMs: import("@deepseek-ai/schemastery").default<number, number, "defined">;
121
+ }>>, "plain">;
122
+ usageScenario: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
123
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
124
+ }>>, Schemastery.ObjectT<NoInfer<{
125
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
126
+ }>>, "plain">;
127
+ narrativeTemplates: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
128
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
129
+ }>>, Schemastery.ObjectT<NoInfer<{
130
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
131
+ }>>, "plain">;
87
132
  recall: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
88
133
  enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
89
134
  maxResults: import("@deepseek-ai/schemastery").default<number, number, "defined">;
@@ -315,12 +360,14 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
315
360
  maxSlots: import("@deepseek-ai/schemastery").default<number, number, "defined">;
316
361
  maxAlwaysOnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
317
362
  maxBodyChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
363
+ migrateLegacy: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
318
364
  }>>, Schemastery.ObjectT<NoInfer<{
319
365
  enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
320
366
  inject: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
321
367
  maxSlots: import("@deepseek-ai/schemastery").default<number, number, "defined">;
322
368
  maxAlwaysOnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
323
369
  maxBodyChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
370
+ migrateLegacy: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
324
371
  }>>, "plain">;
325
372
  tools: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
326
373
  benchControl: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
@@ -421,6 +468,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
421
468
  embedRemoteDimensions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
422
469
  memoryMutate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
423
470
  conflictFreeze: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
471
+ skillsEnabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
424
472
  toolRoom: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
425
473
  toolGraph: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
426
474
  toolRuminate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
@@ -523,6 +571,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
523
571
  embedRemoteDimensions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
524
572
  memoryMutate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
525
573
  conflictFreeze: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
574
+ skillsEnabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
526
575
  toolRoom: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
527
576
  toolGraph: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
528
577
  toolRuminate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
@@ -598,6 +647,51 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
598
647
  maxPending: import("@deepseek-ai/schemastery").default<number, number, "defined">;
599
648
  timeoutDays: import("@deepseek-ai/schemastery").default<number, number, "defined">;
600
649
  }>>, "plain">;
650
+ memport: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
651
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
652
+ scanMaxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
653
+ }>>, Schemastery.ObjectT<NoInfer<{
654
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
655
+ scanMaxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
656
+ }>>, "plain">;
657
+ skills: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
658
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
659
+ inject: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
660
+ allowPromote: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
661
+ minSessions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
662
+ minEvidence: import("@deepseek-ai/schemastery").default<number, number, "defined">;
663
+ maxPerTurnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
664
+ maxSkillCatalogBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
665
+ excerptChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
666
+ idleArchiveDays: import("@deepseek-ai/schemastery").default<number, number, "defined">;
667
+ }>>, Schemastery.ObjectT<NoInfer<{
668
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
669
+ inject: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
670
+ allowPromote: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
671
+ minSessions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
672
+ minEvidence: import("@deepseek-ai/schemastery").default<number, number, "defined">;
673
+ maxPerTurnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
674
+ maxSkillCatalogBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
675
+ excerptChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
676
+ idleArchiveDays: import("@deepseek-ai/schemastery").default<number, number, "defined">;
677
+ }>>, "plain">;
678
+ backfill: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
679
+ maxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
680
+ timeBudgetMs: import("@deepseek-ai/schemastery").default<number, number, "defined">;
681
+ }>>, Schemastery.ObjectT<NoInfer<{
682
+ maxRecords: import("@deepseek-ai/schemastery").default<number, number, "defined">;
683
+ timeBudgetMs: import("@deepseek-ai/schemastery").default<number, number, "defined">;
684
+ }>>, "plain">;
685
+ usageScenario: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
686
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
687
+ }>>, Schemastery.ObjectT<NoInfer<{
688
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
689
+ }>>, "plain">;
690
+ narrativeTemplates: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
691
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
692
+ }>>, Schemastery.ObjectT<NoInfer<{
693
+ enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
694
+ }>>, "plain">;
601
695
  recall: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
602
696
  enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
603
697
  maxResults: import("@deepseek-ai/schemastery").default<number, number, "defined">;
@@ -829,12 +923,14 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
829
923
  maxSlots: import("@deepseek-ai/schemastery").default<number, number, "defined">;
830
924
  maxAlwaysOnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
831
925
  maxBodyChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
926
+ migrateLegacy: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
832
927
  }>>, Schemastery.ObjectT<NoInfer<{
833
928
  enabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
834
929
  inject: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
835
930
  maxSlots: import("@deepseek-ai/schemastery").default<number, number, "defined">;
836
931
  maxAlwaysOnBytes: import("@deepseek-ai/schemastery").default<number, number, "defined">;
837
932
  maxBodyChars: import("@deepseek-ai/schemastery").default<number, number, "defined">;
933
+ migrateLegacy: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
838
934
  }>>, "plain">;
839
935
  tools: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
840
936
  benchControl: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
@@ -935,6 +1031,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
935
1031
  embedRemoteDimensions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
936
1032
  memoryMutate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
937
1033
  conflictFreeze: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
1034
+ skillsEnabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
938
1035
  toolRoom: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
939
1036
  toolGraph: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
940
1037
  toolRuminate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
@@ -1037,6 +1134,7 @@ export declare const Config: import("@deepseek-ai/schemastery").default<Schemast
1037
1134
  embedRemoteDimensions: import("@deepseek-ai/schemastery").default<number, number, "defined">;
1038
1135
  memoryMutate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
1039
1136
  conflictFreeze: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
1137
+ skillsEnabled: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
1040
1138
  toolRoom: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
1041
1139
  toolGraph: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
1042
1140
  toolRuminate: import("@deepseek-ai/schemastery").default<boolean, boolean, "defined">;
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { memorySchema, resolveDataDir } from './config.js';
13
13
  import { registerBenchControl } from './bench-control.js';
14
14
  import { registerCapture } from './hooks/capture.js';
15
15
  import { registerRecall } from './hooks/recall.js';
16
+ import { SessionRecalledStore } from './store/session-recalled.js';
16
17
  import { MemoryRunner } from './pipeline/runner.js';
17
18
  import { RebuildController } from './pipeline/rebuild.js';
18
19
  import { RuminateController } from './pipeline/ruminate.js';
@@ -37,8 +38,13 @@ import { registerMemoryTools } from './tools/index.js';
37
38
  import { registerSlotTools } from './tools/slots.js';
38
39
  import { registerSlotRecall } from './hooks/slot-recall.js';
39
40
  import { RoomRegistryStore } from './store/rooms-registry.js';
41
+ import { MemorySourceStore } from './store/memory-sources.js';
42
+ import { SkillStore } from './store/skill-store.js';
43
+ import { registerSkillRecall } from './hooks/skill-recall.js';
44
+ import { registerSkillTools } from './tools/skills.js';
40
45
  import { RecallUsageStore } from './store/recall-usage.js';
41
46
  import { registerSlotsProjection } from './projection/slots.js';
47
+ import { registerRecallProjection } from './projection/recall.js';
42
48
  import { errDetail, withFileLog } from './util/filelog.js';
43
49
  import { buildRouteChain, resolveModelRoute, invalidateEffortCache } from './llm.js';
44
50
  import { effectiveCfg } from './pipeline/runner.js';
@@ -175,6 +181,8 @@ export async function apply(ctx, config) {
175
181
  work: new PersonaStore(dataDir, 'work', logger),
176
182
  },
177
183
  state: new StateStore(StateStore.pathFor(dataDir), logger),
184
+ // 会话“活召回集”(G-live):sessionKey → 本会话已注入未压缩的记录 id;持久化,旧会话重开载回。
185
+ recalled: new SessionRecalledStore(dataDir, logger),
178
186
  // 激活槽位(active slot):独立 slots.json,高频小改不拖累 checkpoint 原子写
179
187
  slots: new SlotStore(SlotStore.pathFor(dataDir), logger, {
180
188
  maxSlots: config.slots.maxSlots,
@@ -356,6 +364,12 @@ export async function apply(ctx, config) {
356
364
  // Room 注册表(分类管理 beta.4;损坏降级为纯自生长目录)
357
365
  const roomRegistry = new RoomRegistryStore(dataDir, logger);
358
366
  await roomRegistry.init();
367
+ // 外部记忆来源注册表(轨3 Connect;默认由 cfg.memport.enabled 关门控,sidecar 本身无条件加载)
368
+ const memorySources = new MemorySourceStore(dataDir, logger);
369
+ await memorySources.init();
370
+ // 技能存储(轨2;sidecar 无条件加载,但工具/注入均由 cfg.skills.enabled 默认关自 no-op)
371
+ const skillStore = new SkillStore(dataDir, logger);
372
+ await skillStore.init();
359
373
  const ruminate = storageOk && !db.isDegraded() && ruminateFile
360
374
  ? new RuminateController(ctx, config, runner, stores, logger, live, ruminateFile, roomRegistry)
361
375
  : undefined;
@@ -373,7 +387,18 @@ export async function apply(ctx, config) {
373
387
  // 激活槽位(active slot):工具面 + 常驻注入 + 服务端投影(均走 ctx.effect,可撤销)
374
388
  registerSlotTools(ctx, config, stores.slots, logger, modes, live, stores.l1);
375
389
  const slotRecall = registerSlotRecall(ctx, config, stores.slots, logger, live);
390
+ // 轨2 技能:工具面与常驻注入(均 cfg.skills.enabled 默认关时自 no-op,零表面积)。
391
+ registerSkillTools(ctx, config, skillStore, logger, live);
392
+ registerSkillRecall(ctx, config, skillStore, logger, live);
393
+ // memorySlots 投影:常驻槽位(手动 pin 的约定)的展示契约,消费方是 dsh-brief-sidebar。
394
+ // 宿主契约(dsh-session-projection/lib/index.js):插件不注册时 key 从快照消失、
395
+ // 客户端读作 capability absence(不会毒化历史加载),所以“摘注册”技术上可行;
396
+ // 但那是把跨插件展示契约直接抽掉,旧版 brief-sidebar 会默默丢分区——隐藏请走它自己的
397
+ // 「记忆槽位分区」开关。本包另提供一个会话感知的 memoryRecall 投影。
376
398
  registerSlotsProjection(ctx, stores.slots, stores.l1);
399
+ // memoryRecall 投影:本会话“已注入且未被压缩覆盖”的召回集(会话感知,init 取 header.id),
400
+ // 与常驻槽位是两条轴:那个是手动 pin 的硬注入约定,这个是非常驻的相关性浮现结果。
401
+ registerRecallProjection(ctx, stores.recalled, stores.l1);
377
402
  void slotRecall;
378
403
  registerMemoryRpc(ctx, config, stores, logger, {
379
404
  degraded: () => !storageOk || db.isDegraded(),
@@ -388,7 +413,7 @@ export async function apply(ctx, config) {
388
413
  runnerView: (sid, mode) => runner.sessionView(sid, mode),
389
414
  l0Count: (sid) => stores.l0.countBySession(sid),
390
415
  capabilities: () => db.getCapabilities(),
391
- }, ruminate, storageOk ? roomRegistry : undefined);
416
+ }, ruminate, storageOk ? roomRegistry : undefined, storageOk ? memorySources : undefined, skillStore);
392
417
  // bench 控制服务(config.benchControl 门控,默认关):仅基准/调试部署注册,
393
418
  // 供同进程的 bench-runner lifecycle 赛道触发 rebuild / 设置会话档位
394
419
  // (宿主侧 RPC 无 call(),见 bench-control.ts)
@@ -0,0 +1,70 @@
1
+ /** MMF v1.1 默认列序(缺 `#cols=` 时按此解析;v1 包缺 vf/vt/st 列按 -/-/? 补齐)。 */
2
+ export declare const DEFAULT_COLS: readonly ["type", "created", "updated", "vf", "vt", "st", "cf", "rw", "tags", "content"];
3
+ /** 类型码 → 运行时 type(与 memport.py TYPE_CODES 一字不差)。 */
4
+ export declare const TYPE_CODES: Record<string, string>;
5
+ /** 持续性取值域:p 时点 / s 已结束区间 / o 仍在持续 / t 无时间性 / ? 未判定。 */
6
+ export declare const PERSISTENCE: readonly ["p", "s", "o", "t", "?"];
7
+ export interface MemRecord {
8
+ seq: number;
9
+ type: string;
10
+ typeFull: string;
11
+ created: string;
12
+ updated: string;
13
+ vf: string;
14
+ vt: string;
15
+ st: string;
16
+ cf: string;
17
+ rw: string;
18
+ tags: Record<string, string>;
19
+ content: string;
20
+ hash: string;
21
+ createdMs: number | null;
22
+ createdPrec: string;
23
+ updatedMs: number | null;
24
+ updatedPrec: string;
25
+ vfMs: number | null;
26
+ vfPrec: string;
27
+ vtMs: number | null;
28
+ vtPrec: string;
29
+ }
30
+ export interface Bundle {
31
+ header: Record<string, string>;
32
+ cols: string[];
33
+ records: MemRecord[];
34
+ errors: string[];
35
+ warnings: string[];
36
+ declaredCount: number | null;
37
+ declaredSha: string | null;
38
+ /** 记录体原始行(未去转义),用于校验和复算——与 Python `body` 对齐。 */
39
+ bodyLines: string[];
40
+ }
41
+ /** NFKC + 空白折叠 + 去首尾(用于哈希与相似度,不改原文)。 */
42
+ export declare function normText(s: string): string;
43
+ /**
44
+ * 内容哈希:sha256(normText + 小写)[:16]。
45
+ * 注意:Python 用 casefold(激进大小写折叠),JS 用 toLowerCase;
46
+ * 对 CJK/ASCII 记忆正文等价,仅极端 Unicode 有别——实际记忆集不含此类,
47
+ * 跨实现金标准走包级 sha256(不逐条折叠),不受影响。
48
+ */
49
+ export declare function contentHash(s: string): string;
50
+ /** 记录稳定 id:`mp_` + sha256(src|chash)[:16]。 */
51
+ export declare function recordId(src: string, chash: string): string;
52
+ /** 解码内容列:反斜杠+反斜杠 → 单反斜杠;反斜杠+n → 换行(其余反斜杠原样)。 */
53
+ export declare function unescapeContent(s: string): string;
54
+ /**
55
+ * 编码内容列:反斜杠 → 双反斜杠、CRLF → LF、LF → 反斜杠+n。
56
+ * 用 charCode 常量而非控制字符字面量,避免跨工具写盘时 CR/LF 被吞进正则/字符串字面量。
57
+ */
58
+ export declare function escapeContent(s: string): string;
59
+ /** `k:v;k:v` → 对象(缺冒号的段忽略)。 */
60
+ export declare function parseTags(raw: string): Record<string, string>;
61
+ /** 时间串 → { ms|null, precision }(镜像 Python parse_time;naive 视为本地)。 */
62
+ export declare function parseTime(raw: string): {
63
+ ms: number | null;
64
+ precision: string;
65
+ };
66
+ /**
67
+ * 解析整包文本 → Bundle(含结构校验)。镜像 memport.py parse_bundle。
68
+ * 入参是字符串(调用方读文件时自带编码处理);此处容错剥离 BOM。
69
+ */
70
+ export declare function parseBundle(text: string): Bundle;