dsh-prime-memory 0.22.2 → 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.
- package/CHANGELOG.en.md +401 -0
- package/CHANGELOG.ja.md +401 -0
- package/CHANGELOG.ko.md +401 -0
- package/CHANGELOG.md +100 -0
- package/dist/client.js +1180 -845
- package/dist/config.d.ts +128 -1341
- package/dist/config.js +80 -4
- package/dist/conflict-service.d.ts +4 -0
- package/dist/conflict-service.js +6 -1
- package/dist/contract.d.ts +196 -0
- package/dist/domain-gate.d.ts +11 -0
- package/dist/domain-gate.js +15 -0
- package/dist/hooks/obsolete-notice.d.ts +26 -0
- package/dist/hooks/obsolete-notice.js +64 -0
- package/dist/hooks/recall.d.ts +39 -0
- package/dist/hooks/recall.js +110 -30
- package/dist/index.d.ts +1 -1127
- package/dist/index.js +30 -2
- package/dist/memory-types.d.ts +69 -0
- package/dist/memory-types.js +165 -0
- package/dist/memport/format.d.ts +6 -1
- package/dist/memport/format.js +42 -2
- package/dist/pipeline/l1.d.ts +44 -2
- package/dist/pipeline/l1.js +163 -9
- package/dist/pipeline/md-sync.d.ts +65 -0
- package/dist/pipeline/md-sync.js +456 -0
- package/dist/pipeline/ruminate.d.ts +43 -1
- package/dist/pipeline/ruminate.js +100 -0
- package/dist/pipeline/runner.d.ts +10 -0
- package/dist/pipeline/runner.js +7 -1
- package/dist/pipeline/workspace-relink.d.ts +60 -0
- package/dist/pipeline/workspace-relink.js +73 -0
- package/dist/projection/recall.d.ts +51 -13
- package/dist/projection/recall.js +47 -12
- package/dist/prompts/examples.d.ts +73 -0
- package/dist/prompts/examples.js +178 -0
- package/dist/prompts/l1-dedup.d.ts +9 -3
- package/dist/prompts/l1-dedup.js +34 -13
- package/dist/prompts/l1-extraction.d.ts +5 -3
- package/dist/prompts/l1-extraction.js +49 -4
- package/dist/settings.d.ts +8 -0
- package/dist/settings.js +12 -1
- package/dist/stats.d.ts +12 -0
- package/dist/stats.js +327 -15
- package/dist/store/embedding-source.d.ts +8 -0
- package/dist/store/embedding-source.js +39 -2
- package/dist/store/file-versions.d.ts +2 -0
- package/dist/store/file-versions.js +2 -0
- package/dist/store/graph-store.d.ts +14 -0
- package/dist/store/graph-store.js +35 -0
- package/dist/store/l1-snapshot.d.ts +26 -1
- package/dist/store/l1-snapshot.js +53 -8
- package/dist/store/l1.d.ts +75 -3
- package/dist/store/l1.js +124 -3
- package/dist/store/memory-md-archive.d.ts +84 -0
- package/dist/store/memory-md-archive.js +155 -0
- package/dist/store/memory-md-disable.d.ts +23 -0
- package/dist/store/memory-md-disable.js +106 -0
- package/dist/store/memory-md-export.d.ts +50 -0
- package/dist/store/memory-md-export.js +200 -0
- package/dist/store/memory-md-ledger.d.ts +94 -0
- package/dist/store/memory-md-ledger.js +243 -0
- package/dist/store/memory-md-orphan.d.ts +27 -0
- package/dist/store/memory-md-orphan.js +153 -0
- package/dist/store/memory-md-parse.d.ts +35 -0
- package/dist/store/memory-md-parse.js +85 -0
- package/dist/store/memory-md-skeleton.d.ts +18 -0
- package/dist/store/memory-md-skeleton.js +61 -0
- package/dist/store/memory-md-types.d.ts +174 -0
- package/dist/store/memory-md-types.js +10 -0
- package/dist/store/memory-md.d.ts +108 -0
- package/dist/store/memory-md.js +219 -0
- package/dist/store/obsolete-notices.d.ts +39 -0
- package/dist/store/obsolete-notices.js +153 -0
- package/dist/store/recall-feedback.d.ts +75 -0
- package/dist/store/recall-feedback.js +213 -0
- package/dist/store/recall-usage.d.ts +11 -0
- package/dist/store/recall-usage.js +29 -0
- package/dist/store/session-recalled.d.ts +38 -3
- package/dist/store/session-recalled.js +156 -9
- package/dist/store/sqlite.d.ts +66 -0
- package/dist/store/sqlite.js +233 -15
- package/dist/store/supersede.d.ts +1 -1
- package/dist/store/supersede.js +1 -1
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +23 -6
- package/dist/types.d.ts +24 -7
- package/dist/types.js +10 -2
- package/dist/util/maturity.d.ts +20 -3
- package/dist/util/maturity.js +18 -1
- package/dist/util/md-name.d.ts +129 -0
- package/dist/util/md-name.js +366 -0
- package/dist/util/md-normalize.d.ts +23 -0
- package/dist/util/md-normalize.js +131 -0
- package/dist/workspace-route.d.ts +22 -0
- package/dist/workspace-route.js +66 -0
- package/dsh.plugin.json +1 -1
- 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
|
-
|
|
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),
|
|
@@ -78,7 +78,13 @@ export function liveSettingsSchema() {
|
|
|
78
78
|
toolRuminate: Schema.boolean().default(true),
|
|
79
79
|
toolConflict: Schema.boolean().default(true),
|
|
80
80
|
toolMutate: Schema.boolean().default(true),
|
|
81
|
-
})
|
|
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);
|
|
82
88
|
}
|
|
83
89
|
export const memorySchema = Schema.object({
|
|
84
90
|
dataDir: Schema.string().default(''),
|
|
@@ -154,13 +160,83 @@ export const memorySchema = Schema.object({
|
|
|
154
160
|
maxRecords: Schema.number().min(1).max(10_000).default(300),
|
|
155
161
|
timeBudgetMs: Schema.number().min(1000).max(1_800_000).default(120_000),
|
|
156
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
|
+
}),
|
|
157
216
|
// 使用场景(G1):默认关;开时抽取 prompt 追加 usageScenario 指令(off 逐字不变)。
|
|
158
217
|
usageScenario: Schema.object({
|
|
159
218
|
enabled: Schema.boolean().default(false),
|
|
160
219
|
}),
|
|
161
|
-
// 类型叙述模板(G2
|
|
220
|
+
// 类型叙述模板(G2):默认**开**;开时抽取 prompt 追加分类型 content 骨架(关时仍逐字不变)。
|
|
221
|
+
// 默认值从关改开的理由写在上方接口注释:只给定义实测骨架齐备 0/23,
|
|
222
|
+
// 而"没前因后果就不算可用"是用户定的判据,所以默认值要按可用性定而不是按保守定。
|
|
162
223
|
narrativeTemplates: Schema.object({
|
|
163
|
-
enabled: Schema.boolean().default(
|
|
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),
|
|
164
240
|
}),
|
|
165
241
|
recall: Schema.object({
|
|
166
242
|
enabled: Schema.boolean().default(true),
|
package/dist/conflict-service.js
CHANGED
|
@@ -171,7 +171,12 @@ export function listConflictPairs(deps, opts = {}) {
|
|
|
171
171
|
const aging = halfLife > 0
|
|
172
172
|
? Math.max(0.5, Math.pow(0.5, Math.max(0, (Date.now() - (updatedAtById.get(id) ?? 0)) / 86_400_000) / halfLife))
|
|
173
173
|
: null;
|
|
174
|
-
return computeMaturity({
|
|
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
|
+
});
|
|
175
180
|
};
|
|
176
181
|
const items = pending.map((p) => {
|
|
177
182
|
const w = axisOf(p.winnerId);
|
package/dist/contract.d.ts
CHANGED
|
@@ -363,6 +363,39 @@ export interface DownloadProgress {
|
|
|
363
363
|
/** 嵌入源活切换阶段。 */
|
|
364
364
|
export type ApplyPhase = 'idle' | 'installing-runtime' | 'warming' | 'switching' | 'reindexing' | 'done' | 'error';
|
|
365
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
|
+
}
|
|
366
399
|
export interface ReindexProgressState {
|
|
367
400
|
running: boolean;
|
|
368
401
|
l1Done: number;
|
|
@@ -372,6 +405,8 @@ export interface ReindexProgressState {
|
|
|
372
405
|
startedAt: number;
|
|
373
406
|
cancelled: boolean;
|
|
374
407
|
error?: string;
|
|
408
|
+
/** 上一次重建的结果(从未跑过则缺省)。 */
|
|
409
|
+
last?: ReindexRunSummary;
|
|
375
410
|
}
|
|
376
411
|
/** 嵌入源状态视图(embedding-state-get 端点的主体)。 */
|
|
377
412
|
export interface EmbeddingStateView {
|
|
@@ -799,6 +834,12 @@ export interface UiRecord {
|
|
|
799
834
|
retired?: boolean;
|
|
800
835
|
/** 退场原因:`conflict`(裁决) / `superseded`(取代) / `manual`(人工) / `unknown`。未退场为 null。 */
|
|
801
836
|
retiredReason?: string | null;
|
|
837
|
+
/**
|
|
838
|
+
* 「正文已被摘要取代」:压缩发生时这条还在本会话召回列表里,它的正文已不在模型上下文。
|
|
839
|
+
* 与 `retired` **正交**——退场讲“人有没有对它下过裁决”,取代讲“正文还在不在上下文”。
|
|
840
|
+
* 只在 `dsh-memory/session-recalled` 这条路径上有意义;缺省 = 无此概念(旧服务端)。
|
|
841
|
+
*/
|
|
842
|
+
superseded?: boolean;
|
|
802
843
|
/**
|
|
803
844
|
* 成熟度(G3 读时派生):priority + 被用次数 + 老化权重合成。
|
|
804
845
|
* 可选字段——旧 client 忽略=向后兼容;不参与存储/快照(纯投影)。
|
|
@@ -806,6 +847,11 @@ export interface UiRecord {
|
|
|
806
847
|
maturity?: 'low' | 'mid' | 'high';
|
|
807
848
|
/** 使用场景(G1):从 metadata.dsh_usage_scenario 读出;功能关/无则 undefined。 */
|
|
808
849
|
usageScenario?: string[];
|
|
850
|
+
/**
|
|
851
|
+
* 人给的相关度均值(0..5);从未被 0..5 打过分为 undefined(**缺席 ≠ 0 分**)。
|
|
852
|
+
* 读时从 recall-feedback sidecar 派生,不进存储/快照。
|
|
853
|
+
*/
|
|
854
|
+
userRelevance?: number;
|
|
809
855
|
}
|
|
810
856
|
export interface ListRecordsResponse {
|
|
811
857
|
items: UiRecord[];
|
|
@@ -832,6 +878,14 @@ export interface ListRecordsResponse {
|
|
|
832
878
|
id: string;
|
|
833
879
|
label: string;
|
|
834
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
|
+
}>;
|
|
835
889
|
}
|
|
836
890
|
/** dsh-memory/records-delete(面板高权限退场指定记忆;须 memoryMutate 开启)。 */
|
|
837
891
|
export interface RecordsDeleteRequest {
|
|
@@ -892,6 +946,30 @@ export interface CleanupRetiredRequest {
|
|
|
892
946
|
}
|
|
893
947
|
export interface CleanupRetiredResponse {
|
|
894
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;
|
|
895
973
|
/** 本次涉及(干跑)或实际处理(真跑)的条数。 */
|
|
896
974
|
targets: number;
|
|
897
975
|
/** 真正物理删除的条数(干跑恒为 0;中止恒为 0)。 */
|
|
@@ -1419,6 +1497,112 @@ export interface SessionRecalledResponse {
|
|
|
1419
1497
|
sessionId: string;
|
|
1420
1498
|
/** 按召回插入序(旧→新);复用 UiRecord(含 maturity/usageScenario)。 */
|
|
1421
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>;
|
|
1422
1606
|
}
|
|
1423
1607
|
export interface DshMemoryRequestMap {
|
|
1424
1608
|
'dsh-memory/stats': Record<string, never>;
|
|
@@ -1473,6 +1657,12 @@ export interface DshMemoryRequestMap {
|
|
|
1473
1657
|
'dsh-memory/memport-scan': MemportScanRequest;
|
|
1474
1658
|
'dsh-memory/skills-list': Record<string, never>;
|
|
1475
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;
|
|
1476
1666
|
'dsh-memory/skills-action': SkillsActionRequest;
|
|
1477
1667
|
}
|
|
1478
1668
|
export interface DshMemoryResponseMap {
|
|
@@ -1528,6 +1718,12 @@ export interface DshMemoryResponseMap {
|
|
|
1528
1718
|
'dsh-memory/memport-scan': MemportScanResponse;
|
|
1529
1719
|
'dsh-memory/skills-list': SkillsListResponse;
|
|
1530
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;
|
|
1531
1727
|
'dsh-memory/skills-action': SkillsActionResponse;
|
|
1532
1728
|
}
|
|
1533
1729
|
/** 全部端点名(client 调用与 host case 表的共用字面量来源)。 */
|
package/dist/domain-gate.d.ts
CHANGED
|
@@ -55,3 +55,14 @@ export declare function sortByDomainWeight<T extends {
|
|
|
55
55
|
export declare function sortByUsageScenario<T extends {
|
|
56
56
|
id: string;
|
|
57
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[];
|
package/dist/domain-gate.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* 可解释性:每次召回把 8 个域权重写进日志(可读出),权重计算是纯函数、可回归。
|
|
10
10
|
*/
|
|
11
11
|
import { WING_FALLBACK } from './types.js';
|
|
12
|
+
import { markSignal } from './util/maturity.js';
|
|
12
13
|
/** 8 条域锚文本(单一事实源):一段描述该域典型内容的短文本,供向量化。
|
|
13
14
|
* keywords 是嵌入关闭时的关键词降级匹配词表(命中任一 = 该域相关)。 */
|
|
14
15
|
export const WING_ANCHORS = [
|
|
@@ -179,3 +180,17 @@ export function sortByUsageScenario(hits, scenariosOf, query) {
|
|
|
179
180
|
.sort((a, b) => b.m - a.m || a.i - b.i)
|
|
180
181
|
.map((x) => x.h);
|
|
181
182
|
}
|
|
183
|
+
/**
|
|
184
|
+
* 人工打分参与排序(纯重排、**score 不改写**):4/5 上浮、0/1/2 下沉。
|
|
185
|
+
*
|
|
186
|
+
* 三条约束:
|
|
187
|
+
* - 同档内保持进入顺序(稳定排序),所以不会推翻 scopeFence / 场景细排的结果;
|
|
188
|
+
* - **未打分与 3 分同为中性**(fail-open),全部未打分时输出与输入逐字同序;
|
|
189
|
+
* - 判据是**最近一笔**而非均值——用户改主意的路径必须存在(先 5 后 0 应当下沉)。
|
|
190
|
+
*/
|
|
191
|
+
export function sortByHumanMark(hits, markOf) {
|
|
192
|
+
return hits
|
|
193
|
+
.map((h, i) => ({ h, i, s: markSignal(markOf(h.id)) }))
|
|
194
|
+
.sort((a, b) => b.s - a.s || a.i - b.i)
|
|
195
|
+
.map((x) => x.h);
|
|
196
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「记忆已声明过时」一次性播报 Hook。
|
|
3
|
+
*
|
|
4
|
+
* 用户在召回卡上打 `-1` 之后,记忆库那侧当场退场了,但模型此刻的上下文里那条记忆
|
|
5
|
+
* 还在——它必须被告知"这条已由用户判定过时,别再依据它作答",否则退场只发生在
|
|
6
|
+
* 用户看得见的地方,模型仍在按旧信息干活。
|
|
7
|
+
*
|
|
8
|
+
* 与槽位常驻注入(`slot-recall.ts`)的分工:那是**跨会话常驻**的约定,每轮都在;
|
|
9
|
+
* 本 Hook 是**一次性**的更正通知,`drain` 取走即离队,下一轮就没有了。
|
|
10
|
+
*
|
|
11
|
+
* 门控:`cfg.recallFeedback.enabled && cfg.recallFeedback.announce`。关时不进
|
|
12
|
+
* `agent/pre-step` 的注入分支(零出队、零新帧),既有会话逐字现状。
|
|
13
|
+
*
|
|
14
|
+
* 注入帧形状刻意与 recall/slot 同源(`source.kind:'plugin:memory'` + `form:'recall'`):
|
|
15
|
+
* 改这个形状会让宿主的"记忆召回份额"统计漏判(isOwnRecallSource),是踩过的红线。
|
|
16
|
+
* 正文按宿主 house style 转义闭合标签,防伪造帧边界。
|
|
17
|
+
*/
|
|
18
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
19
|
+
import type { MemoryConfig } from '../config.js';
|
|
20
|
+
import type { ObsoleteNoticeStore } from '../store/obsolete-notices.js';
|
|
21
|
+
import type { MemoryLogger } from '../types.js';
|
|
22
|
+
export interface ObsoleteNoticeController {
|
|
23
|
+
/** 生成本轮播报文本(null = 无待播报);导出供单元测试。会**出队**。 */
|
|
24
|
+
buildInjection(sessionKey: string): string | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function registerObsoleteNotice(ctx: Context, cfg: MemoryConfig, notices: ObsoleteNoticeStore, logger: MemoryLogger): ObsoleteNoticeController;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
2
|
+
const SYSTEM_REMINDER_OPEN = '<system-reminder>';
|
|
3
|
+
const SYSTEM_REMINDER_CLOSE = '</system-reminder>';
|
|
4
|
+
const INTRO = '以下几条记忆刚被用户判定为**已过时**,并已从记忆库退场(后续不会再被召回)。' +
|
|
5
|
+
'请勿依据它们作答;若你此前的回答引用过,请主动更正并说明依据已失效。' +
|
|
6
|
+
'这是系统注入的元信息,不是用户发言——无需回应本节内容。';
|
|
7
|
+
/** 单轮最多播报条数(超出折叠;防一次批量误操作灌满上下文)。 */
|
|
8
|
+
const MAX_PER_TURN = 8;
|
|
9
|
+
function escapeFrameBody(body) {
|
|
10
|
+
return body.replaceAll(SYSTEM_REMINDER_CLOSE, '<\\/system-reminder>');
|
|
11
|
+
}
|
|
12
|
+
/** 注入键 = sessionKey(`agent.session?.id ?? agent.id`),与活召回集记账同源。 */
|
|
13
|
+
function sessionKeyOf(agent) {
|
|
14
|
+
const a = agent;
|
|
15
|
+
const sid = a?.session?.id;
|
|
16
|
+
if (typeof sid === 'string' && sid)
|
|
17
|
+
return sid;
|
|
18
|
+
return typeof a?.id === 'string' ? a.id : '';
|
|
19
|
+
}
|
|
20
|
+
export function registerObsoleteNotice(ctx, cfg, notices, logger) {
|
|
21
|
+
const on = () => cfg.recallFeedback?.enabled === true && cfg.recallFeedback?.announce !== false;
|
|
22
|
+
const buildInjection = (sessionKey) => {
|
|
23
|
+
if (!on())
|
|
24
|
+
return null;
|
|
25
|
+
if (!sessionKey)
|
|
26
|
+
return null;
|
|
27
|
+
const drained = notices.drain(sessionKey);
|
|
28
|
+
if (drained.length === 0)
|
|
29
|
+
return null;
|
|
30
|
+
const lines = drained.slice(0, MAX_PER_TURN).map((n) => `- ${n.title || n.id}`);
|
|
31
|
+
if (drained.length > MAX_PER_TURN) {
|
|
32
|
+
lines.push(`… 另有 ${drained.length - MAX_PER_TURN} 条同批声明过时的记忆未列出`);
|
|
33
|
+
}
|
|
34
|
+
return [
|
|
35
|
+
SYSTEM_REMINDER_OPEN,
|
|
36
|
+
INTRO,
|
|
37
|
+
escapeFrameBody(lines.join('\n')),
|
|
38
|
+
SYSTEM_REMINDER_CLOSE,
|
|
39
|
+
].join('\n');
|
|
40
|
+
};
|
|
41
|
+
ctx.on('agent/pre-step', async (payload, next) => {
|
|
42
|
+
const decision = await next();
|
|
43
|
+
if (decision.kind === 'reject' || (payload.signal && payload.signal.aborted))
|
|
44
|
+
return decision;
|
|
45
|
+
if (!on())
|
|
46
|
+
return decision;
|
|
47
|
+
try {
|
|
48
|
+
const text = buildInjection(sessionKeyOf(payload.agent));
|
|
49
|
+
if (!text)
|
|
50
|
+
return decision;
|
|
51
|
+
const injection = createUserMessage({
|
|
52
|
+
content: [{ type: 'text', text }],
|
|
53
|
+
source: { kind: 'plugin:memory', form: 'recall' },
|
|
54
|
+
});
|
|
55
|
+
logger.info('[memory] 已向本会话播报记忆过时声明(一次性)');
|
|
56
|
+
return { kind: 'enter', messages: [injection, ...decision.messages] };
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
logger.warn(`[memory] 过时播报注入失败(跳过本轮): ${err instanceof Error ? err.message : String(err)}`);
|
|
60
|
+
return decision;
|
|
61
|
+
}
|
|
62
|
+
}, { prepend: true });
|
|
63
|
+
return { buildInjection };
|
|
64
|
+
}
|
package/dist/hooks/recall.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type { LiveSettingsHandle } from '../settings.js';
|
|
|
21
21
|
import type { L1Store } from '../store/l1.js';
|
|
22
22
|
import type { PersonaStore } from '../store/persona.js';
|
|
23
23
|
import type { SessionRecalledStore } from '../store/session-recalled.js';
|
|
24
|
+
import type { RecallFeedbackFace } from '../projection/recall.js';
|
|
24
25
|
import type { SceneStore } from '../store/scenes.js';
|
|
25
26
|
import type { SessionModeStore } from '../store/session-modes.js';
|
|
26
27
|
import type { RecallUsageStore } from '../store/recall-usage.js';
|
|
@@ -40,6 +41,43 @@ export declare function isOwnRecallSource(src: {
|
|
|
40
41
|
plugin?: unknown;
|
|
41
42
|
form?: unknown;
|
|
42
43
|
} | undefined | null): boolean;
|
|
44
|
+
/** 会话事件里本插件可能关心的那几个字段(只读形状,不依赖宿主类型导出)。 */
|
|
45
|
+
export interface InjectionEventLike {
|
|
46
|
+
/** 存储兜底路的事件形状宽松,`type` 也可能缺;缺即不是注入(下面的不等判定已覆盖 undefined)。 */
|
|
47
|
+
type?: string;
|
|
48
|
+
/** 存储兜底路(`sessionPersistence.loadStored`)的事件**不带 seq**,所以它是可选的。 */
|
|
49
|
+
seq?: number;
|
|
50
|
+
data?: {
|
|
51
|
+
id?: unknown;
|
|
52
|
+
source?: Record<string, unknown>;
|
|
53
|
+
content?: ReadonlyArray<{
|
|
54
|
+
type?: string;
|
|
55
|
+
text?: string;
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** 一条本插件召回注入的事实。 */
|
|
60
|
+
export interface OwnInjectionFacts {
|
|
61
|
+
/** 注入消息 id——稳定锚点。**不能用 seq 做键**:压缩的 `surfaceOp:{op:'replace'}` 会重排它。 */
|
|
62
|
+
id: string;
|
|
63
|
+
/**
|
|
64
|
+
* 该消息此刻在日志里的 seq(拿去比 `surface.nodes`)。
|
|
65
|
+
*
|
|
66
|
+
* `undefined` = 这条来源根本没有 seq。**没有 seq 就不能声称“它可见”**——
|
|
67
|
+
* 宁可算“不在可见集里”,也不拿一个臆测的默认值去判(哨兵值会把误标藏起来)。
|
|
68
|
+
*/
|
|
69
|
+
seq: number | undefined;
|
|
70
|
+
/** 注入正文字符数(token 估算用)。 */
|
|
71
|
+
chars: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 认出一条 `user/message` 事件是不是本插件的召回注入,并取出它的消息 id / seq / 正文字数。
|
|
75
|
+
*
|
|
76
|
+
* 这是**唯一的识别口径**:召份额估算(live 与存储两条路)与「正文已被摘要取代」对账都读它。
|
|
77
|
+
* 两处各写一套判定迟早漂移,而漂移的后果是静默的(份额归零或误标取代)。
|
|
78
|
+
* 署名判定走 `isOwnRecallSource`,同时吃 v4 `plugin:memory` 与 v3 旧行形状。
|
|
79
|
+
*/
|
|
80
|
+
export declare function ownInjectionFromEvent(ev: InjectionEventLike): OwnInjectionFacts | undefined;
|
|
43
81
|
export declare function buildRecallQuery(messages: Array<{
|
|
44
82
|
content: unknown;
|
|
45
83
|
}>, tailMessages?: number, maxChars?: number): string;
|
|
@@ -92,6 +130,7 @@ export declare function registerRecall(ctx: Context, cfg: MemoryConfig, stores:
|
|
|
92
130
|
scenes: Record<'chat' | 'work', SceneStore>;
|
|
93
131
|
persona: Record<'chat' | 'work', PersonaStore>;
|
|
94
132
|
recalled?: SessionRecalledStore;
|
|
133
|
+
feedback?: RecallFeedbackFace;
|
|
95
134
|
}, logger: MemoryLogger, live: LiveSettingsHandle, modes: SessionModeStore, dataDir: string,
|
|
96
135
|
/** 召回使用统计(可选;未装配 = 零成本旁路):进 topN(注入)才算 used。 */
|
|
97
136
|
usage?: RecallUsageStore): RecallHooks;
|