dsh-prime-memory 0.22.1 → 0.24.0

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 (121) hide show
  1. package/CHANGELOG.en.md +401 -0
  2. package/CHANGELOG.ja.md +401 -0
  3. package/CHANGELOG.ko.md +401 -0
  4. package/CHANGELOG.md +100 -0
  5. package/dist/client.js +1951 -1188
  6. package/dist/config.d.ts +176 -1240
  7. package/dist/config.js +113 -2
  8. package/dist/conflict-service.d.ts +12 -0
  9. package/dist/conflict-service.js +24 -0
  10. package/dist/contract.d.ts +327 -0
  11. package/dist/domain-gate.d.ts +19 -0
  12. package/dist/domain-gate.js +45 -0
  13. package/dist/graph/constraints.d.ts +4 -2
  14. package/dist/graph/constraints.js +4 -2
  15. package/dist/hooks/obsolete-notice.d.ts +26 -0
  16. package/dist/hooks/obsolete-notice.js +64 -0
  17. package/dist/hooks/recall.d.ts +41 -0
  18. package/dist/hooks/recall.js +127 -28
  19. package/dist/hooks/skill-recall.d.ts +25 -0
  20. package/dist/hooks/skill-recall.js +72 -0
  21. package/dist/index.d.ts +1 -1029
  22. package/dist/index.js +55 -2
  23. package/dist/memory-types.d.ts +69 -0
  24. package/dist/memory-types.js +165 -0
  25. package/dist/memport/format.d.ts +75 -0
  26. package/dist/memport/format.js +345 -0
  27. package/dist/memport/prefilter.d.ts +62 -0
  28. package/dist/memport/prefilter.js +132 -0
  29. package/dist/memport/scan.d.ts +13 -0
  30. package/dist/memport/scan.js +100 -0
  31. package/dist/metadata-validators.d.ts +22 -0
  32. package/dist/metadata-validators.js +45 -0
  33. package/dist/pipeline/l1.d.ts +44 -2
  34. package/dist/pipeline/l1.js +177 -9
  35. package/dist/pipeline/md-sync.d.ts +65 -0
  36. package/dist/pipeline/md-sync.js +456 -0
  37. package/dist/pipeline/ruminate.d.ts +46 -1
  38. package/dist/pipeline/ruminate.js +150 -1
  39. package/dist/pipeline/runner.d.ts +10 -0
  40. package/dist/pipeline/runner.js +7 -1
  41. package/dist/pipeline/slot-migrate.d.ts +45 -0
  42. package/dist/pipeline/slot-migrate.js +45 -0
  43. package/dist/pipeline/workspace-relink.d.ts +60 -0
  44. package/dist/pipeline/workspace-relink.js +73 -0
  45. package/dist/projection/recall.d.ts +112 -0
  46. package/dist/projection/recall.js +183 -0
  47. package/dist/projection/slots.d.ts +5 -0
  48. package/dist/prompts/examples.d.ts +73 -0
  49. package/dist/prompts/examples.js +178 -0
  50. package/dist/prompts/graph-projection.js +3 -0
  51. package/dist/prompts/l1-dedup.d.ts +9 -3
  52. package/dist/prompts/l1-dedup.js +34 -13
  53. package/dist/prompts/l1-extraction.d.ts +9 -4
  54. package/dist/prompts/l1-extraction.js +76 -8
  55. package/dist/settings.d.ts +8 -0
  56. package/dist/settings.js +15 -1
  57. package/dist/skills/crystallize.d.ts +41 -0
  58. package/dist/skills/crystallize.js +75 -0
  59. package/dist/stats.d.ts +30 -1
  60. package/dist/stats.js +504 -13
  61. package/dist/store/embedding-source.d.ts +8 -0
  62. package/dist/store/embedding-source.js +39 -2
  63. package/dist/store/file-versions.d.ts +4 -0
  64. package/dist/store/file-versions.js +4 -0
  65. package/dist/store/graph-store.d.ts +14 -0
  66. package/dist/store/graph-store.js +35 -0
  67. package/dist/store/l1-snapshot.d.ts +26 -1
  68. package/dist/store/l1-snapshot.js +53 -8
  69. package/dist/store/l1.d.ts +77 -3
  70. package/dist/store/l1.js +128 -3
  71. package/dist/store/memory-md-archive.d.ts +84 -0
  72. package/dist/store/memory-md-archive.js +155 -0
  73. package/dist/store/memory-md-disable.d.ts +23 -0
  74. package/dist/store/memory-md-disable.js +106 -0
  75. package/dist/store/memory-md-export.d.ts +50 -0
  76. package/dist/store/memory-md-export.js +200 -0
  77. package/dist/store/memory-md-ledger.d.ts +94 -0
  78. package/dist/store/memory-md-ledger.js +243 -0
  79. package/dist/store/memory-md-orphan.d.ts +27 -0
  80. package/dist/store/memory-md-orphan.js +153 -0
  81. package/dist/store/memory-md-parse.d.ts +35 -0
  82. package/dist/store/memory-md-parse.js +85 -0
  83. package/dist/store/memory-md-skeleton.d.ts +18 -0
  84. package/dist/store/memory-md-skeleton.js +61 -0
  85. package/dist/store/memory-md-types.d.ts +174 -0
  86. package/dist/store/memory-md-types.js +10 -0
  87. package/dist/store/memory-md.d.ts +108 -0
  88. package/dist/store/memory-md.js +219 -0
  89. package/dist/store/memory-sources.d.ts +59 -0
  90. package/dist/store/memory-sources.js +214 -0
  91. package/dist/store/obsolete-notices.d.ts +39 -0
  92. package/dist/store/obsolete-notices.js +153 -0
  93. package/dist/store/recall-feedback.d.ts +75 -0
  94. package/dist/store/recall-feedback.js +213 -0
  95. package/dist/store/recall-usage.d.ts +11 -0
  96. package/dist/store/recall-usage.js +29 -0
  97. package/dist/store/session-recalled.d.ts +60 -0
  98. package/dist/store/session-recalled.js +310 -0
  99. package/dist/store/skill-store.d.ts +99 -0
  100. package/dist/store/skill-store.js +330 -0
  101. package/dist/store/sqlite.d.ts +71 -0
  102. package/dist/store/sqlite.js +259 -15
  103. package/dist/store/supersede.d.ts +1 -1
  104. package/dist/store/supersede.js +1 -1
  105. package/dist/tools/index.d.ts +4 -13
  106. package/dist/tools/index.js +80 -12
  107. package/dist/tools/skills.d.ts +13 -0
  108. package/dist/tools/skills.js +130 -0
  109. package/dist/types.d.ts +30 -7
  110. package/dist/types.js +10 -2
  111. package/dist/util/maturity.d.ts +44 -0
  112. package/dist/util/maturity.js +43 -0
  113. package/dist/util/md-name.d.ts +129 -0
  114. package/dist/util/md-name.js +366 -0
  115. package/dist/util/md-normalize.d.ts +23 -0
  116. package/dist/util/md-normalize.js +131 -0
  117. package/dist/wing-backfill.js +7 -4
  118. package/dist/workspace-route.d.ts +22 -0
  119. package/dist/workspace-route.js +66 -0
  120. package/dsh.plugin.json +1 -1
  121. package/package.json +6 -5
