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/hooks/recall.js
CHANGED
|
@@ -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, sortByUsageScenario, WING_ANCHORS } from '../domain-gate.js';
|
|
14
|
+
import { domainGate, formatWeights, hardFilterByWingLock, sortByDomainWeight, sortByHumanMark, 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;
|
|
@@ -40,6 +40,30 @@ const RECALL_QUERY_MAX_CHARS = 2_000;
|
|
|
40
40
|
export function isOwnRecallSource(src) {
|
|
41
41
|
return !!src && src.form === 'recall' && (src.kind === 'plugin:memory' || (src.kind === 'plugin' && src.plugin === 'memory'));
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* 认出一条 `user/message` 事件是不是本插件的召回注入,并取出它的消息 id / seq / 正文字数。
|
|
45
|
+
*
|
|
46
|
+
* 这是**唯一的识别口径**:召份额估算(live 与存储两条路)与「正文已被摘要取代」对账都读它。
|
|
47
|
+
* 两处各写一套判定迟早漂移,而漂移的后果是静默的(份额归零或误标取代)。
|
|
48
|
+
* 署名判定走 `isOwnRecallSource`,同时吃 v4 `plugin:memory` 与 v3 旧行形状。
|
|
49
|
+
*/
|
|
50
|
+
export function ownInjectionFromEvent(ev) {
|
|
51
|
+
if (ev.type !== 'user/message')
|
|
52
|
+
return undefined;
|
|
53
|
+
const msg = ev.data;
|
|
54
|
+
if (msg === undefined || !isOwnRecallSource(msg.source))
|
|
55
|
+
return undefined;
|
|
56
|
+
let chars = 0;
|
|
57
|
+
for (const b of msg.content ?? []) {
|
|
58
|
+
if (b?.type === 'text' && typeof b.text === 'string')
|
|
59
|
+
chars += b.text.length;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
id: typeof msg.id === 'string' ? msg.id : '',
|
|
63
|
+
seq: typeof ev.seq === 'number' ? ev.seq : undefined,
|
|
64
|
+
chars,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
43
67
|
export function buildRecallQuery(messages, tailMessages = RECALL_QUERY_TAIL_MESSAGES, maxChars = RECALL_QUERY_MAX_CHARS) {
|
|
44
68
|
const tail = messages.slice(-tailMessages);
|
|
45
69
|
let text = tail.map((m) => blocksToText(m.content)).join(' ').trim();
|
|
@@ -215,9 +239,13 @@ usage) {
|
|
|
215
239
|
ctx.on('session/event', (session, event) => {
|
|
216
240
|
if (event.type !== 'compaction/end')
|
|
217
241
|
return;
|
|
218
|
-
|
|
219
|
-
|
|
242
|
+
const sid = String(session.id ?? session);
|
|
243
|
+
if (postCompaction.onCompactionEnd(sid)) {
|
|
244
|
+
logger.info(`[memory] compaction/end:下一轮将做一次增强召回(session=${sid})`);
|
|
220
245
|
}
|
|
246
|
+
// Phase 0B:压缩完成的**权威**信号。取代标记不再只靠 agent/created 的粗标,而是从
|
|
247
|
+
// surface.nodes 逐条对账;即使那条事件根本没发,这里也能把状态算对(并可撤销误标)。
|
|
248
|
+
void reconcileSupersededFromSession(sid);
|
|
221
249
|
});
|
|
222
250
|
// 上下文压缩/清空 → 已注入内容从模型上下文丢失,重置该会话的去重压制
|
|
223
251
|
// (resume/startup 不重置:历史仍在,已注入的记忆模型还持有)。
|
|
@@ -225,7 +253,9 @@ usage) {
|
|
|
225
253
|
ctx.on('agent/created', async (payload) => {
|
|
226
254
|
if (payload.source === 'compact' || payload.source === 'clear') {
|
|
227
255
|
dedupe.reset(payload.agent.id);
|
|
228
|
-
|
|
256
|
+
// 压缩与清空**不同对待**:摘要替换不得抹掉本会话的召回历史(尤其已判过时的裁决凭证),
|
|
257
|
+
// 只有用户主动清空才整表清。去重与占用账本仍按原样重置(那两者关心“能不能重新注入”)。
|
|
258
|
+
stores.recalled?.reset(String(payload.agent.session?.id ?? payload.agent.id), payload.source === 'compact' ? 'compact' : 'clear');
|
|
229
259
|
const led = ledgerFor(payload.agent.id);
|
|
230
260
|
resetForCompaction(led);
|
|
231
261
|
occupancyStore.save(payload.agent.id, led); // stock 归零 ⇒ 流水条目删除
|
|
@@ -388,6 +418,33 @@ usage) {
|
|
|
388
418
|
logger.info(`[memory] 使用场景细排生效:agent=${payload.agent.id}`);
|
|
389
419
|
}
|
|
390
420
|
}
|
|
421
|
+
// ── 人工打分参与排序(默认开):4/5 上浮、0/1/2 下沉。纯重排、**score 不改写**、
|
|
422
|
+
// 同档内保持进入顺序(不推翻上面的围栏/场景细排)。这就是 0/1/2 的常规出口:
|
|
423
|
+
// 不相关就排到注不上,而不是从记忆里消失。未装配反馈面 / 全未打分 → 逐字同序。
|
|
424
|
+
if (cfg.recallFeedback?.enabled === true
|
|
425
|
+
&& cfg.recallFeedback.affectRank !== false
|
|
426
|
+
&& stores.feedback !== undefined
|
|
427
|
+
&& scoped.length > 1) {
|
|
428
|
+
const fb = stores.feedback;
|
|
429
|
+
const before = scoped.map((h) => h.id).join(',');
|
|
430
|
+
scoped = sortByHumanMark(scoped, (id) => fb.lastOf(id));
|
|
431
|
+
if (scoped.map((h) => h.id).join(',') !== before) {
|
|
432
|
+
logger.info(`[memory] 人工打分排序生效(agent=${payload.agent.id})`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// ── 可选硬门 excludeZero(**默认关**):最近一笔是 0 → 本轮不注入(不退场,
|
|
436
|
+
// 再点 1..5 即回来)。开着它=“误点一次就完全不再浮现”,所以默认只靠上面的沉底。
|
|
437
|
+
if (cfg.recallFeedback?.enabled === true
|
|
438
|
+
&& cfg.recallFeedback.excludeZero === true
|
|
439
|
+
&& stores.feedback !== undefined
|
|
440
|
+
&& scoped.length > 0) {
|
|
441
|
+
const fb = stores.feedback;
|
|
442
|
+
const kept = scoped.filter((h) => fb.lastOf(h.id) !== 0);
|
|
443
|
+
if (kept.length !== scoped.length) {
|
|
444
|
+
logger.info(`[memory] 相关度 0 分剔除 ${scoped.length - kept.length} 条(agent=${payload.agent.id})`);
|
|
445
|
+
scoped = kept;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
391
448
|
// 召回去重:同会话已注入过的记录不再重复注入(模型上下文已持有,省 token)。
|
|
392
449
|
// 纯过滤——剩几条注几条,全量压制(0 条新鲜命中)是正确状态而非未命中。
|
|
393
450
|
// §E:压缩后增强轮跳过压制(压缩把已注入内容逐出了模型上下文)。
|
|
@@ -460,7 +517,7 @@ usage) {
|
|
|
460
517
|
usage?.recordAttempt(scoped.map((h) => h.id), fresh.slice(0, lines.length).map((h) => h.id));
|
|
461
518
|
// G-live:按 **sessionKey**(=agent.session?.id ?? agent.id,与 pill 的 sessionId / 投影 header.id 同源,
|
|
462
519
|
// 非 ownerId)记入本会话“活召回集”,供侧栏卡读取;压缩时同键 reset。
|
|
463
|
-
stores.recalled?.add(sessionKey, fresh.slice(0, lines.length).map((h) => h.id));
|
|
520
|
+
stores.recalled?.add(sessionKey, fresh.slice(0, lines.length).map((h) => h.id), injection.id);
|
|
464
521
|
// 入账在成功构造注入消息之后、返回 enter 之前——任何前置抛错路径账目零扰动
|
|
465
522
|
const led = ledgerFor(payload.agent.id);
|
|
466
523
|
recordRecallInjection(led, text.length);
|
|
@@ -500,8 +557,46 @@ usage) {
|
|
|
500
557
|
return '';
|
|
501
558
|
return body ? `${body}\n\n${MEMORY_TOOLS_GUIDE}` : MEMORY_TOOLS_GUIDE;
|
|
502
559
|
};
|
|
503
|
-
/**
|
|
504
|
-
|
|
560
|
+
/**
|
|
561
|
+
* Phase 0B:从宿主官方会话服务对账「正文已被摘要取代」。
|
|
562
|
+
*
|
|
563
|
+
* 为什么不再靠“收到一条压缩事件”:事件会漏(压缩不重建 agent 的宿主线、含 CCI compat 摘要
|
|
564
|
+
* 路径,根本不发 `agent/created{source:'compact'}`),漏一次就永久错且无法补算。这里每轮从
|
|
565
|
+
* `surface.nodes` 重算,所以既能标上也能撤销(误标与重新注入都会自愈)。
|
|
566
|
+
*
|
|
567
|
+
* @returns true = 会话在 live store 且已对账;false = 不在(调用方回退到粗标记)。
|
|
568
|
+
*/
|
|
569
|
+
const reconcileSupersededFromSession = async (sessionId) => {
|
|
570
|
+
const recalled = stores.recalled;
|
|
571
|
+
if (recalled === undefined || sessionId.length === 0)
|
|
572
|
+
return false;
|
|
573
|
+
try {
|
|
574
|
+
// 可选服务一律走 ctx.get() 的宽容路径(属性访问对未 inject 的服务会抛)
|
|
575
|
+
const sessions = ctx.get?.('sessions');
|
|
576
|
+
const session = typeof sessions?.get === 'function' ? sessions.get(sessionId) : undefined;
|
|
577
|
+
if (session === undefined)
|
|
578
|
+
return false;
|
|
579
|
+
const visibleSeqs = new Set(session.surface.nodes);
|
|
580
|
+
const visibleIds = new Set();
|
|
581
|
+
const knownIds = new Set();
|
|
582
|
+
for (const ev of session.events) {
|
|
583
|
+
const own = ownInjectionFromEvent(ev);
|
|
584
|
+
if (own === undefined || own.id.length === 0)
|
|
585
|
+
continue;
|
|
586
|
+
knownIds.add(own.id);
|
|
587
|
+
// 只有“确实拿到了 seq 且 seq 在 surface.nodes 里”才算可见;
|
|
588
|
+
// seq 缺失时宁可不撤销(保持原状),也不拿臆测值去改动人的可见性判定。
|
|
589
|
+
if (own.seq !== undefined && visibleSeqs.has(own.seq))
|
|
590
|
+
visibleIds.add(own.id);
|
|
591
|
+
}
|
|
592
|
+
return recalled.reconcileSuperseded(sessionId, visibleIds, knownIds);
|
|
593
|
+
}
|
|
594
|
+
catch (err) {
|
|
595
|
+
// 对账是旁路:失败只能“本轮不动”,绝不能弄挂召回或面板。
|
|
596
|
+
logger.warn(`[memory] 取代对账失败(本轮跳过): ${errDetail(err)}`);
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
505
600
|
async function estimateRecallFromStorage(sessionId) {
|
|
506
601
|
if (storedEstimateCache.has(sessionId))
|
|
507
602
|
return storedEstimateCache.get(sessionId) ?? null;
|
|
@@ -513,20 +608,13 @@ usage) {
|
|
|
513
608
|
if (stored?.events) {
|
|
514
609
|
tokens = 0;
|
|
515
610
|
for (const ev of stored.events) {
|
|
611
|
+
// 存储路没有 surface,只能沿用“遇到压缩就归零”的启发式(粗近似,不伪装成精确判定)。
|
|
516
612
|
if (typeof ev.type === 'string' && ev.type.startsWith('compaction'))
|
|
517
613
|
tokens = 0;
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const src = ev.data?.source;
|
|
521
|
-
if (!isOwnRecall(src))
|
|
614
|
+
const own = ownInjectionFromEvent(ev);
|
|
615
|
+
if (own === undefined || own.chars === 0)
|
|
522
616
|
continue;
|
|
523
|
-
|
|
524
|
-
for (const b of ev.data?.content ?? []) {
|
|
525
|
-
if (b?.type === 'text' && typeof b.text === 'string')
|
|
526
|
-
chars += b.text.length;
|
|
527
|
-
}
|
|
528
|
-
if (chars > 0)
|
|
529
|
-
tokens += estimateInjectedMessageTokens(chars);
|
|
617
|
+
tokens += estimateInjectedMessageTokens(own.chars);
|
|
530
618
|
}
|
|
531
619
|
}
|
|
532
620
|
}
|
|
@@ -551,19 +639,11 @@ usage) {
|
|
|
551
639
|
const visible = new Set(session.surface.nodes);
|
|
552
640
|
let total = 0;
|
|
553
641
|
for (const ev of session.events) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const src = msg?.source;
|
|
558
|
-
if (!isOwnRecall(src) || !msg)
|
|
642
|
+
const own = ownInjectionFromEvent(ev);
|
|
643
|
+
// 无 seq = 无法证明它可见,就不能算进份额(与原行为一致:原来它必须 visible.has(seq))。
|
|
644
|
+
if (own === undefined || own.chars === 0 || own.seq === undefined || !visible.has(own.seq))
|
|
559
645
|
continue;
|
|
560
|
-
|
|
561
|
-
for (const b of msg.content ?? []) {
|
|
562
|
-
if (b?.type === 'text' && typeof b.text === 'string')
|
|
563
|
-
chars += b.text.length;
|
|
564
|
-
}
|
|
565
|
-
if (chars > 0)
|
|
566
|
-
total += estimateInjectedMessageTokens(chars);
|
|
646
|
+
total += estimateInjectedMessageTokens(own.chars);
|
|
567
647
|
}
|
|
568
648
|
return total;
|
|
569
649
|
}
|