dsh-lite-memory 0.18.19 → 0.18.26
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/TRIGGERS.md +1 -1
- package/client.js +136 -28
- package/docs/CONVENTIONS.md +10 -1
- package/docs/RELEASE-0.18.16.md +6 -0
- package/docs/RELEASE-0.18.17.md +6 -0
- package/docs/RELEASE-0.18.18.md +7 -0
- package/docs/RELEASE-0.18.19.md +7 -0
- package/docs/RELEASE-0.18.20.md +4 -0
- package/docs/RELEASE-0.18.21.md +4 -0
- package/docs/RELEASE-0.18.22.md +5 -0
- package/docs/RELEASE-0.18.23.md +5 -0
- package/docs/RELEASE-0.18.24.md +3 -0
- package/docs/RELEASE-0.18.25.md +5 -0
- package/docs/RELEASE-0.18.26.md +5 -0
- package/index.js +168 -35
- package/learning.js +3 -2
- package/package.json +1 -1
- package/state.js +11 -6
- package/store.js +84 -13
- package/test/client.test.mjs +25 -1
- package/test/contract.test.mjs +130 -1
- package/test/control.test.mjs +22 -0
- package/test/evolution-read.test.mjs +2 -0
- package/test/learning.test.mjs +3 -2
- package/test/lexical.test.mjs +22 -0
- package/test/recall.test.mjs +53 -0
- package/test/state.test.mjs +8 -5
- package/test/store.test.mjs +50 -3
- package/view.js +129 -16
package/TRIGGERS.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
- 写:用户明确要求;用户自己的话已经是长期规则(「以后都这样 / 别再 X」)时直接存,不先问;或用户对某条建议点了「保留」。
|
|
17
17
|
- 建议:一条经验能跨会话复用、又只存在于用户嘴里(偏好、惯例、踩过的坑),且同一项目 48 小时内没建议过。只出按钮,不写「要不要记」这种问句——问句要用户打字,按钮不要。
|
|
18
18
|
- 不写:`1+1=2` 这种常识、一次性任务参数、能从仓库直接读到的正文;凭据只记位置与调用方式。
|
|
19
|
-
- 自动命中:用户这句话与某条记忆的标题、旧标题、标签、触发词或摘要真有重合、且分数过线时,这一步的运行时上下文里多一段「已有记忆可能相关」——最多三条标题加 id,普通卡片与实验规则共用 3 条 / 1200 字节预算,正文仍要 `memory_read`。只有当前任务条件判为 `ok` 的条目能进;条件没确认或明确不符的既不进自动注入,也不进检索结果,检索只在全都被挡时说明「有相关的项目记忆,但适用条件还没确认 / 当前任务明确不适用」。没重合为空字符串,整条索引不进上下文,也不改 hits / lastUsed
|
|
19
|
+
- 自动命中:用户这句话与某条记忆的标题、旧标题、标签、触发词或摘要真有重合、且分数过线时,这一步的运行时上下文里多一段「已有记忆可能相关」——最多三条标题加 id,普通卡片与实验规则共用 3 条 / 1200 字节预算,正文仍要 `memory_read`。只有当前任务条件判为 `ok` 的条目能进;条件没确认或明确不符的既不进自动注入,也不进检索结果,检索只在全都被挡时说明「有相关的项目记忆,但适用条件还没确认 / 当前任务明确不适用」。没重合为空字符串,整条索引不进上下文,也不改 hits / lastUsed,更不落盘。用户这句话没提到动作、一张卡都没命中时,再用上一轮助手的话和工具调用里的实词补一次有界查询(预算不变);补不到就保留第一次的「为什么没提醒」。同一句话只算一次;换句话重新算。
|
|
20
20
|
|
|
21
21
|
## 触发后长什么样
|
|
22
22
|
|
package/client.js
CHANGED
|
@@ -94,7 +94,7 @@ window.__ModuleLoader__.load({
|
|
|
94
94
|
scope: meta.scope === 'global' ? 'global' : 'project',
|
|
95
95
|
revision: Number.isInteger(meta.revision) ? meta.revision : 1,
|
|
96
96
|
status: typeof meta.status === 'string' ? meta.status : 'ok',
|
|
97
|
-
origin: meta.origin === 'project_evolution'
|
|
97
|
+
origin: meta.origin === 'project_evolution' || meta.origin === 'model_drafted' ? meta.origin : 'user_explicit',
|
|
98
98
|
kind,
|
|
99
99
|
exposure: kind === 'memory-save' || kind === 'memory-forget' ? 'update' : 'reference',
|
|
100
100
|
/** 条目类型:读正文看 recordKind,检索看 ref 自己的 kind;工具名不是类型。 */
|
|
@@ -568,12 +568,32 @@ window.__ModuleLoader__.load({
|
|
|
568
568
|
};
|
|
569
569
|
|
|
570
570
|
const versionLine = (shown) => {
|
|
571
|
-
|
|
571
|
+
/** 项目名只当分层后缀:光写目录名会被当成记忆标题。 */
|
|
572
|
+
const scope = shown.scope === 'global' ? '全局'
|
|
573
|
+
: shown.scopeLabel ? `项目 · ${shown.scopeLabel}` : '项目';
|
|
572
574
|
const archived = shown.status === 'archived' ? ' · 已归档' : '';
|
|
573
575
|
const processing = processingStatusOf(shown.processingStatus ? { processingStatus: shown.processingStatus } : shown);
|
|
574
576
|
return `${scope}${archived}${processing ? ` · ${PROCESSING_LABEL[processing]}` : ''}`;
|
|
575
577
|
};
|
|
576
578
|
|
|
579
|
+
/** 展开的正文紧跟被点的那条;顺序由列表决定,不挂在一段末尾。 */
|
|
580
|
+
const refSectionRowsOf = (items, shown, renderRow, renderDetail) => {
|
|
581
|
+
const rows = [];
|
|
582
|
+
for (const item of items) {
|
|
583
|
+
rows.push(renderRow(item));
|
|
584
|
+
if (shown && shown.ref && shown.key === item.key && shown.ref.revision === item.revision) rows.push(renderDetail(item));
|
|
585
|
+
}
|
|
586
|
+
return rows;
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
/** 正文抬头永远是记忆标题;项目/全局只做下面的来源行。 */
|
|
590
|
+
const refDetailTitleOf = (shown) => {
|
|
591
|
+
const title = shown && shown.ref && typeof shown.ref.title === 'string' ? shown.ref.title.trim() : '';
|
|
592
|
+
if (title) return title;
|
|
593
|
+
const fallback = shown && typeof shown.title === 'string' ? shown.title.trim() : '';
|
|
594
|
+
return fallback || '记忆';
|
|
595
|
+
};
|
|
596
|
+
|
|
577
597
|
/**
|
|
578
598
|
* 引用三层的色调:中性 / 强调 / 成功。
|
|
579
599
|
* 三档色阶:中性 / 强调 / 成功;正文用主文本色。
|
|
@@ -599,7 +619,8 @@ window.__ModuleLoader__.load({
|
|
|
599
619
|
};
|
|
600
620
|
const refMetaOf = (item) => {
|
|
601
621
|
/** 会话里只标层与来源:类型留给面板和工具,不占引用行的宽度。 */
|
|
602
|
-
|
|
622
|
+
/** 来源只标注信任差别:自进化规则与模型整理稿都不会因此升级层级。 */
|
|
623
|
+
const layer = `${item.scope === 'global' ? '[全局]' : '[项目]'}${item.origin === 'project_evolution' ? '[自进化]' : item.origin === 'model_drafted' ? '[模型]' : ''}`;
|
|
603
624
|
const parts = [];
|
|
604
625
|
const ago = agoLabel(item.updated);
|
|
605
626
|
if (ago) parts.push(ago);
|
|
@@ -624,8 +645,9 @@ window.__ModuleLoader__.load({
|
|
|
624
645
|
onMouseEnter: () => setHover(true),
|
|
625
646
|
onMouseLeave: () => setHover(false),
|
|
626
647
|
onClick: props.onShow, // ui-copy-ignore
|
|
627
|
-
'aria-
|
|
628
|
-
|
|
648
|
+
'aria-expanded': Boolean(props.active),
|
|
649
|
+
'aria-label': `${props.active ? '收起' : '查看'}记忆 ${item.title || '记忆'}${reason ? `,${reason}` : ''}`,
|
|
650
|
+
title: props.active ? `收起 ${item.title || '记忆'}` : (reason ? `${item.title || '记忆'},${reason}` : (item.title || '记忆')),
|
|
629
651
|
style: { // ui-copy-ignore
|
|
630
652
|
display: 'flex', alignItems: 'flex-start', gap: '7px', width: '100%', minWidth: 0, textAlign: 'left',
|
|
631
653
|
border: 'none', borderRadius: '8px', padding: '3px 7px', cursor: 'pointer', color: 'inherit',
|
|
@@ -674,8 +696,11 @@ window.__ModuleLoader__.load({
|
|
|
674
696
|
setOpen(false);
|
|
675
697
|
};
|
|
676
698
|
const show = (item) => {
|
|
677
|
-
|
|
678
|
-
|
|
699
|
+
/** 再点同一条只收起正文,列表留着,方便接着看别的。 */
|
|
700
|
+
if (shown && shown.key === item.key && shown.ref && shown.ref.revision === item.revision) {
|
|
701
|
+
generation.current += 1;
|
|
702
|
+
setShown(null);
|
|
703
|
+
setNote('');
|
|
679
704
|
return;
|
|
680
705
|
}
|
|
681
706
|
const request = generation.current + 1;
|
|
@@ -710,20 +735,13 @@ window.__ModuleLoader__.load({
|
|
|
710
735
|
});
|
|
711
736
|
});
|
|
712
737
|
};
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
item, tone, active: Boolean(shown && shown.key === item.key),
|
|
716
|
-
onShow: () => show(item),
|
|
717
|
-
})) : [];
|
|
718
|
-
const detail = shown && shown.ref && items.some((item) => item.key === shown.key && item.revision === shown.ref.revision)
|
|
719
|
-
? h('div', { style: { ...REF_BODY, borderLeft: `2px solid ${tone}` } },
|
|
738
|
+
const detail = shown && shown.ref
|
|
739
|
+
? h('div', { style: { ...REF_BODY, margin: '2px 0 4px 7px', padding: '2px 2px 4px 10px', borderLeft: `2px solid ${tone}` } },
|
|
720
740
|
shown.loading ? h('span', { style: REF_META }, '读取中…') : null,
|
|
721
|
-
shown.gone ? h('span', { style: REF_META }, '
|
|
741
|
+
shown.gone ? h('span', { style: REF_META }, '这条记忆已经不在这里了') : null,
|
|
722
742
|
shown.invalid ? h('span', { style: REF_META }, '读不到正文,点名字重试') : null,
|
|
723
743
|
!shown.loading && !shown.gone && !shown.invalid ? h(React.Fragment, null,
|
|
724
|
-
|
|
725
|
-
? h('div', { style: { fontSize: '12px', lineHeight: '1.55', fontWeight: 600 } }, shown.title)
|
|
726
|
-
: null,
|
|
744
|
+
h('div', { style: { fontSize: '13px', lineHeight: '1.5', fontWeight: 600, wordBreak: 'break-word' } }, refDetailTitleOf(shown)),
|
|
727
745
|
h('div', { style: REF_META }, versionLine(shown)),
|
|
728
746
|
shown.when ? h('div', { style: REF_META }, shown.when) : null,
|
|
729
747
|
shown.body
|
|
@@ -734,6 +752,13 @@ window.__ModuleLoader__.load({
|
|
|
734
752
|
note ? h('span', { style: REF_META }, note) : null,
|
|
735
753
|
)
|
|
736
754
|
: null;
|
|
755
|
+
const rows = open
|
|
756
|
+
? refSectionRowsOf(items, shown, (item) => h(RefRow, {
|
|
757
|
+
key: `${item.key}|${item.revision}`, // ui-copy-ignore
|
|
758
|
+
item, tone, active: Boolean(shown && shown.ref && shown.key === item.key && shown.ref.revision === item.revision),
|
|
759
|
+
onShow: () => show(item),
|
|
760
|
+
}), () => detail)
|
|
761
|
+
: [];
|
|
737
762
|
return h('div', { style: DOCK, 'data-memory-section': props.label },
|
|
738
763
|
h('button', {
|
|
739
764
|
type: 'button',
|
|
@@ -752,7 +777,6 @@ window.__ModuleLoader__.load({
|
|
|
752
777
|
h(RefChevron, { open }),
|
|
753
778
|
),
|
|
754
779
|
...rows,
|
|
755
|
-
detail,
|
|
756
780
|
);
|
|
757
781
|
};
|
|
758
782
|
|
|
@@ -870,10 +894,22 @@ window.__ModuleLoader__.load({
|
|
|
870
894
|
explainBus.add(listener);
|
|
871
895
|
return () => explainBus.delete(listener);
|
|
872
896
|
};
|
|
897
|
+
/** 召回回执只说“多久以前”,不跟列表的“更新”混用。 */
|
|
898
|
+
const shortAgo = (value) => {
|
|
899
|
+
const at = Date.parse(value);
|
|
900
|
+
if (!Number.isFinite(at)) return '';
|
|
901
|
+
const minutes = Math.floor((Date.now() - at) / 60_000);
|
|
902
|
+
if (minutes < 1) return '刚刚';
|
|
903
|
+
if (minutes < 60) return `${minutes} 分钟前`;
|
|
904
|
+
const hours = Math.floor(minutes / 60);
|
|
905
|
+
if (hours < 24) return `${hours} 小时前`;
|
|
906
|
+
const days = Math.floor(hours / 24);
|
|
907
|
+
return days < 30 ? `${days} 天前` : '';
|
|
908
|
+
};
|
|
873
909
|
/**
|
|
874
910
|
* 面板只说判断结果,不说内部原因码;条数来自上一次自动召回的回执。
|
|
875
911
|
*/
|
|
876
|
-
const
|
|
912
|
+
const recallTextOf = (receipt) => {
|
|
877
913
|
if (!receipt || typeof receipt !== 'object') return '';
|
|
878
914
|
const cards = Number.isInteger(receipt.cards) ? receipt.cards : 0;
|
|
879
915
|
const matched = Number.isInteger(receipt.matched) ? receipt.matched : 0;
|
|
@@ -882,12 +918,20 @@ window.__ModuleLoader__.load({
|
|
|
882
918
|
case 'hit': return cards > 0 ? `上次自动提醒 ${cards} 条` : '';
|
|
883
919
|
case 'budget': return `上次没提醒,${related}本轮自动提醒已用满`;
|
|
884
920
|
case 'needs_context': return `上次没提醒,${related}适用条件未确认`;
|
|
921
|
+
case 'conflict': return `上次没提醒,${related}存在冲突的结论`;
|
|
885
922
|
case 'no_overlap': return '上次没提醒,没有匹配的记忆';
|
|
923
|
+
case 'expired': return '上次没提醒,相关内容已经过期';
|
|
886
924
|
case 'memory_off': return '上次没提醒,记忆已关闭';
|
|
887
925
|
case 'tools_only': return '上次没提醒,只在你问时检索';
|
|
888
926
|
default: return '';
|
|
889
927
|
}
|
|
890
928
|
};
|
|
929
|
+
/** 回执可能是上一轮甚至更早的:带上时间,别让旧提醒看起来像刚发生。 */
|
|
930
|
+
const recallLineOf = (receipt) => {
|
|
931
|
+
const text = recallTextOf(receipt);
|
|
932
|
+
const when = receipt && typeof receipt === 'object' ? shortAgo(receipt.at) : '';
|
|
933
|
+
return text && when ? `${text} · ${when}` : text;
|
|
934
|
+
};
|
|
891
935
|
/** 功能关闭时视图统一回列表,不保留一个画不出来的页签。 */
|
|
892
936
|
const graphViewOf = (enabled, view) => (enabled === true && view === 'graph' ? 'graph' : 'list');
|
|
893
937
|
const viewModeOf = (display, view) => {
|
|
@@ -2194,7 +2238,7 @@ window.__ModuleLoader__.load({
|
|
|
2194
2238
|
post('/lite-memory/body', { sessionId, id: selectedMemory, scopeKey: data.selected.key }).then((result) => {
|
|
2195
2239
|
if (!live) return;
|
|
2196
2240
|
if (result.ok && result.body && result.body.ok) {
|
|
2197
|
-
if (result.body.status === 'gone') setDetailNote('
|
|
2241
|
+
if (result.body.status === 'gone') setDetailNote('这条记忆已经不在这里了');
|
|
2198
2242
|
else setDetail(result.body);
|
|
2199
2243
|
} else setDetailNote(failure(result, '读不到正文'));
|
|
2200
2244
|
});
|
|
@@ -2210,6 +2254,8 @@ window.__ModuleLoader__.load({
|
|
|
2210
2254
|
? selected.control
|
|
2211
2255
|
: (current && selected && selected.key === current.key && current.control ? current.control : { enabled: true });
|
|
2212
2256
|
const memoryOn = control.enabled !== false;
|
|
2257
|
+
const memoryMode = !memoryOn ? 'off'
|
|
2258
|
+
: (control.effectiveMode === 'tool_only' || control.mode === 'tool_only' ? 'tool_only' : 'adaptive');
|
|
2213
2259
|
const evolution = selected && selected.evolution
|
|
2214
2260
|
? selected.evolution
|
|
2215
2261
|
: (current && selected && selected.key === current.key ? current.evolution : null);
|
|
@@ -2374,6 +2420,7 @@ window.__ModuleLoader__.load({
|
|
|
2374
2420
|
|| !fresh.ok || !fresh.body || !fresh.body.ok || fresh.body.status === 'gone') return;
|
|
2375
2421
|
const revision = Number.isInteger(fresh.body.revision) ? fresh.body.revision : 0;
|
|
2376
2422
|
setDetail(fresh.body);
|
|
2423
|
+
setEditNote('已载入最新内容,再保存会覆盖它');
|
|
2377
2424
|
setData((prev) => {
|
|
2378
2425
|
if (!prev || !prev.selected || !Array.isArray(prev.selected.records)) return prev;
|
|
2379
2426
|
const records = prev.selected.records.map((row) => (row.id === record.id ? { ...row, revision: revision || row.revision } : row));
|
|
@@ -2385,14 +2432,45 @@ window.__ModuleLoader__.load({
|
|
|
2385
2432
|
});
|
|
2386
2433
|
};
|
|
2387
2434
|
|
|
2388
|
-
|
|
2389
|
-
|
|
2435
|
+
/** 适用范围待确认是模型留下的不确定;人点一次就把它变成明确结论,别再让模型猜。 */
|
|
2436
|
+
const confirmMemoryScope = (record) => {
|
|
2437
|
+
if (busy || !canManage || !record || !record.id) return;
|
|
2438
|
+
const text = currentBodyOf(detail) || '';
|
|
2439
|
+
if (!text) return;
|
|
2390
2440
|
setBusy(true);
|
|
2391
|
-
post('/lite-memory/
|
|
2441
|
+
post('/lite-memory/entry', {
|
|
2442
|
+
sessionId, scopeKey: selectedKey, id: record.id,
|
|
2443
|
+
title: record.title || detail.title || '', body: text,
|
|
2444
|
+
expectedRevision: record.revision, applicability: 'all_in_scope', conditions: [],
|
|
2445
|
+
}).then((result) => {
|
|
2446
|
+
if (!alive.current || (selectedKey && scopeRef.current !== selectedKey)) return;
|
|
2447
|
+
setBusy(false);
|
|
2448
|
+
if (!succeeded(result)) {
|
|
2449
|
+
setNote(failure(result, '适用范围没有改动'));
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
const revision = result.body && Number.isInteger(result.body.revision) ? result.body.revision : record.revision;
|
|
2453
|
+
setDetail((prev) => (prev ? { ...prev, applicability: 'all_in_scope', conditions: [], revision } : prev));
|
|
2454
|
+
setData((prev) => {
|
|
2455
|
+
if (!prev || !prev.selected || !Array.isArray(prev.selected.records)) return prev;
|
|
2456
|
+
const records = prev.selected.records.map((row) => (row.id === record.id
|
|
2457
|
+
? { ...row, applicability: 'all_in_scope', conditions: [], revision } : row));
|
|
2458
|
+
return { ...prev, selected: { ...prev.selected, records } };
|
|
2459
|
+
});
|
|
2460
|
+
});
|
|
2461
|
+
};
|
|
2462
|
+
|
|
2463
|
+
const setControlMode = (mode) => {
|
|
2464
|
+
if (busy || !canToggle || mode === memoryMode) return;
|
|
2465
|
+
setBusy(true);
|
|
2466
|
+
const body = mode === 'off'
|
|
2467
|
+
? { sessionId, enabled: false, operationId: newOperationId() }
|
|
2468
|
+
: { sessionId, enabled: true, mode, operationId: newOperationId() };
|
|
2469
|
+
post('/lite-memory/control', body).then((result) => {
|
|
2392
2470
|
if (!alive.current || (selectedKey && scopeRef.current !== selectedKey)) return;
|
|
2393
2471
|
setBusy(false);
|
|
2394
2472
|
if (succeeded(result)) load(selectedKey);
|
|
2395
|
-
else setNote(failure(result, '
|
|
2473
|
+
else setNote(failure(result, '触发方式没有改动'));
|
|
2396
2474
|
});
|
|
2397
2475
|
};
|
|
2398
2476
|
|
|
@@ -2460,6 +2538,25 @@ window.__ModuleLoader__.load({
|
|
|
2460
2538
|
} : undefined,
|
|
2461
2539
|
);
|
|
2462
2540
|
|
|
2541
|
+
const modeChoices = () => h('div', {
|
|
2542
|
+
style: { display: 'flex', alignItems: 'center', gap: '8px', minWidth: 0 },
|
|
2543
|
+
},
|
|
2544
|
+
h('span', { style: { fontSize: '12px', lineHeight: '20px', fontWeight: 600, whiteSpace: 'nowrap' } }, '记忆'),
|
|
2545
|
+
h('div', {
|
|
2546
|
+
role: 'radiogroup', 'aria-label': '记忆触发方式',
|
|
2547
|
+
style: { display: 'flex', gap: '2px', padding: '2px', borderRadius: '8px', background: 'var(--dsw-alias-bg-layer-2)', border: '1px solid var(--dsw-alias-border-l1)' },
|
|
2548
|
+
},
|
|
2549
|
+
[['adaptive', '自动提醒'], ['tool_only', '仅搜索'], ['off', '关闭']].map(([key, text]) => h('button', {
|
|
2550
|
+
key, type: 'button', role: 'radio', 'aria-checked': memoryMode === key, disabled: busy || !canToggle,
|
|
2551
|
+
onClick: canToggle && !busy ? () => setControlMode(key) : undefined,
|
|
2552
|
+
style: {
|
|
2553
|
+
padding: '2px 7px', minHeight: '22px', border: 'none', borderRadius: '6px',
|
|
2554
|
+
background: memoryMode === key ? 'var(--dsw-alias-bg-layer-1)' : 'transparent',
|
|
2555
|
+
color: 'inherit', fontSize: '11px', lineHeight: '18px', fontWeight: memoryMode === key ? 600 : 400,
|
|
2556
|
+
cursor: canToggle && !busy ? 'pointer' : 'default', whiteSpace: 'nowrap',
|
|
2557
|
+
opacity: memoryMode === key || canToggle ? 1 : 0.55,
|
|
2558
|
+
},
|
|
2559
|
+
}, text))));
|
|
2463
2560
|
const switchItem = (label, hint, on, disabled, onClick) => h('div', {
|
|
2464
2561
|
style: { display: 'flex', flexDirection: 'column', gap: '1px', minWidth: 0, opacity: disabled && !on ? 0.55 : 1 },
|
|
2465
2562
|
},
|
|
@@ -2476,7 +2573,7 @@ window.__ModuleLoader__.load({
|
|
|
2476
2573
|
globalView
|
|
2477
2574
|
? h('div', { style: { ...PANEL_MUTED, lineHeight: '20px' } }, '全局记忆只读')
|
|
2478
2575
|
: h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(190px, 1fr))', gap: '4px 18px' } },
|
|
2479
|
-
|
|
2576
|
+
modeChoices(),
|
|
2480
2577
|
switchItem('自进化', evolutionOn ? '实验功能,会增加 token 消耗' : '从反馈里整理经验', evolutionOn, busy || !evolution || !canToggle || !memoryOn, toggleEvolution), // ui-copy-ignore 用户指定文案
|
|
2481
2578
|
showSettings ? displayToggle('启用图谱', 'graph') : null,
|
|
2482
2579
|
showSettings ? displayToggle('启用记忆宫殿', 'palace') : null,
|
|
@@ -2549,12 +2646,23 @@ window.__ModuleLoader__.load({
|
|
|
2549
2646
|
h('div', { style: { flex: 1, minWidth: 0, fontSize: '13px', lineHeight: '1.45', fontWeight: 600, wordBreak: 'break-word' } }, detail.title),
|
|
2550
2647
|
h(Action, { onClick: () => setSelectedMemory('') }, '关闭'),
|
|
2551
2648
|
),
|
|
2552
|
-
h('div', { style: PANEL_MUTED }, `${KIND_LABEL[detailRecord && detailRecord.kind] || '记忆'} · ${detail.scopeLabel || ''}${detailProcessing ? ` · ${PROCESSING_LABEL[detailProcessing]}` : ''}`),
|
|
2649
|
+
h('div', { style: PANEL_MUTED }, `${KIND_LABEL[detailRecord && detailRecord.kind] || '记忆'} · ${detail.scopeLabel || ''}${detailRecord && detailRecord.origin === 'model_drafted' ? ' · [模型]' : ''}${detailProcessing ? ` · ${PROCESSING_LABEL[detailProcessing]}` : ''}`),
|
|
2553
2650
|
Array.isArray(detailRecord && detailRecord.tags) && detailRecord.tags.length
|
|
2554
2651
|
? h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: '4px' } },
|
|
2555
2652
|
detailRecord.tags.slice(0, 6).map((tag) => h('span', { key: tag, style: { ...PANEL_MUTED, padding: '0 6px', border: '1px solid var(--dsw-alias-border-l1)', borderRadius: '999px' } }, `#${tag}`))) : null,
|
|
2556
2653
|
memoryEditor,
|
|
2557
2654
|
editingMemory ? null : h('div', { style: { fontSize: '12px', lineHeight: '1.65', whiteSpace: 'pre-wrap', wordBreak: 'break-word' } }, currentBodyOf(detail) || '没有正文'),
|
|
2655
|
+
!editingMemory && detailRecord && detailRecord.applicability === 'unresolved'
|
|
2656
|
+
? h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' } },
|
|
2657
|
+
h('span', { style: PANEL_MUTED }, '适用范围待确认'),
|
|
2658
|
+
canManage
|
|
2659
|
+
? h(Action, {
|
|
2660
|
+
disabled: busy || !currentBodyOf(detail),
|
|
2661
|
+
'aria-label': '标为所有任务都适用',
|
|
2662
|
+
onClick: () => confirmMemoryScope(detailRecord),
|
|
2663
|
+
}, '都适用')
|
|
2664
|
+
: null)
|
|
2665
|
+
: null,
|
|
2558
2666
|
editingMemory ? null : h(DetailExtras, { payload: detail }),
|
|
2559
2667
|
!editingMemory && detailRecord && canManage
|
|
2560
2668
|
? h('div', { style: { display: 'flex', alignItems: 'center', gap: '12px', flexWrap: 'wrap', paddingTop: '2px' } },
|
|
@@ -2850,7 +2958,7 @@ window.__ModuleLoader__.load({
|
|
|
2850
2958
|
|
|
2851
2959
|
// 测试缝:Node 里验引用投影,浏览器里没有任何副作用。
|
|
2852
2960
|
if (typeof globalThis.__LITE_MEMORY_TEST__ === 'function') {
|
|
2853
|
-
globalThis.__LITE_MEMORY_TEST__({ metaOf, citationOf, citationsOfMeta, citationsOfTurn, dedupe, undoRefOf, graphOf, ruleGraphOf, processingStatusOf, currentBodyOf, historyDiffsOf, MemoryPanel, MemoryGraph, MemoryPalace, TurnMemory, RefSection, displayKeyOf, readDisplaySettings, writeDisplaySettings, graphViewOf, viewModeOf, filterRecords, projectionOf, failureStateOf, evidenceTextOf, evidenceMetaOf, palaceRoomsOf, palaceRoomPageOf, palacePageWindow, palaceToggleOf, palaceMotionOf, palaceRoomIdOf, refMetaOf, explainKeyOf, explainModeOf, setExplainMode, recallLineOf, agoLabel, checkupOf, titleCountsOf, rowKeyOf, rowKeepFlags, Row });
|
|
2961
|
+
globalThis.__LITE_MEMORY_TEST__({ metaOf, citationOf, citationsOfMeta, citationsOfTurn, dedupe, undoRefOf, graphOf, ruleGraphOf, processingStatusOf, currentBodyOf, historyDiffsOf, MemoryPanel, MemoryGraph, MemoryPalace, TurnMemory, RefSection, displayKeyOf, readDisplaySettings, writeDisplaySettings, graphViewOf, viewModeOf, filterRecords, projectionOf, failureStateOf, evidenceTextOf, evidenceMetaOf, palaceRoomsOf, palaceRoomPageOf, palacePageWindow, palaceToggleOf, palaceMotionOf, palaceRoomIdOf, refMetaOf, explainKeyOf, explainModeOf, setExplainMode, recallLineOf, agoLabel, checkupOf, refSectionRowsOf, refDetailTitleOf, versionLine, titleCountsOf, rowKeyOf, rowKeepFlags, Row });
|
|
2854
2962
|
}
|
|
2855
2963
|
|
|
2856
2964
|
return {
|
package/docs/CONVENTIONS.md
CHANGED
|
@@ -36,9 +36,12 @@
|
|
|
36
36
|
- 归档条目、实验来源(普通自动召回不碰,实验规则单独走 `experimentFor`)、`legacy_unreviewed` 全部排除。
|
|
37
37
|
- 同一 `topicKey` 槽位只让胜出的记录进;槽位内冲突或条件未知的整组不进自动召回,不猜哪条对。
|
|
38
38
|
- 默认值 `__tasks.defaults` 会参与任务上下文,但只是「范围提示」,不是把某条记忆设成必用。
|
|
39
|
+
- 「继续 / 然后呢」这类续说句没有检索词时,沿用同会话上一轮原话;别把新的短问题接到旧查询上。
|
|
39
40
|
|
|
40
41
|
**预算**:普通卡片与实验规则**共用**一份账本:最多 3 张卡片、最终拼接文本最多 1200 字节。字节量的是 provider 最终看到的完整文本(含标题行、ID、换行、两段之间的空行),不是单张正文。超预算时跳过这张,不截半句、不把标题留在模型侧。
|
|
41
42
|
|
|
43
|
+
**实际 Token**:自动提示最多 3 条、1200 字节,不随全库条数线性增长;总量仍取决于命中频率、模型调用步数、主动读取正文,以及实验性自进化的整理次数。别把「每轮有上限」说成「不增加消耗」。
|
|
44
|
+
|
|
42
45
|
**失败**:
|
|
43
46
|
|
|
44
47
|
- 准备超过 `RECALL_DEADLINE_MS`、被取消、投影期间开关/代次变了:整批回退,预算退还,下一轮重新来。绝不发布半旧卡片。
|
|
@@ -77,7 +80,13 @@
|
|
|
77
80
|
- `topicKey` 是槽位;同一槽位只允许一条当前胜出记录,冲突时先让人选,不自动合并。
|
|
78
81
|
- `kind` 只影响展示分组,不参与检索和可用性判断。
|
|
79
82
|
|
|
80
|
-
**范围路由**:模型给的 `scope`
|
|
83
|
+
**范围路由**:模型给的 `scope` 是提案,不是命令。
|
|
84
|
+
|
|
85
|
+
- 显式 `scope="global"` 表示「用户在这一次对话里要求存全局」,只有用户确实这么说才传;模型自己觉得通用不是理由。显式全局不再因为缺少 `evidenceRef` 被降级。
|
|
86
|
+
- 仍然会拦住的两类:设备事实没有条件(`machine` / `targetOs` / `runtime` / `shell` 至少一个),以及正文或依据里出现本机路径、回环地址、端口的「通用流程」——后者换台机器/换个项目就不成立,留在项目层。
|
|
87
|
+
- `memoryClass="project_fact"` 不因写了 `global` 就变全局:项目事实换个项目本来就不成立。
|
|
88
|
+
- 按类别推导(没有显式 `scope`)保持更严:`portable_procedure` 没有 `evidenceRef` 就落项目层。
|
|
89
|
+
- 每次降级都要能解释:只在降级时返回 `scopeNote` 一行,说明为什么没进全局、怎么改才进得去;正常落全局或落项目不带这行。
|
|
81
90
|
|
|
82
91
|
**限额与腾位**:每层活跃条目软上限 200、硬上限 1000;单条正文 4096 字节;单层总字节 1 MiB。到硬上限时把最久没被读的活跃条目移进归档,并在结果里写明「已归档「标题」腾位」,不拒绝新记忆;单条正文超限只报错、不截断。
|
|
83
92
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.18.18:触发方式按项目选
|
|
2
|
+
|
|
3
|
+
- 面板把「启用记忆」换成一排选择:自动提醒 / 仅搜索 / 关闭。选「仅搜索」后不再主动弹卡片,工具检索照常;选「关闭」等同原来的总开关,写入与学习一起停。
|
|
4
|
+
- 触发方式存在项目自己的状态里,不改插件配置,也不影响其他项目;没有选过的项目仍跟随插件默认。改方式会换代次,关闭前排队的召回不会补交。
|
|
5
|
+
- 记忆开关和触发方式在同一把锁里落盘,不会只改一半;关闭时切换方式不会顺手把记忆打开。
|
|
6
|
+
- 编辑碰到别人先改过时,面板载入最新正文并说明「已载入最新内容,再保存会覆盖它」,不再只报一句通用失败。
|
|
7
|
+
- 引用点开时条目已归档或移走,改说「这条记忆已经不在这里了」,不再误称已删除。
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# 0.18.19
|
|
2
|
+
|
|
3
|
+
- 保存失败回执走统一形状:带 `error` 字段、过输出 schema,界面显示具体原因,不再出现 `saved "undefined"`。
|
|
4
|
+
- `memory_read` 的关联清单带上标题,关联卡片不再显示 undefined。
|
|
5
|
+
- 作用域推断收窄:「所有口播」「一律」不再被当成全项目授权;无可信任务条件时留待确认。
|
|
6
|
+
- 实验规则参与本轮相关度排序,和普通记忆共用 3 张卡片 / 1200 字节预算;条件已满足的规则仍按绑定注入。
|
|
7
|
+
- 契约测试按已注册 `output.schema` 校验成功与失败回执。
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# 0.18.26
|
|
2
|
+
|
|
3
|
+
- 只在本机成立、但跨项目复用的命令/流程(例如本机 npm 包装命令):声明 machine 或 runtime 条件后可以进全局层,换台机器条件不成立就不会拿出来用;仍然写死本机路径/端口又没声明环境的,继续留在项目层。
|
|
4
|
+
- 显式存全局的降级说明补了一句怎么改:补机器条件后可以存全局,不用把规则改写成一个不存在的通用说法。
|
|
5
|
+
- CI 稳定性:召回投影的耗时上限可以在测试里放宽(CI 冷启动偶尔超过 150ms,逻辑断言不该被「这轮先不注入」判失败);旧库正文比对按行尾归一(Windows 检出把 LF 转成 CRLF)。Windows 与 Ubuntu 六格矩阵全绿。
|