package/dist/config.js CHANGED
@@ -32,7 +32,7 @@ export function liveSettingsSchema() {
32
32
  model: Schema.string().default(''),
33
33
  reasoningEffort: Schema.union([...EFFORT_CHOICES]).default(''),
34
34
  });
35
- return Schema.object({
35
+ const node = Schema.object({
36
36
  enabled: Schema.boolean().default(true),
37
37
  capture: Schema.boolean().default(true),
38
38
  distill: Schema.boolean().default(true),
@@ -69,6 +69,8 @@ export function liveSettingsSchema() {
69
69
  memoryMutate: Schema.boolean().default(false),
70
70
  // §C 人工冲突裁决总开关:默认 false(冻结消耗注意力,不可默认全开)
71
71
  conflictFreeze: Schema.boolean().default(false),
72
+ // 轨2 技能结晶总开关(看板可拨):默认 false。开=注册 memory_skill_* 工具 + 注入 active 技能 + skills-* 端点可用。
73
+ skillsEnabled: Schema.boolean().default(false),
72
74
  // 工具分组封印(v0.21.0):false = 该组工具不注册,从模型工具列表消失。
73
75
  // 核心读(memory_search/read_scene/receipts/conversation_search)与槽位三件不受控。
74
76
  toolRoom: Schema.boolean().default(true),
@@ -76,7 +78,13 @@ export function liveSettingsSchema() {
76
78
  toolRuminate: Schema.boolean().default(true),
77
79
  toolConflict: Schema.boolean().default(true),
78
80
  toolMutate: Schema.boolean().default(true),
79
- }).volatile();
81
+ });
82
+ // 探测后再标:`Schema.object(...).volatile()` 只在新版 schemastery 上有(格内实测:
83
+ // cordis/schemastery 4.0.4 有、0.1.5-rc.3 解析到的 4.0.2 没有)。直接调用会让**整插件**
84
+ // 在老线 load-failure——不是降级,是起不来。未标时读侧走 readLiveRef 的兜底路径:
85
+ // 开关仍然生效(取宿主注入的冻结值),只是改值需要重启而不是热更。
86
+ const maybe = node;
87
+ return (typeof maybe.volatile === 'function' ? maybe.volatile() : node);
80
88
  }
81
89
  export const memorySchema = Schema.object({
82
90
  dataDir: Schema.string().default(''),
@@ -129,6 +137,107 @@ export const memorySchema = Schema.object({
129
137
  // 30 天:足够跨过假期与项目间歇,又不至于让互相矛盾的两条记忆长期并列召回。
130
138
  timeoutDays: Schema.number().min(0).max(3650).default(30),
131
139
  }),
140
+ // 轨3 memport Connect:默认关(新功能默认关)。scan 为**只读预览**,写库仍走 memory_import。
141
+ memport: Schema.object({
142
+ enabled: Schema.boolean().default(false),
143
+ // 单源扫描上限(防极端大包在事件循环里阻塞 RPC;scan 是手动触发,仍留护栏)。
144
+ scanMaxRecords: Schema.number().min(0).max(20_000).default(5000),
145
+ }),
146
+ // 轨2 技能结晶:默认关 + allowPromote 关(先只计数不晋升)。
147
+ skills: Schema.object({
148
+ enabled: Schema.boolean().default(false),
149
+ inject: Schema.boolean().default(true),
150
+ allowPromote: Schema.boolean().default(false),
151
+ minSessions: Schema.number().min(1).max(100).default(3),
152
+ minEvidence: Schema.number().min(1).max(1000).default(5),
153
+ maxPerTurnBytes: Schema.number().min(0).max(8192).default(600),
154
+ maxSkillCatalogBytes: Schema.number().min(0).max(8192).default(2048),
155
+ excerptChars: Schema.number().min(0).max(4000).default(240),
156
+ idleArchiveDays: Schema.number().min(0).max(3650).default(90),
157
+ }),
158
+ // wing 回填护栏:默认与旧行为一致(300 条 / 120s),需一次多跑点时调大。
159
+ backfill: Schema.object({
160
+ maxRecords: Schema.number().min(1).max(10_000).default(300),
161
+ timeBudgetMs: Schema.number().min(1000).max(1_800_000).default(120_000),
162
+ }),
163
+ /**
164
+ * 内容路由:默认**开**(用户口径:谈到本仓/dsh 生态的记忆要归到本仓 workspace,
165
+ * 而不是全托底到 `<dataDir>/memories`)。默认路由表就是用户给的两条归类规则(路径可改,不需改码)。
166
+ */
167
+ workspaceSource: Schema.object({
168
+ enabled: Schema.boolean().default(true),
169
+ routes: Schema.array(Schema.object({
170
+ /**
171
+ * 归到哪个 workspace(**workspace 根**,绝对路径;写入前过 normalizeWorkspacePath)。
172
+ *
173
+ * 存的不包含 `.agents/memory`:投射落点由 `targetFor` 算,它会在本值后拼上
174
+ * `WORKSPACE_SUBDIR = ['.agents','memory']`。旧默认值把它写成了
175
+ * `E:\test\rewrite-agently\.agents`,于是落点变成 `.agents\.agents\memory\`——
176
+ * 而 `.agents` 目录确实存在,"目录不存在则退回单根"的护栏救不了这种形状,
177
+ * 等于在仓里挂一棵没人认领的孤儿树。新增用例钉住这个不变式。
178
+ */
179
+ path: Schema.string().default(''),
180
+ /** 命中任一关键词即归此路(小写子串匹配,已 NFKC 归一)。 */
181
+ anyOf: Schema.array(Schema.string()).default([]),
182
+ /** 出现任一排除词则此路不命中(用于「无 dsh 才算非 dsh 项目」)。 */
183
+ noneOf: Schema.array(Schema.string()).default([]),
184
+ })).default([
185
+ {
186
+ path: 'E:\\test\\rewrite-agently',
187
+ // 'dsh' 子串已覆盖 mine-dsh-plugins 下所有插件名;把目录名单列出来是因为
188
+ // 有些记忆只写了"某个插件"而没写 dsh- 前缀,那时这一项才顶用。
189
+ anyOf: ['dsh', 'mine-dsh-plugins', 'cordis', 'prime-memory', 'brief-sidebar', 'host farm', 'inferglow-dsh'],
190
+ noneOf: [],
191
+ },
192
+ {
193
+ path: 'E:\\test\\agent-no-dsh',
194
+ anyOf: ['hanako', 'hanaagent', 'openhanako', 'inferglow', 'pi-agent'],
195
+ // 同一段内容又谈到 dsh 时属于 dsh 生态(如 Hanako 逆向用于反推 dsh 机制),归 dsh 侧。
196
+ noneOf: ['dsh'],
197
+ },
198
+ ]),
199
+ maxRecords: Schema.number().min(1).max(10_000).default(300),
200
+ timeBudgetMs: Schema.number().min(1000).max(1_800_000).default(120_000),
201
+ }),
202
+ // md 投射与外部存档:**默认开**(写盘不影响检索,关掉只会让人看不到任何投影文件)。root 空串 = 用默认根(<dataDir>/memories),形状校验在投射器里做。
203
+ memExport: Schema.object({
204
+ enabled: Schema.boolean().default(true),
205
+ root: Schema.string().default(''),
206
+ maxRecords: Schema.number().min(1).max(10_000).default(300),
207
+ timeBudgetMs: Schema.number().min(1000).max(1_800_000).default(120_000),
208
+ }),
209
+ // P2 反向同步:默认关。文件数护栏比导出的条数护栏宽一些——一次目录扫描的代价
210
+ // 低于一次全库渲染,但时间预算照同一口径(面板不能被卡住)。
211
+ mdSync: Schema.object({
212
+ enabled: Schema.boolean().default(false),
213
+ maxFiles: Schema.number().min(1).max(20_000).default(2_000),
214
+ timeBudgetMs: Schema.number().min(1000).max(1_800_000).default(120_000),
215
+ }),
216
+ // 使用场景(G1):默认关;开时抽取 prompt 追加 usageScenario 指令(off 逐字不变)。
217
+ usageScenario: Schema.object({
218
+ enabled: Schema.boolean().default(false),
219
+ }),
220
+ // 类型叙述模板(G2):默认**开**;开时抽取 prompt 追加分类型 content 骨架(关时仍逐字不变)。
221
+ // 默认值从关改开的理由写在上方接口注释:只给定义实测骨架齐备 0/23,
222
+ // 而"没前因后果就不算可用"是用户定的判据,所以默认值要按可用性定而不是按保守定。
223
+ narrativeTemplates: Schema.object({
224
+ enabled: Schema.boolean().default(true),
225
+ }),
226
+ // 名称蒸馏(Phase 3)。两个开关的默认值都来自 A/B 实测,不是审美偏好:
227
+ // `ab-distill.mjs`(真会话)+0.703 骨架齐备 → 默认开;`ab-naming.mjs`(存量改名)
228
+ // −0.113 → 证明示例不能跨任务复用,所以抽取提示里的那段不注入命名脚本。
229
+ naming: Schema.object({
230
+ enabled: Schema.boolean().default(true),
231
+ fewShot: Schema.boolean().default(true),
232
+ }),
233
+ // 召回相关度反馈:默认开(只新增写端点与播报,不改任何既有产物;播报需用户显式打 -1)。
234
+ recallFeedback: Schema.object({
235
+ enabled: Schema.boolean().default(true),
236
+ announce: Schema.boolean().default(true),
237
+ // 低分沉底/高分上浮默认开(这是打分的常规出口);0 分硬剔除默认关。
238
+ affectRank: Schema.boolean().default(true),
239
+ excludeZero: Schema.boolean().default(false),
240
+ }),
132
241
  recall: Schema.object({
133
242
  enabled: Schema.boolean().default(true),
134
243
  maxResults: Schema.number().min(1).max(20).default(5),
@@ -221,6 +330,8 @@ export const memorySchema = Schema.object({
221
330
  maxSlots: Schema.number().min(1).max(32).default(8),
222
331
  maxAlwaysOnBytes: Schema.number().min(128).max(16_384).default(2048),
223
332
  maxBodyChars: Schema.number().min(32).max(2048).default(512),
333
+ // 旧 slot 语义迁移:默认关(不自动碰用户手动 pin 的常驻约定)。
334
+ migrateLegacy: Schema.boolean().default(false),
224
335
  }),
225
336
  tools: Schema.boolean().default(true),
226
337
  benchControl: Schema.boolean().default(false),
@@ -39,6 +39,18 @@ export interface ConflictListDeps {
39
39
  l1: Pick<L1Store, 'listConflictPending' | 'countConflictPendingUnresolved' | 'getByIds'>;
40
40
  /** `conflictFreeze.enabled`。 */
41
41
  conflictFreezeEnabled: boolean;
42
+ /** G3 成熟度旁注用:召回使用统计(缺省按 0 强化)。 */
43
+ usage?: {
44
+ get(id: string): {
45
+ used: number;
46
+ } | null | undefined;
47
+ };
48
+ /** 时效半衰期(天);≤0 = 衰减关 → aging 视为 null。 */
49
+ decayHalfLifeDays?: number;
50
+ /** 人工打分面(可选):成熟度旁注要吃“最近一笔”,与面板/召回同一口径。 */
51
+ feedback?: {
52
+ lastOf(id: string): number | undefined;
53
+ };
42
54
  }
43
55
  /**
44
56
  * 列出待裁决对。
@@ -1,4 +1,5 @@
1
1
  import { DEFER_MAX } from './store/conflicts.js';
2
+ import { computeMaturity } from './util/maturity.js';
2
3
  const OUTCOMES = ['winner', 'loser', 'both', 'defer'];
3
4
  function view(partial) {
4
5
  return { pair_id: '', outcome: '', resolved_at: '', removed_record_id: '', ...partial };
@@ -150,6 +151,9 @@ export function listConflictPairs(deps, opts = {}) {
150
151
  const contentById = new Map();
151
152
  // §C 三轴(conflict-3axis):记忆记录自带有效期与持续性,取出来用于裁决视图对比。
152
153
  const axisById = new Map();
154
+ // G3 成熟度旁注:取 priority/updatedAt 供 computeMaturity(读时派生,不落库)。
155
+ const priorityById = new Map();
156
+ const updatedAtById = new Map();
153
157
  for (const r of deps.l1.getByIds([...ids])) {
154
158
  contentById.set(r.id, r.content);
155
159
  axisById.set(r.id, {
@@ -157,8 +161,23 @@ export function listConflictPairs(deps, opts = {}) {
157
161
  validTo: (r.validTo ?? null),
158
162
  persistence: (r.persistence ?? null),
159
163
  });
164
+ priorityById.set(r.id, r.priority ?? 60);
165
+ updatedAtById.set(r.id, r.updatedAt ?? 0);
160
166
  }
161
167
  const axisOf = (id) => axisById.get(id) ?? { validFrom: null, validTo: null, persistence: null };
168
+ // 成熟度(priority + 被用次数 + 老化)——仅帮人工裁决看“哪条更确立”,不参与自动了结。
169
+ const halfLife = deps.decayHalfLifeDays ?? 0;
170
+ const maturityOf = (id) => {
171
+ const aging = halfLife > 0
172
+ ? Math.max(0.5, Math.pow(0.5, Math.max(0, (Date.now() - (updatedAtById.get(id) ?? 0)) / 86_400_000) / halfLife))
173
+ : null;
174
+ return computeMaturity({
175
+ priority: priorityById.get(id) ?? 60,
176
+ usage: deps.usage?.get(id) ?? null,
177
+ agingWeight: aging,
178
+ mark: deps.feedback?.lastOf(id) ?? null,
179
+ });
180
+ };
162
181
  const items = pending.map((p) => {
163
182
  const w = axisOf(p.winnerId);
164
183
  const l = axisOf(p.loserId);
@@ -184,6 +203,8 @@ export function listConflictPairs(deps, opts = {}) {
184
203
  // ('hard' / ''),使"旧行经 ALTER 回填"与"读取面兜底"呈现同一个形状。
185
204
  conflict_type: p.conflictType ?? 'hard',
186
205
  claim_key: p.claimKey ?? '',
206
+ winner_maturity: maturityOf(p.winnerId),
207
+ loser_maturity: maturityOf(p.loserId),
187
208
  };
188
209
  });
189
210
  return { enabled: true, total: deps.l1.countConflictPendingUnresolved(), items };
@@ -201,6 +222,7 @@ export function renderConflicts(v) {
201
222
  const vf = side === 'winner' ? p.winner_valid_from_ms : p.loser_valid_from_ms;
202
223
  const vt = side === 'winner' ? p.winner_valid_to_ms : p.loser_valid_to_ms;
203
224
  const ps = side === 'winner' ? p.winner_persistence : p.loser_persistence;
225
+ const mt = side === 'winner' ? p.winner_maturity : p.loser_maturity;
204
226
  const parts = [];
205
227
  const vfS = iso(vf);
206
228
  const vtS = iso(vt);
@@ -210,6 +232,8 @@ export function renderConflicts(v) {
210
232
  parts.push(`有效期止 ${vtS}`);
211
233
  if (ps)
212
234
  parts.push(`持续性 ${ps}`);
235
+ if (mt)
236
+ parts.push(`成熟度 ${mt}`);
213
237
  return parts.length ? ` 三轴: ${parts.join(' / ')}` : '';
214
238
  };
215
239
  const rows = v.items.map((p, i) => {
@@ -103,6 +103,9 @@ export interface MemoryLiveSettings {
103
103
  /** §C 人工冲突裁决总开关:true = 去重判定"两边都像对的"时冻结冲突对,停放到待人工裁决区;
104
104
  * false = 默认,冲突按 LLM 的 winner/loser 自动了结。运行时覆盖静态 config 的 conflictFreeze.enabled。 */
105
105
  conflictFreeze: boolean;
106
+ /** 轨2 技能结晶总开关(看板可拨):true = 注册 memory_skill_* 工具、注入 active 技能、skills-* 端点可用。
107
+ * 默认 false(新功能默认关);与部署 cfg.skills.enabled 取或(任一开则生效)。 */
108
+ skillsEnabled: boolean;
106
109
  }
107
110
  /** 召回停用原因(session-stats recall.enabled=false 时带出;短路序第一个为假的因子)。 */
108
111
  export type RecallDisabledReason = 'deploy' | 'global' | 'session' | 'mode';
@@ -360,6 +363,39 @@ export interface DownloadProgress {
360
363
  /** 嵌入源活切换阶段。 */
361
364
  export type ApplyPhase = 'idle' | 'installing-runtime' | 'warming' | 'switching' | 'reindexing' | 'done' | 'error';
362
365
  /** 重嵌入进度。 */
366
+ /**
367
+ * 一轮重建跑完后的留痕。
368
+ *
369
+ * 为什么必需:进度条只在 `running` 期间存在,而“待补 0 条”的重建会**瞬间跑完**
370
+ * (l1Total/l0Total 都是 0),界面与“点了什么也没发生”长得一模一样。真机反馈原话就是
371
+ * “重建索引无法观察进度无进度条、索引重设的效果无法确认”——缺的不是进度,是事后凭证。
372
+ * 所以这里同时存**重建前后的待补数**:效果就是这两个数的差,不是“我看它转了一下”。
373
+ */
374
+ export interface ReindexRunSummary {
375
+ /** 完成时刻(ms epoch);0 = 从未跑过。 */
376
+ finishedAt: number;
377
+ /** 从 startedAt 到完成的毫秒数。 */
378
+ durationMs: number;
379
+ /** 本轮实际重嵌条数(L1 + L0 完成数之和)。 */
380
+ processed: number;
381
+ /** 两层计划总数(为 0 就是“无事可做”,这不是失败而是本来就同步好了)。 */
382
+ planned: number;
383
+ /** 嵌入失败条数(进 skip 集的那些)。 */
384
+ failed: number;
385
+ cancelled: boolean;
386
+ /** 抛出来的整体错误(单层失败不在这里,在 failed 里)。 */
387
+ error?: string;
388
+ /** 重建**前**的待补数(-1 = 向量能力不可用)。 */
389
+ missingBefore: {
390
+ l1: number;
391
+ l0: number;
392
+ };
393
+ /** 重建**后**的待补数。 */
394
+ missingAfter: {
395
+ l1: number;
396
+ l0: number;
397
+ };
398
+ }
363
399
  export interface ReindexProgressState {
364
400
  running: boolean;
365
401
  l1Done: number;
@@ -369,6 +405,8 @@ export interface ReindexProgressState {
369
405
  startedAt: number;
370
406
  cancelled: boolean;
371
407
  error?: string;
408
+ /** 上一次重建的结果(从未跑过则缺省)。 */
409
+ last?: ReindexRunSummary;
372
410
  }
373
411
  /** 嵌入源状态视图(embedding-state-get 端点的主体)。 */
374
412
  export interface EmbeddingStateView {
@@ -796,6 +834,24 @@ export interface UiRecord {
796
834
  retired?: boolean;
797
835
  /** 退场原因:`conflict`(裁决) / `superseded`(取代) / `manual`(人工) / `unknown`。未退场为 null。 */
798
836
  retiredReason?: string | null;
837
+ /**
838
+ * 「正文已被摘要取代」:压缩发生时这条还在本会话召回列表里,它的正文已不在模型上下文。
839
+ * 与 `retired` **正交**——退场讲“人有没有对它下过裁决”,取代讲“正文还在不在上下文”。
840
+ * 只在 `dsh-memory/session-recalled` 这条路径上有意义;缺省 = 无此概念(旧服务端)。
841
+ */
842
+ superseded?: boolean;
843
+ /**
844
+ * 成熟度(G3 读时派生):priority + 被用次数 + 老化权重合成。
845
+ * 可选字段——旧 client 忽略=向后兼容;不参与存储/快照(纯投影)。
846
+ */
847
+ maturity?: 'low' | 'mid' | 'high';
848
+ /** 使用场景(G1):从 metadata.dsh_usage_scenario 读出;功能关/无则 undefined。 */
849
+ usageScenario?: string[];
850
+ /**
851
+ * 人给的相关度均值(0..5);从未被 0..5 打过分为 undefined(**缺席 ≠ 0 分**)。
852
+ * 读时从 recall-feedback sidecar 派生,不进存储/快照。
853
+ */
854
+ userRelevance?: number;
799
855
  }
800
856
  export interface ListRecordsResponse {
801
857
  items: UiRecord[];
@@ -822,6 +878,14 @@ export interface ListRecordsResponse {
822
878
  id: string;
823
879
  label: string;
824
880
  }>;
881
+ /**
882
+ * 类型词表目录(pm-name-distill task_1.5:词表单一真源随首屏下发,client 不再手抄
883
+ * `TYPE_LABELS`;仅 offset===0 时附带)。缺省(旧服务端)时 client 降级显示 type 原值。
884
+ */
885
+ typeCatalog?: Array<{
886
+ id: string;
887
+ label: string;
888
+ }>;
825
889
  }
826
890
  /** dsh-memory/records-delete(面板高权限退场指定记忆;须 memoryMutate 开启)。 */
827
891
  export interface RecordsDeleteRequest {
@@ -882,6 +946,30 @@ export interface CleanupRetiredRequest {
882
946
  }
883
947
  export interface CleanupRetiredResponse {
884
948
  dryRun: boolean;
949
+ /**
950
+ * 待删/已删清单预览(最多 50 条)。
951
+ *
952
+ * `targets` 只是一个计数,人无法从计数判断“这批是不是该删的那批”——物理删除不可逆,
953
+ * 干跑报告必须能抽看内容。取前 50 条而不是全量:全量会把面板撑爆,而判据只需抽看。
954
+ */
955
+ items?: Array<{
956
+ id: string;
957
+ name: string;
958
+ type: string;
959
+ retiredAt: string;
960
+ }>;
961
+ /** 本批之外发生漂移的记录数(只计数、**不中止**)。 */
962
+ otherChanged?: number;
963
+ /** 归档正文 `retired.md` 路径(未写 = 空串)。 */
964
+ archiveMd?: string;
965
+ /** 投射文件搬运计数(N12;投射面关着时全 0)。 */
966
+ files?: {
967
+ moved: number;
968
+ failed: number;
969
+ absent: number;
970
+ };
971
+ /** 图投影索引表里一并抹掉的行数(F19;图面未装配或不该动时为 0)。 */
972
+ graphForgotten?: number;
885
973
  /** 本次涉及(干跑)或实际处理(真跑)的条数。 */
886
974
  targets: number;
887
975
  /** 真正物理删除的条数(干跑恒为 0;中止恒为 0)。 */
@@ -1218,6 +1306,9 @@ export interface ConflictPairView {
1218
1306
  loser_valid_from_ms?: number | null;
1219
1307
  loser_valid_to_ms?: number | null;
1220
1308
  loser_persistence?: string | null;
1309
+ /** G3 成熟度旁注(读时派生,仅帮人工看“哪条更确立”,不参与自动裁决)。 */
1310
+ winner_maturity?: 'low' | 'mid' | 'high';
1311
+ loser_maturity?: 'low' | 'mid' | 'high';
1221
1312
  review_state?: 'unseen' | 'deferred';
1222
1313
  /** R1:复看次数(面板据此显示"已复看 N 次");达 `DEFER_MAX` 即钉子户。 */
1223
1314
  defer_count?: number;
@@ -1303,6 +1394,216 @@ export interface ReceiptsResponse {
1303
1394
  items: ReceiptItemView[];
1304
1395
  total: number;
1305
1396
  }
1397
+ /** 外部来源视图(轨3 Connect;路径指针,不含正文)。 */
1398
+ export interface MemorySourceView {
1399
+ id: string;
1400
+ kind: string;
1401
+ path: string;
1402
+ label?: string;
1403
+ lastScanAt: string;
1404
+ lastImportAt: string;
1405
+ disabled: boolean;
1406
+ status: 'active' | 'retired';
1407
+ }
1408
+ export interface MemorySourcesListResponse {
1409
+ sources: MemorySourceView[];
1410
+ /** 写门/启用态提示(面板据它显示“导入需开高权限”)。 */
1411
+ memoryMutate: boolean;
1412
+ }
1413
+ export interface MemorySourcesAddRequest {
1414
+ id: string;
1415
+ kind: string;
1416
+ path: string;
1417
+ label?: string;
1418
+ }
1419
+ export interface MemorySourcesAddResponse {
1420
+ id: string;
1421
+ created: boolean;
1422
+ notice: string;
1423
+ }
1424
+ export interface MemorySourcesRetireRequest {
1425
+ id: string;
1426
+ /** true = 恢复为 active;缺省/ false = 退役(不删数据,来源仍可重放)。 */
1427
+ active?: boolean;
1428
+ }
1429
+ export interface MemorySourcesRetireResponse {
1430
+ changed: boolean;
1431
+ notice: string;
1432
+ }
1433
+ export interface MemportScanRequest {
1434
+ /** 省略 = 扫描全部启用来源;指定 = 只扫该 id。 */
1435
+ sourceId?: string;
1436
+ }
1437
+ export interface MemportScanCounts {
1438
+ dup: number;
1439
+ 'near-dup': number;
1440
+ 'conflict-candidate': number;
1441
+ new: number;
1442
+ }
1443
+ export interface MemportScanSourceResult {
1444
+ sourceId: string;
1445
+ label?: string;
1446
+ /** 命中的 .mem 包数(只读,未写库)。 */
1447
+ files: number;
1448
+ /** 解析出的总条数(受 scanMaxRecords 上限)。 */
1449
+ total: number;
1450
+ /** 每包校验失败原因(不阻断其余来源)。 */
1451
+ errors: string[];
1452
+ counts: MemportScanCounts;
1453
+ /** 新记录样本预览(正文截断;实际写库走 memory_import,那里才做脱敏)。 */
1454
+ sampleNew: Array<{
1455
+ type: string;
1456
+ content: string;
1457
+ }>;
1458
+ }
1459
+ export interface MemportScanResponse {
1460
+ /** cfg.memport.enabled——关时 sources 为空并附 notice。 */
1461
+ enabled: boolean;
1462
+ sources: MemportScanSourceResult[];
1463
+ notice?: string;
1464
+ }
1465
+ /** 技能摘要(轨2 SkillsTab;供人工审阅/晋升/pin/归档)。 */
1466
+ export interface SkillView {
1467
+ id: string;
1468
+ title: string;
1469
+ status: 'candidate' | 'active' | 'archived';
1470
+ tier: 'full' | 'excerpt' | 'hint';
1471
+ pinned: boolean;
1472
+ sessionCount: number;
1473
+ updatedAt: string;
1474
+ }
1475
+ export interface SkillsListResponse {
1476
+ enabled: boolean;
1477
+ /** 自动晋升总闸(allowPromote);关时 promote 动作会被拒。 */
1478
+ allowPromote: boolean;
1479
+ memoryMutate: boolean;
1480
+ skills: SkillView[];
1481
+ }
1482
+ export interface SkillsActionRequest {
1483
+ id: string;
1484
+ action: 'promote' | 'archive' | 'pin' | 'unpin' | 'set-tier';
1485
+ tier?: 'full' | 'excerpt' | 'hint';
1486
+ reason?: string;
1487
+ }
1488
+ export interface SkillsActionResponse {
1489
+ changed: boolean;
1490
+ notice: string;
1491
+ }
1492
+ /** dsh-memory/session-recalled (G-live):本会话已注入、未被压缩覆盖的召回集。 */
1493
+ export interface SessionRecalledRequest {
1494
+ sessionId: string;
1495
+ }
1496
+ export interface SessionRecalledResponse {
1497
+ sessionId: string;
1498
+ /** 按召回插入序(旧→新);复用 UiRecord(含 maturity/usageScenario)。 */
1499
+ records: UiRecord[];
1500
+ /** 词表目录(pill 与面板同源取标签;见 `ListRecordsResponse.typeCatalog`)。 */
1501
+ typeCatalog?: Array<{
1502
+ id: string;
1503
+ label: string;
1504
+ }>;
1505
+ }
1506
+ /**
1507
+ * dsh-memory/recall-feedback(人在召回卡上打分)。
1508
+ *
1509
+ * 语义边界:`0..5` = 人给的相关度(只聚合、不改写任何排序分);`-1` = 声明过时,
1510
+ * 服务端会顺手做三件事——退场该记录(软删,可 `records-restore` 恢复)、从本会话
1511
+ * 活召回集摘掉、把“已向模型播报”入队(下一轮一次性告知)。
1512
+ */
1513
+ export interface RecallFeedbackRequest {
1514
+ sessionId: string;
1515
+ /** 被打分的那条召回记录 id(必须是本会话活召回集里的,否则拒)。 */
1516
+ id: string;
1517
+ /** -1 或 0..5。 */
1518
+ score: number;
1519
+ }
1520
+ export interface RecallFeedbackResponse {
1521
+ id: string;
1522
+ score: number;
1523
+ /** 聚合后的反馈条目。 */
1524
+ rating: {
1525
+ last: number;
1526
+ rated: number;
1527
+ mean: number;
1528
+ obsolete: number;
1529
+ };
1530
+ /** 本次是否触发了退场(仅 score=-1)。 */
1531
+ retired: boolean;
1532
+ /** 退场后是否已排入下一轮向模型播报(仅 score=-1 且 cfg.recallFeedback.announce)。 */
1533
+ announced: boolean;
1534
+ /**
1535
+ * 失效联动(N11)的文件侧结果,仅当 `score === -1`、真退场且 `cfg.memExport.enabled` 时出现:
1536
+ * `renamed`(投射文件已改名 `.memory.md.disable`)/ `created`(本来没导出过,新建了凭证)/
1537
+ * `already`(早就是 `.disable`)/ `no-workspace`(归属不明,**没动任何文件**)/ `failed`。
1538
+ * 缺席 = 本次未走文件联动(功能关着或账本未装配);它不影响 `retired`,退场已成立。
1539
+ */
1540
+ fileDisabled?: string;
1541
+ }
1542
+ /**
1543
+ * dsh-memory/mem-export / mem-reconcile:md 投射的导出与对账(P1 + N8/N9)。
1544
+ *
1545
+ * 两个都无入参(护栏条数/墙钟来自 config),响应形状直接取存储层的报告类型
1546
+ * ——同一事实源,避开“契约手抄一份然后与实现漂移”。
1547
+ *
1548
+ * 写面要说准:**对账什么都不写**;**导出写文件、写账本,并会回填一次 `l1_records.name`**
1549
+ * (给没名字的记录派生全库唯一的文件身份名,计数在 `named`/`nameFailed`)。那一步只改
1550
+ * `name`:内容、类型、退场态、检索面一律不动,而 `name` 不在 `canonicalRecords` 的快照
1551
+ * 哈希字段表里,所以“导出不得改变库内容”这条护栏仍成立——但“导出不碰库”不成立。
1552
+ */
1553
+ export type MemExportRequest = Record<string, never>;
1554
+ export type MemExportResponse = import('./store/memory-md-types.js').MemExportResponse;
1555
+ export type MemReconcileRequest = Record<string, never>;
1556
+ export type MemReconcileResponse = import('./store/memory-md-types.js').ReconcileReport;
1557
+ /**
1558
+ * dsh-memory/mem-sync:P2 反向同步(md 文件 → 索引)。
1559
+ *
1560
+ * 与 `mem-reconcile` 的区别是风险不对称:对账**什么都不写**,同步**会写库**(回灌
1561
+ * 文件侧修改、把 `.disable` 对应的记录退场)。所以服务端除了配置门还要高权限门。
1562
+ * 两边都变的那批**两侧都不动**,只在 `pending` 里挂账等人裁决。
1563
+ */
1564
+ export type MemSyncRequest = Record<string, never>;
1565
+ export type MemSyncResponse = import('./store/memory-md-types.js').MdSyncReport;
1566
+ /**
1567
+ * dsh-memory/mem-orphans:P4.4 孤儿投射文件归档。
1568
+ *
1569
+ * 只搬“**账本记着我们写过、但现在没记录映射到这个路径**”的文件(改名/消歧/物理删除都会
1570
+ * 留下这种残片)。账本里没有的来路不明文件**一律不动**——那可能是人放进投射目录等着
1571
+ * 被同步进来的输入。
1572
+ *
1573
+ * `dryRun` **省略即 true**:搬文件在用户眼里与删文件差不离,必须显式要;真搬还需要高权限模式。
1574
+ */
1575
+ export interface MemOrphansRequest {
1576
+ dryRun?: boolean;
1577
+ }
1578
+ export type MemOrphansResponse = import('./store/memory-md-types.js').OrphanReport;
1579
+ /**
1580
+ * dsh-memory/workspace-relink:按内容路由把存量记忆归到 workspace。
1581
+ *
1582
+ * 只写 `workspace_id` 列——**不改 `scope`**,所以可见性今天一行不变
1583
+ * (`isScopeVisible` 先看 scope,global 即全可见);`workspace_id` 不在
1584
+ * `canonicalRecords` 快照哈希字段表里,因此回填不会让旧快照失配。
1585
+ */
1586
+ export interface WorkspaceRelinkRequest {
1587
+ /** 缺省 true(干跑):只看报告不写库。给 `false` 才真写(需高权限)。 */
1588
+ dryRun?: boolean;
1589
+ }
1590
+ export interface WorkspaceRelinkResponse {
1591
+ dryRun: boolean;
1592
+ /** 本次扫过的记录数(含未命中)。 */
1593
+ scanned: number;
1594
+ /** 路由命中的记录数。 */
1595
+ matched: number;
1596
+ /** 归属已是目标值(幂等重跑时等于 matched)。 */
1597
+ unchanged: number;
1598
+ /** 干跑时“若真跑会写多少条”;真写时恒为 0。 */
1599
+ toWrite: number;
1600
+ /** 实际写入条数(干跑恒 0)。 */
1601
+ written: number;
1602
+ /** 未扫到、下次触发续扫的条数。 */
1603
+ remaining: number;
1604
+ /** 命中记录按归属分布——拿它判断路由表合不合口味。 */
1605
+ byWorkspace: Record<string, number>;
1606
+ }
1306
1607
  export interface DshMemoryRequestMap {
1307
1608
  'dsh-memory/stats': Record<string, never>;
1308
1609
  'dsh-memory/token-cost': TokenCostRequest;
@@ -1350,6 +1651,19 @@ export interface DshMemoryRequestMap {
1350
1651
  'dsh-memory/cleanup-retired': CleanupRetiredRequest;
1351
1652
  'dsh-memory/snapshots-list': SnapshotsListRequest;
1352
1653
  'dsh-memory/snapshot-restore': SnapshotRestoreRequest;
1654
+ 'dsh-memory/memory-sources-list': Record<string, never>;
1655
+ 'dsh-memory/memory-sources-add': MemorySourcesAddRequest;
1656
+ 'dsh-memory/memory-sources-retire': MemorySourcesRetireRequest;
1657
+ 'dsh-memory/memport-scan': MemportScanRequest;
1658
+ 'dsh-memory/skills-list': Record<string, never>;
1659
+ 'dsh-memory/session-recalled': SessionRecalledRequest;
1660
+ 'dsh-memory/recall-feedback': RecallFeedbackRequest;
1661
+ 'dsh-memory/workspace-relink': WorkspaceRelinkRequest;
1662
+ 'dsh-memory/mem-export': MemExportRequest;
1663
+ 'dsh-memory/mem-reconcile': MemReconcileRequest;
1664
+ 'dsh-memory/mem-sync': MemSyncRequest;
1665
+ 'dsh-memory/mem-orphans': MemOrphansRequest;
1666
+ 'dsh-memory/skills-action': SkillsActionRequest;
1353
1667
  }
1354
1668
  export interface DshMemoryResponseMap {
1355
1669
  'dsh-memory/stats': StatsResponse;
@@ -1398,6 +1712,19 @@ export interface DshMemoryResponseMap {
1398
1712
  'dsh-memory/cleanup-retired': CleanupRetiredResponse;
1399
1713
  'dsh-memory/snapshots-list': SnapshotsListResponse;
1400
1714
  'dsh-memory/snapshot-restore': SnapshotRestoreResponse;
1715
+ 'dsh-memory/memory-sources-list': MemorySourcesListResponse;
1716
+ 'dsh-memory/memory-sources-add': MemorySourcesAddResponse;
1717
+ 'dsh-memory/memory-sources-retire': MemorySourcesRetireResponse;
1718
+ 'dsh-memory/memport-scan': MemportScanResponse;
1719
+ 'dsh-memory/skills-list': SkillsListResponse;
1720
+ 'dsh-memory/session-recalled': SessionRecalledResponse;
1721
+ 'dsh-memory/recall-feedback': RecallFeedbackResponse;
1722
+ 'dsh-memory/workspace-relink': WorkspaceRelinkResponse;
1723
+ 'dsh-memory/mem-export': MemExportResponse;
1724
+ 'dsh-memory/mem-reconcile': MemReconcileResponse;
1725
+ 'dsh-memory/mem-sync': MemSyncResponse;
1726
+ 'dsh-memory/mem-orphans': MemOrphansResponse;
1727
+ 'dsh-memory/skills-action': SkillsActionResponse;
1401
1728
  }
1402
1729
  /** 全部端点名(client 调用与 host case 表的共用字面量来源)。 */
1403
1730
  export type DshMemoryEndpoint = keyof DshMemoryResponseMap;
@@ -47,3 +47,22 @@ export declare function sortByDomainWeight<T extends {
47
47
  id: string;
48
48
  score: number;
49
49
  }>(hits: readonly T[], wingOf: (id: string) => unknown, weights: Record<string, number>): T[];
50
+ /**
51
+ * G1 使用场景细排(纯函数、**score 不改写**、默认关时不进此路径):
52
+ * 命中查询词的 usageScenario 记忆上浮;未命中/无场景的记录保持原序(fail-open)。
53
+ * 保守:只按“命中强度”重排,不引入新相关性判断;无查询词时原样返回。
54
+ */
55
+ export declare function sortByUsageScenario<T extends {
56
+ id: string;
57
+ }>(hits: readonly T[], scenariosOf: (id: string) => readonly string[], query: string): T[];
58
+ /**
59
+ * 人工打分参与排序(纯重排、**score 不改写**):4/5 上浮、0/1/2 下沉。
60
+ *
61
+ * 三条约束:
62
+ * - 同档内保持进入顺序(稳定排序),所以不会推翻 scopeFence / 场景细排的结果;
63
+ * - **未打分与 3 分同为中性**(fail-open),全部未打分时输出与输入逐字同序;
64
+ * - 判据是**最近一笔**而非均值——用户改主意的路径必须存在(先 5 后 0 应当下沉)。
65
+ */
66
+ export declare function sortByHumanMark<T extends {
67
+ id: string;
68
+ }>(hits: readonly T[], markOf: (id: string) => number | undefined): T[];