@modusensus/dsh-mneme 0.7.17 → 0.7.20
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/README.en.md +6 -3
- package/README.md +12 -17
- package/cordis.patch.yml +5 -0
- package/lib/api.js +23 -5
- package/lib/client.js +280 -42
- package/lib/config.js +22 -1
- package/lib/dream/sleep.js +14 -1
- package/lib/heat.js +136 -0
- package/lib/service.js +24 -7
- package/lib/settings.js +1 -0
- package/lib/store.js +16 -2
- package/package.json +8 -2
- package/src/api.js +23 -5
- package/src/config.js +22 -1
- package/src/dream/sleep.js +14 -1
- package/src/heat.js +136 -0
- package/src/service.js +24 -7
- package/src/settings.js +1 -0
- package/src/store.js +16 -2
- package/test/api.test.js +88 -5
- package/test/client.test.js +134 -0
- package/test/heat.test.js +148 -0
- package/test/recall-layer.test.js +15 -4
- package/test/sleep-heat.test.js +125 -0
- package/test/sleep.test.js +12 -4
- package/test/updated-at-semantics.test.js +113 -0
package/lib/client.js
CHANGED
|
@@ -55,7 +55,8 @@ window.__ModuleLoader__.load({
|
|
|
55
55
|
copy: [["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }], ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" }]],
|
|
56
56
|
check: [["path", { d: "M20 6 9 17l-5-5" }]],
|
|
57
57
|
inbox: [["polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12" }], ["path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" }]],
|
|
58
|
-
activity: [["path", { d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" }]]
|
|
58
|
+
activity: [["path", { d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" }]],
|
|
59
|
+
flame: [["path", { d: "M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z" }]]
|
|
59
60
|
};
|
|
60
61
|
const Icon = ({ name, size = 16, className }) => {
|
|
61
62
|
const parts = ICON_PATHS[name];
|
|
@@ -92,6 +93,18 @@ window.__ModuleLoader__.load({
|
|
|
92
93
|
}))));
|
|
93
94
|
};
|
|
94
95
|
|
|
96
|
+
// 热度徽章(阶段二):flame 图标 + 整数百分比,三档配色(热/温/冷)。
|
|
97
|
+
// /list 仅在 heatEnabled=true 时下发 heat 字段——缺省即不渲染,开关关闭
|
|
98
|
+
// 时徽章全站自动消失,前端无需感知开关状态。
|
|
99
|
+
const HeatBadge = ({ value, size = 12 }) => {
|
|
100
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
101
|
+
const pct = Math.round(Math.min(1, Math.max(0, value)) * 100);
|
|
102
|
+
const tier = pct >= 66 ? "hot" : pct >= 33 ? "warm" : "cold";
|
|
103
|
+
return h("span", { className: `mneme-heat mneme-heat--${tier}`, title: `${pct}%` },
|
|
104
|
+
h(Icon, { name: "flame", size, className: "mneme-heatico" }),
|
|
105
|
+
h("span", { className: "mneme-heatpct" }, `${pct}%`));
|
|
106
|
+
};
|
|
107
|
+
|
|
95
108
|
// Unified API fetcher: attaches the optional apiToken (set in the settings
|
|
96
109
|
// view, persisted in localStorage) as a Bearer header. When no token has
|
|
97
110
|
// been configured the header is omitted and the API stays open (default).
|
|
@@ -105,6 +118,10 @@ window.__ModuleLoader__.load({
|
|
|
105
118
|
return fetch(path, { ...opts, headers });
|
|
106
119
|
}
|
|
107
120
|
|
|
121
|
+
// ⚠️ 'betterSidebar' 绝不能进模块级 inject(issue #88 实锤):loader 对
|
|
122
|
+
// inject 声明的服务是硬等待——未安装 better-sidebar 的环境里整个 entry
|
|
123
|
+
// 永远 pending("1 entry did not activate" → Failed to load plugins)。
|
|
124
|
+
// 软依赖靠内层动态子插件探测(见 apply 内 better-sidebar 挂载块)。
|
|
108
125
|
const inject = ["slots", "locale"];
|
|
109
126
|
|
|
110
127
|
const NS = "memory";
|
|
@@ -174,6 +191,15 @@ window.__ModuleLoader__.load({
|
|
|
174
191
|
"memory.explorer.updated": "更新",
|
|
175
192
|
"memory.explorer.tags": "标签",
|
|
176
193
|
"memory.explorer.importance": "重要性",
|
|
194
|
+
"memory.explorer.heat": "热度",
|
|
195
|
+
"memory.explorer.sourceFilter": "来源",
|
|
196
|
+
"memory.explorer.sort": "排序",
|
|
197
|
+
"memory.explorer.heatSort": "热度优先",
|
|
198
|
+
"memory.explorer.heatSortHint": "对已加载条目按热度降序(页内排序,非全局序;时间树保持时间序)",
|
|
199
|
+
"memory.explorer.filterDeposited": "沉淀",
|
|
200
|
+
"memory.explorer.filterDepositedHint": "只看 autoDream 巩固/更新过的记忆",
|
|
201
|
+
"memory.explorer.filterArchived": "已归档",
|
|
202
|
+
"memory.explorer.filterArchivedHint": "只看已归档的记忆(默认列表不含它们)",
|
|
177
203
|
"memory.explorer.topK": "返回数量",
|
|
178
204
|
"memory.explorer.topKOption": "返回 {n} 条",
|
|
179
205
|
"memory.card.open": "在记忆库中查看全文",
|
|
@@ -329,6 +355,8 @@ window.__ModuleLoader__.load({
|
|
|
329
355
|
"memory.explorer.detail.save": "保存",
|
|
330
356
|
"memory.explorer.detail.cancel": "取消",
|
|
331
357
|
"memory.explorer.detail.archive": "归档",
|
|
358
|
+
"memory.explorer.detail.restore": "恢复",
|
|
359
|
+
"memory.explorer.detail.restored": "已恢复到主列表",
|
|
332
360
|
"memory.explorer.detail.archived": "已归档",
|
|
333
361
|
"memory.explorer.detail.archiveFailed": "操作失败",
|
|
334
362
|
"memory.explorer.detail.saved": "已保存,镜像同步更新",
|
|
@@ -355,6 +383,11 @@ window.__ModuleLoader__.load({
|
|
|
355
383
|
"memory.status.consolidated": "沉淀的记忆",
|
|
356
384
|
"memory.status.consolidatedEmpty": "autoDream / autoSummarize 沉淀的记忆会出现在这里",
|
|
357
385
|
"memory.status.archivedMemories": "已归档的记忆",
|
|
386
|
+
"memory.status.heatDistribution": "热度分布",
|
|
387
|
+
"memory.status.heatHint": "热门(≥66%){hot} · 温热(33–66%){warm} · 冷却(<33%){cold},样本为最近 {sample} 条",
|
|
388
|
+
"memory.status.viewAll": "查看全部",
|
|
389
|
+
"memory.status.depositedCount": "沉淀的记忆({n})",
|
|
390
|
+
"memory.status.archivedCount": "已归档的记忆({n})",
|
|
358
391
|
"memory.status.archivedEmpty": "没有归档的记忆",
|
|
359
392
|
"memory.status.restore": "恢复",
|
|
360
393
|
"memory.settings.extapi.reveal": "显示",
|
|
@@ -438,6 +471,15 @@ window.__ModuleLoader__.load({
|
|
|
438
471
|
"memory.explorer.updated": "Updated",
|
|
439
472
|
"memory.explorer.tags": "Tags",
|
|
440
473
|
"memory.explorer.importance": "Importance",
|
|
474
|
+
"memory.explorer.heat": "Heat",
|
|
475
|
+
"memory.explorer.sourceFilter": "Source",
|
|
476
|
+
"memory.explorer.sort": "Sort",
|
|
477
|
+
"memory.explorer.heatSort": "By heat",
|
|
478
|
+
"memory.explorer.heatSortHint": "Sort loaded items by heat, in-page (not global; the month tree keeps chronological order)",
|
|
479
|
+
"memory.explorer.filterDeposited": "Deposited",
|
|
480
|
+
"memory.explorer.filterDepositedHint": "Only memories autoDream consolidated or updated",
|
|
481
|
+
"memory.explorer.filterArchived": "Archived",
|
|
482
|
+
"memory.explorer.filterArchivedHint": "Only archived memories (excluded from the default list)",
|
|
441
483
|
"memory.explorer.topK": "Results limit",
|
|
442
484
|
"memory.explorer.topKOption": "Return {n}",
|
|
443
485
|
"memory.card.open": "Open full text in the memory library",
|
|
@@ -593,6 +635,8 @@ window.__ModuleLoader__.load({
|
|
|
593
635
|
"memory.explorer.detail.save": "Save",
|
|
594
636
|
"memory.explorer.detail.cancel": "Cancel",
|
|
595
637
|
"memory.explorer.detail.archive": "Archive",
|
|
638
|
+
"memory.explorer.detail.restore": "Restore",
|
|
639
|
+
"memory.explorer.detail.restored": "Restored to the main list",
|
|
596
640
|
"memory.explorer.detail.archived": "Archived",
|
|
597
641
|
"memory.explorer.detail.archiveFailed": "Action failed",
|
|
598
642
|
"memory.explorer.detail.saved": "Saved; mirrors re-rendered",
|
|
@@ -619,6 +663,11 @@ window.__ModuleLoader__.load({
|
|
|
619
663
|
"memory.status.consolidated": "Deposited memories",
|
|
620
664
|
"memory.status.consolidatedEmpty": "Memories deposited by autoDream / autoSummarize appear here",
|
|
621
665
|
"memory.status.archivedMemories": "Archived memories",
|
|
666
|
+
"memory.status.heatDistribution": "Heat distribution",
|
|
667
|
+
"memory.status.heatHint": "hot (≥66%) {hot} · warm (33–66%) {warm} · cooled (<33%) {cold}, sampled from the latest {sample}",
|
|
668
|
+
"memory.status.viewAll": "View all",
|
|
669
|
+
"memory.status.depositedCount": "Deposited memories ({n})",
|
|
670
|
+
"memory.status.archivedCount": "Archived memories ({n})",
|
|
622
671
|
"memory.status.archivedEmpty": "Nothing archived",
|
|
623
672
|
"memory.status.restore": "Restore",
|
|
624
673
|
"memory.settings.extapi.reveal": "Reveal",
|
|
@@ -686,6 +735,8 @@ window.__ModuleLoader__.load({
|
|
|
686
735
|
// text-turns-brand-blue active states — no boxed panels).
|
|
687
736
|
const CSS_TAG = "@modusensus/dsh-mneme/drawer.css";
|
|
688
737
|
const css = [
|
|
738
|
+
// --- explorer shell: container queries let the toolbar adapt to the
|
|
739
|
+
// better-sidebar workbench pane (~500px) as well as the 1240px sheet ---
|
|
689
740
|
// --- sidebar foot trigger (wide row / collapsed rail icon) ---
|
|
690
741
|
".mneme-trigger{box-sizing:border-box;cursor:pointer;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:12px;flex:none;align-items:center;gap:8px;margin:4px -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}",
|
|
691
742
|
".mneme-trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
@@ -705,7 +756,11 @@ window.__ModuleLoader__.load({
|
|
|
705
756
|
".mneme-entitychip{flex:none;height:26px;padding:0 10px;border-radius:8px;border:none;background:none;color:var(--dsw-alias-state-business-primary);cursor:pointer;font-family:inherit;font-size:12px;line-height:16px;display:inline-flex;align-items:center}",
|
|
706
757
|
".mneme-entitychip:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent)}",
|
|
707
758
|
// --- main-area memory library page ---
|
|
708
|
-
".mneme-x{flex:1;min-height:0;height:100%;width:100%;box-sizing:border-box;display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-1);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2)}",
|
|
759
|
+
".mneme-x{flex:1;min-height:0;height:100%;width:100%;box-sizing:border-box;display:flex;flex-direction:column;background:var(--dsw-alias-bg-layer-1);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);container-type:inline-size}",
|
|
760
|
+
// 窄容器(better-sidebar 工作台 ~500px)下中央 vtabs 会与右缘悬浮的
|
|
761
|
+
// 卡片/时间线切换器重叠:容器查询收窄时隐藏切换器,视图模式沿用
|
|
762
|
+
// localStorage 记忆,宽容器(sheet)不受影响。
|
|
763
|
+
"@container (max-width: 640px){.mneme-xtools .mneme-seg{display:none}}",
|
|
709
764
|
".mneme-xbar{position:relative;flex:none;display:flex;align-items:center;justify-content:center;gap:6px;border-bottom:1px solid var(--dsw-alias-border-l2);padding:0 16px;min-height:52px}",
|
|
710
765
|
".mneme-filterbar{flex:none;display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}",
|
|
711
766
|
".mneme-vtabs{display:flex;align-items:stretch;height:46px}",
|
|
@@ -845,6 +900,12 @@ window.__ModuleLoader__.load({
|
|
|
845
900
|
// wrapper display:contents 隐身,按钮成为侧边栏弹性布局的直接子元素;
|
|
846
901
|
// 盒模型/间距/收起态 rail 几何全部继承宿主,我们只覆盖配色为次级观感。
|
|
847
902
|
".mneme-stars{display:inline-flex;align-items:center;gap:2px}",
|
|
903
|
+
// 热度徽章三档:热(橙)/温(次级文字)/冷(弱化文字),冷档藏百分比只留图标。
|
|
904
|
+
".mneme-heat{display:inline-flex;align-items:center;gap:2px;font-size:11px;line-height:14px;font-variant-numeric:tabular-nums}",
|
|
905
|
+
".mneme-heat--hot{color:var(--dsw-alias-state-warning,#d97706)}",
|
|
906
|
+
".mneme-heat--warm{color:var(--dsw-alias-label-secondary)}",
|
|
907
|
+
".mneme-heat--cold{color:var(--dsw-alias-label-tertiary)}",
|
|
908
|
+
".mneme-heat--cold .mneme-heatpct{display:none}",
|
|
848
909
|
// width:100% 与「新会话」同宽(同一弹性父容器);文案对齐交给复制来的
|
|
849
910
|
// 原生类,不再用 flex:1/text-align:left 覆盖成左对齐。
|
|
850
911
|
".mneme-topentry{display:contents}",
|
|
@@ -938,7 +999,7 @@ window.__ModuleLoader__.load({
|
|
|
938
999
|
".mneme-statusgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;max-width:1000px;margin:0 auto}",
|
|
939
1000
|
".mneme-statuscard{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:14px}",
|
|
940
1001
|
// --- 状态页工作台:让用户看见插件在干活(动态/沉淀/归档) ---
|
|
941
|
-
".mneme-wbhead{margin:28px auto 10px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);max-width:1000px}",
|
|
1002
|
+
".mneme-wbhead{margin:28px auto 10px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);max-width:1000px;display:flex;align-items:baseline;justify-content:space-between;gap:8px}",
|
|
942
1003
|
".mneme-wblist{max-width:1000px;display:flex;flex-direction:column;gap:6px}",
|
|
943
1004
|
".mneme-wbrow{display:flex;gap:10px;align-items:flex-start;padding:10px 14px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px}",
|
|
944
1005
|
".mneme-wbdot{flex:none;width:8px;height:8px;border-radius:50%;margin-top:6px;background:var(--dsw-alias-state-success,#2a9d6a)}",
|
|
@@ -1023,7 +1084,16 @@ window.__ModuleLoader__.load({
|
|
|
1023
1084
|
|
|
1024
1085
|
function nodeRadius(n) {
|
|
1025
1086
|
// mention_count → area-ish growth, clamped so hubs stay legible.
|
|
1026
|
-
|
|
1087
|
+
const base = 7 + Math.min(20, Math.max(1, n.mention_count ?? 1)) * 0.55;
|
|
1088
|
+
// heat projection (0-1 or null): hot entities render slightly larger.
|
|
1089
|
+
if (n.heat == null) return base;
|
|
1090
|
+
return base + (Math.max(0, Math.min(1, n.heat)) - 0.5) * 4;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
function nodeOpacity(n) {
|
|
1094
|
+
// heat projection: hot entities are bright (opacity 1), cold fade to 0.4.
|
|
1095
|
+
if (n.heat == null) return 1;
|
|
1096
|
+
return 0.4 + 0.6 * Math.max(0, Math.min(1, n.heat));
|
|
1027
1097
|
}
|
|
1028
1098
|
|
|
1029
1099
|
// Deterministic golden-angle spiral: no two nodes start overlapping, and
|
|
@@ -1472,7 +1542,7 @@ window.__ModuleLoader__.load({
|
|
|
1472
1542
|
onClick: () => onNodeClick(n),
|
|
1473
1543
|
"data-node": n.name
|
|
1474
1544
|
},
|
|
1475
|
-
h("circle", { r: nodeRadius(n), fill: typeColor(n.type) }),
|
|
1545
|
+
h("circle", { r: nodeRadius(n), fill: typeColor(n.type), fillOpacity: nodeOpacity(n) }),
|
|
1476
1546
|
h("text", { className: "mneme-glabel", y: nodeRadius(n) + 13 }, n.name)
|
|
1477
1547
|
))
|
|
1478
1548
|
)
|
|
@@ -2379,11 +2449,14 @@ window.__ModuleLoader__.load({
|
|
|
2379
2449
|
return [];
|
|
2380
2450
|
}
|
|
2381
2451
|
|
|
2382
|
-
function WorkbenchSection({ t }) {
|
|
2452
|
+
function WorkbenchSection({ t, onBrowse }) {
|
|
2383
2453
|
const [feed, setFeed] = useState(null); // audit rows(已过滤巩固/总结)
|
|
2384
|
-
const [deposited, setDeposited] = useState(null); //
|
|
2385
|
-
const [
|
|
2454
|
+
const [deposited, setDeposited] = useState(null); // 沉淀记忆首页(服务端 deposited 视图)
|
|
2455
|
+
const [depositedTotal, setDepositedTotal] = useState(0);
|
|
2456
|
+
const [archived, setArchived] = useState(null); // 归档记忆首页
|
|
2457
|
+
const [archivedTotal, setArchivedTotal] = useState(0);
|
|
2386
2458
|
const [restoring, setRestoring] = useState("");
|
|
2459
|
+
const [wbReload, setWbReload] = useState(0);
|
|
2387
2460
|
|
|
2388
2461
|
const load = useCallback(() => {
|
|
2389
2462
|
let cancelled = false;
|
|
@@ -2391,30 +2464,31 @@ window.__ModuleLoader__.load({
|
|
|
2391
2464
|
.then((r) => (r.ok ? r.json() : { items: [] }))
|
|
2392
2465
|
.then((d) => {
|
|
2393
2466
|
if (cancelled) return;
|
|
2394
|
-
|
|
2395
|
-
r.trigger_source === "autoDream" || r.trigger_source === "autoSummarize");
|
|
2396
|
-
setFeed(rows);
|
|
2397
|
-
const ids = [];
|
|
2398
|
-
for (const row of rows) {
|
|
2399
|
-
for (const id of parseRelatedIds(row.related_memory_ids)) {
|
|
2400
|
-
if (!ids.includes(id)) ids.push(id);
|
|
2401
|
-
}
|
|
2402
|
-
}
|
|
2403
|
-
return apiFetch("/api/dsh-mneme/list?limit=300&order=chrono")
|
|
2404
|
-
.then((r2) => (r2.ok ? r2.json() : { items: [] }))
|
|
2405
|
-
.then((d2) => {
|
|
2406
|
-
if (cancelled) return;
|
|
2407
|
-
const byId = new Map((d2.items || []).map((m) => [m.id, m]));
|
|
2408
|
-
setDeposited(ids.map((id) => byId.get(id)).filter(Boolean).slice(0, 8));
|
|
2409
|
-
});
|
|
2467
|
+
setFeed((d.items || []).filter((r) =>
|
|
2468
|
+
r.trigger_source === "autoDream" || r.trigger_source === "autoSummarize"));
|
|
2410
2469
|
})
|
|
2411
2470
|
.catch(() => { if (!cancelled) setFeed([]); });
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2471
|
+
// 仪表盘数据源:沉淀/归档各取一小页 + 服务端 total(/list 的 total
|
|
2472
|
+
// 与行同过滤)。长列表不再进状态页——「查看全部」跳记忆库的筛选
|
|
2473
|
+
// 视图,查询收敛到有搜索/分页的地方。
|
|
2474
|
+
apiFetch("/api/dsh-mneme/list?deposited=only&limit=8&order=chrono")
|
|
2475
|
+
.then((r) => (r.ok ? r.json() : { items: [], total: 0 }))
|
|
2476
|
+
.then((d) => {
|
|
2477
|
+
if (cancelled) return;
|
|
2478
|
+
setDeposited(d.items || []);
|
|
2479
|
+
setDepositedTotal(d.total || 0);
|
|
2480
|
+
})
|
|
2481
|
+
.catch(() => { if (!cancelled) setDeposited([]); });
|
|
2482
|
+
apiFetch("/api/dsh-mneme/list?archived=only&limit=3&order=chrono")
|
|
2483
|
+
.then((r) => (r.ok ? r.json() : { items: [], total: 0 }))
|
|
2484
|
+
.then((d) => {
|
|
2485
|
+
if (cancelled) return;
|
|
2486
|
+
setArchived(d.items || []);
|
|
2487
|
+
setArchivedTotal(d.total || 0);
|
|
2488
|
+
})
|
|
2415
2489
|
.catch(() => { if (!cancelled) setArchived([]); });
|
|
2416
2490
|
return () => { cancelled = true; };
|
|
2417
|
-
}, []);
|
|
2491
|
+
}, [wbReload]);
|
|
2418
2492
|
useEffect(() => { load(); }, [load]);
|
|
2419
2493
|
|
|
2420
2494
|
const restore = (id) => {
|
|
@@ -2425,7 +2499,10 @@ window.__ModuleLoader__.load({
|
|
|
2425
2499
|
body: JSON.stringify({ id, archived: false })
|
|
2426
2500
|
})
|
|
2427
2501
|
.then((r) => { if (!r.ok) throw new Error("http"); })
|
|
2428
|
-
.then(() =>
|
|
2502
|
+
.then(() => {
|
|
2503
|
+
setArchived((cur) => (cur || []).filter((m) => m.id !== id));
|
|
2504
|
+
setArchivedTotal((n) => Math.max(0, n - 1));
|
|
2505
|
+
})
|
|
2429
2506
|
.catch(() => {})
|
|
2430
2507
|
.finally(() => setRestoring(""));
|
|
2431
2508
|
};
|
|
@@ -2457,8 +2534,14 @@ window.__ModuleLoader__.load({
|
|
|
2457
2534
|
row.error_message ? ` · ${row.error_message}` : "")
|
|
2458
2535
|
));
|
|
2459
2536
|
})),
|
|
2460
|
-
deposited !== null &&
|
|
2461
|
-
h("div", { className: "mneme-wbhead" },
|
|
2537
|
+
deposited !== null && depositedTotal > 0 && h(react.Fragment, null,
|
|
2538
|
+
h("div", { className: "mneme-wbhead" },
|
|
2539
|
+
h("span", null, t("memory.status.depositedCount").replace("{n}", String(depositedTotal))),
|
|
2540
|
+
onBrowse && depositedTotal > deposited.length && h("button", {
|
|
2541
|
+
type: "button",
|
|
2542
|
+
className: "mneme-footbtn",
|
|
2543
|
+
onClick: () => onBrowse({ deposited: true })
|
|
2544
|
+
}, t("memory.status.viewAll"))),
|
|
2462
2545
|
h("div", { className: "mneme-wblist" },
|
|
2463
2546
|
deposited.map((m) => h("div", { className: "mneme-wbmemo", key: m.id },
|
|
2464
2547
|
h("span", { className: "mneme-xdot", style: { color: memoryTypeColor(m.type) }, "aria-hidden": "true" }),
|
|
@@ -2468,7 +2551,13 @@ window.__ModuleLoader__.load({
|
|
|
2468
2551
|
`${typeLabel(t, m.type)} · ${formatRelativeTime(m.updated_at || m.created_at, t)}`))
|
|
2469
2552
|
)))),
|
|
2470
2553
|
archived !== null && h(react.Fragment, null,
|
|
2471
|
-
h("div", { className: "mneme-wbhead" },
|
|
2554
|
+
h("div", { className: "mneme-wbhead" },
|
|
2555
|
+
h("span", null, t("memory.status.archivedCount").replace("{n}", String(archivedTotal))),
|
|
2556
|
+
onBrowse && archivedTotal > archived.length && h("button", {
|
|
2557
|
+
type: "button",
|
|
2558
|
+
className: "mneme-footbtn",
|
|
2559
|
+
onClick: () => onBrowse({ archived: true })
|
|
2560
|
+
}, t("memory.status.viewAll"))),
|
|
2472
2561
|
archived.length === 0
|
|
2473
2562
|
? h("div", { className: "mneme-featsubhint" }, t("memory.status.archivedEmpty"))
|
|
2474
2563
|
: h("div", { className: "mneme-wblist" },
|
|
@@ -2488,16 +2577,56 @@ window.__ModuleLoader__.load({
|
|
|
2488
2577
|
);
|
|
2489
2578
|
}
|
|
2490
2579
|
|
|
2491
|
-
|
|
2580
|
+
// 热度分布卡(阶段二):采样最近 200 条的 heat 值做三档分布。自门控——
|
|
2581
|
+
// /list 不下发 heat(heatEnabled=false)时整卡不渲染,前端不感知开关。
|
|
2582
|
+
function HeatStatusCard({ t }) {
|
|
2583
|
+
const [state, setState] = useState({ loading: true, off: false, hot: 0, warm: 0, cold: 0, sample: 0 });
|
|
2584
|
+
useEffect(() => {
|
|
2585
|
+
let cancelled = false;
|
|
2586
|
+
apiFetch("/api/dsh-mneme/list?limit=200&order=chrono")
|
|
2587
|
+
.then((res) => { if (!res.ok) throw new Error("http"); return res.json(); })
|
|
2588
|
+
.then((d) => {
|
|
2589
|
+
if (cancelled) return;
|
|
2590
|
+
const items = d.items || [];
|
|
2591
|
+
if (!items.length || typeof items[0].heat !== "number") {
|
|
2592
|
+
setState({ loading: false, off: true, hot: 0, warm: 0, cold: 0, sample: 0 });
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
let hot = 0, warm = 0, cold = 0;
|
|
2596
|
+
for (const m of items) {
|
|
2597
|
+
if (m.heat >= 0.66) hot++; else if (m.heat >= 0.33) warm++; else cold++;
|
|
2598
|
+
}
|
|
2599
|
+
setState({ loading: false, off: false, hot, warm, cold, sample: items.length });
|
|
2600
|
+
})
|
|
2601
|
+
.catch(() => { if (!cancelled) setState({ loading: false, off: true, hot: 0, warm: 0, cold: 0, sample: 0 }); });
|
|
2602
|
+
return () => { cancelled = true; };
|
|
2603
|
+
}, []);
|
|
2604
|
+
if (state.off) return null;
|
|
2605
|
+
return h(StatusCard, {
|
|
2606
|
+
t,
|
|
2607
|
+
title: t("memory.status.heatDistribution"),
|
|
2608
|
+
loading: state.loading,
|
|
2609
|
+
error: false,
|
|
2610
|
+
num: `${state.hot}`,
|
|
2611
|
+
cap: t("memory.status.heatHint")
|
|
2612
|
+
.replace("{hot}", String(state.hot))
|
|
2613
|
+
.replace("{warm}", String(state.warm))
|
|
2614
|
+
.replace("{cold}", String(state.cold))
|
|
2615
|
+
.replace("{sample}", String(state.sample))
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
function StatusPanel({ t, onBrowse }) {
|
|
2492
2620
|
return h("div", { className: "mneme-status" },
|
|
2493
2621
|
h("div", { className: "mneme-statusgrid" },
|
|
2494
2622
|
h(MemoriesStatusCard, { t }),
|
|
2495
2623
|
h(EntitiesStatusCard, { t }),
|
|
2496
2624
|
h(VectorStatusCard, { t }),
|
|
2497
2625
|
h(LlmStatusCard, { t }),
|
|
2498
|
-
h(DreamStatusCards, { t })
|
|
2626
|
+
h(DreamStatusCards, { t }),
|
|
2627
|
+
h(HeatStatusCard, { t })
|
|
2499
2628
|
),
|
|
2500
|
-
h(WorkbenchSection, { t })
|
|
2629
|
+
h(WorkbenchSection, { t, onBrowse })
|
|
2501
2630
|
);
|
|
2502
2631
|
}
|
|
2503
2632
|
|
|
@@ -2584,6 +2713,9 @@ window.__ModuleLoader__.load({
|
|
|
2584
2713
|
onChange: (e) => setImportance(Number(e.target.value))
|
|
2585
2714
|
}, [1, 2, 3, 4, 5].map((n) => h("option", { key: n, value: n }, "★".repeat(n))))
|
|
2586
2715
|
: h(ImportanceStars, { className: "mneme-dmetaval", value: memory.importance || 0 }),
|
|
2716
|
+
memory.heat != null && h(react.Fragment, null,
|
|
2717
|
+
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.heat")),
|
|
2718
|
+
h("span", { className: "mneme-dmetaval" }, h(HeatBadge, { value: memory.heat, size: 13 }))),
|
|
2587
2719
|
memory.source && h(react.Fragment, null,
|
|
2588
2720
|
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.source")),
|
|
2589
2721
|
h("span", { className: "mneme-dmetaval", title: memory.source }, memory.source)),
|
|
@@ -2633,8 +2765,11 @@ window.__ModuleLoader__.load({
|
|
|
2633
2765
|
)
|
|
2634
2766
|
: h(react.Fragment, null,
|
|
2635
2767
|
h("button", { type: "button", className: "mneme-btn", onClick: () => setEditing(true) }, t("memory.explorer.detail.edit")),
|
|
2636
|
-
|
|
2637
|
-
|
|
2768
|
+
memory.archived
|
|
2769
|
+
? h("button", { type: "button", className: "mneme-btn", disabled: busy, onClick: () => postUpdate({ archived: false }, { restored: true }) },
|
|
2770
|
+
t("memory.explorer.detail.restore"))
|
|
2771
|
+
: h("button", { type: "button", className: "mneme-btn", disabled: busy, onClick: () => postUpdate({ archived: true }, { archived: true }) },
|
|
2772
|
+
t("memory.explorer.detail.archive")),
|
|
2638
2773
|
h("button", {
|
|
2639
2774
|
type: "button",
|
|
2640
2775
|
className: "mneme-btn",
|
|
@@ -2747,6 +2882,10 @@ window.__ModuleLoader__.load({
|
|
|
2747
2882
|
catch { return "timeline"; }
|
|
2748
2883
|
});
|
|
2749
2884
|
const [dateRange, setDateRange] = useState("all"); // all | 7d | 30d | 90d
|
|
2885
|
+
// 方案 A:沉淀/归档筛选 chip——状态页「查看全部」也会带着它们跳转过来。
|
|
2886
|
+
const [depositedOnly, setDepositedOnly] = useState(false); // 只看 autoDream 巩固
|
|
2887
|
+
const [archivedOnly, setArchivedOnly] = useState(false); // 只看已归档
|
|
2888
|
+
const [heatSort, setHeatSort] = useState(false); // 卡片页内热度降序(阶段二补口)
|
|
2750
2889
|
const [pendingIds, setPendingIds] = useState(() => new Set());
|
|
2751
2890
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
2752
2891
|
const [importOpen, setImportOpen] = useState(false);
|
|
@@ -2785,7 +2924,9 @@ window.__ModuleLoader__.load({
|
|
|
2785
2924
|
).toISOString();
|
|
2786
2925
|
const filterQS = (type === "all" ? "" : `&type=${encodeURIComponent(type)}`)
|
|
2787
2926
|
+ (minImp ? `&minImportance=${minImp}` : "")
|
|
2788
|
-
+ (dateFromIso ? `&updatedFrom=${encodeURIComponent(dateFromIso)}` : "")
|
|
2927
|
+
+ (dateFromIso ? `&updatedFrom=${encodeURIComponent(dateFromIso)}` : "")
|
|
2928
|
+
+ (depositedOnly ? "&deposited=only" : "")
|
|
2929
|
+
+ (archivedOnly ? "&archived=only" : "");
|
|
2789
2930
|
const listUrl = (offset) => `/api/dsh-mneme/list?limit=${PAGE_SIZE}&offset=${offset}${filterQS}&order=chrono`;
|
|
2790
2931
|
|
|
2791
2932
|
// Browse = paged, pure-chronological pages (order=chrono). The default
|
|
@@ -2914,6 +3055,13 @@ window.__ModuleLoader__.load({
|
|
|
2914
3055
|
// the grouping follows the sort order instead of re-sorting buckets.
|
|
2915
3056
|
const sorted = [...visible].sort((a, b) =>
|
|
2916
3057
|
new Date(b.updated_at || b.created_at || 0) - new Date(a.updated_at || a.created_at || 0));
|
|
3058
|
+
// heatSort(阶段二补口,order=heat 的前端实现):热度是运行时投影、
|
|
3059
|
+
// 无存储序,SQL 排不了;页内对已加载条目降序(非全局序),时间树
|
|
3060
|
+
// 仍走 chrono 不受影响。开关仅在 heat 字段在场上时出现(自门控)。
|
|
3061
|
+
const heatAvailable = visible.some((m) => typeof m.heat === "number");
|
|
3062
|
+
const gridItems = heatSort
|
|
3063
|
+
? [...sorted].sort((a, b) => (b.heat ?? 0) - (a.heat ?? 0))
|
|
3064
|
+
: sorted;
|
|
2917
3065
|
const months = [];
|
|
2918
3066
|
let curMonth = null, curDay = null;
|
|
2919
3067
|
for (const m of sorted) {
|
|
@@ -2971,7 +3119,7 @@ window.__ModuleLoader__.load({
|
|
|
2971
3119
|
|
|
2972
3120
|
// 编辑保存 / 归档的本地落账:替换或移除对应行;镜像由后端重渲染。
|
|
2973
3121
|
// 归档从所有视图移除(列表默认只看未归档),并清空选中。
|
|
2974
|
-
const applyMemoryUpdate = (updated, { archived = false } = {}) => {
|
|
3122
|
+
const applyMemoryUpdate = (updated, { archived = false, restored = false } = {}) => {
|
|
2975
3123
|
if (archived) {
|
|
2976
3124
|
setItems((cur) => cur.filter((m) => m.id !== updated.id));
|
|
2977
3125
|
setRemoteItems((cur) => (cur ? cur.filter((m) => m.id !== updated.id) : cur));
|
|
@@ -2981,6 +3129,17 @@ window.__ModuleLoader__.load({
|
|
|
2981
3129
|
setTimeout(() => setToastMsg(""), 2500);
|
|
2982
3130
|
return;
|
|
2983
3131
|
}
|
|
3132
|
+
if (restored) {
|
|
3133
|
+
// 恢复:行离开当前视图(抽屉只能从归档浏览列表打开它),回主列表
|
|
3134
|
+
// 由下一次筛选切换/刷新自然带出。
|
|
3135
|
+
setItems((cur) => cur.filter((m) => m.id !== updated.id));
|
|
3136
|
+
setRemoteItems((cur) => (cur ? cur.filter((m) => m.id !== updated.id) : cur));
|
|
3137
|
+
setTotal((n) => Math.max(0, n - 1));
|
|
3138
|
+
setSelectedId(null);
|
|
3139
|
+
setToastMsg(t("memory.explorer.detail.restored"));
|
|
3140
|
+
setTimeout(() => setToastMsg(""), 2500);
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
2984
3143
|
const replace = (m) => (m.id === updated.id ? updated : m);
|
|
2985
3144
|
setItems((cur) => cur.map(replace));
|
|
2986
3145
|
setRemoteItems((cur) => (cur ? cur.map(replace) : cur));
|
|
@@ -3026,6 +3185,16 @@ window.__ModuleLoader__.load({
|
|
|
3026
3185
|
setView("entity");
|
|
3027
3186
|
};
|
|
3028
3187
|
|
|
3188
|
+
// 状态页「查看全部」入口:跳回记忆库并预置对应筛选(清搜索态,避免
|
|
3189
|
+
// remoteItems 盖过浏览列表)。
|
|
3190
|
+
const browseWithFilter = (patch) => {
|
|
3191
|
+
setDepositedOnly(!!patch.deposited);
|
|
3192
|
+
setArchivedOnly(!!patch.archived);
|
|
3193
|
+
setQuery("");
|
|
3194
|
+
setRemoteItems(null);
|
|
3195
|
+
setView("memory");
|
|
3196
|
+
};
|
|
3197
|
+
|
|
3029
3198
|
const subviews = [
|
|
3030
3199
|
{ key: "memory", label: t("memory.explorer.tabMemory") },
|
|
3031
3200
|
{ key: "entity", label: t("memory.explorer.tabEntities") },
|
|
@@ -3144,6 +3313,32 @@ window.__ModuleLoader__.load({
|
|
|
3144
3313
|
onClick: () => setMinImp(v)
|
|
3145
3314
|
}, v === 0 ? t("memory.tab.all") : `★${v}+`))
|
|
3146
3315
|
),
|
|
3316
|
+
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.sourceFilter")),
|
|
3317
|
+
h("div", { className: "mneme-xrow" },
|
|
3318
|
+
h("button", {
|
|
3319
|
+
className: depositedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
3320
|
+
title: t("memory.explorer.filterDepositedHint"),
|
|
3321
|
+
onClick: () => setDepositedOnly(!depositedOnly)
|
|
3322
|
+
}, t("memory.explorer.filterDeposited")),
|
|
3323
|
+
h("button", {
|
|
3324
|
+
className: archivedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
3325
|
+
title: t("memory.explorer.filterArchivedHint"),
|
|
3326
|
+
onClick: () => setArchivedOnly(!archivedOnly)
|
|
3327
|
+
}, t("memory.explorer.filterArchived"))
|
|
3328
|
+
),
|
|
3329
|
+
heatAvailable && h(react.Fragment, null,
|
|
3330
|
+
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.sort")),
|
|
3331
|
+
h("div", { className: "mneme-xrow" },
|
|
3332
|
+
h("button", {
|
|
3333
|
+
className: heatSort ? "mneme-chip mneme-active" : "mneme-chip",
|
|
3334
|
+
title: t("memory.explorer.heatSortHint"),
|
|
3335
|
+
onClick: () => {
|
|
3336
|
+
setHeatSort(!heatSort);
|
|
3337
|
+
if (!heatSort) switchViewMode("cards"); // 排序只作用于卡片网格
|
|
3338
|
+
}
|
|
3339
|
+
}, t("memory.explorer.heatSort"))
|
|
3340
|
+
)
|
|
3341
|
+
),
|
|
3147
3342
|
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.types")),
|
|
3148
3343
|
h("button", {
|
|
3149
3344
|
className: type === "all" ? "mneme-xtype mneme-active" : "mneme-xtype",
|
|
@@ -3168,7 +3363,7 @@ window.__ModuleLoader__.load({
|
|
|
3168
3363
|
? h("div", { className: "mneme-xempty", style: { gridColumn: "1 / -1" } },
|
|
3169
3364
|
h(Icon, { name: "inbox", size: 20, className: "mneme-xemptyico" }),
|
|
3170
3365
|
t("memory.explorer.empty"))
|
|
3171
|
-
:
|
|
3366
|
+
: gridItems.map((m) => h("button", {
|
|
3172
3367
|
key: m.id,
|
|
3173
3368
|
type: "button",
|
|
3174
3369
|
className: m.id === selectedId ? "mneme-card mneme-active" : "mneme-card",
|
|
@@ -3184,6 +3379,7 @@ window.__ModuleLoader__.load({
|
|
|
3184
3379
|
h("div", { className: "mneme-cardexcerpt" }, m.content || ""),
|
|
3185
3380
|
h("div", { className: "mneme-cardfoot" },
|
|
3186
3381
|
h(ImportanceStars, { value: m.importance || 0, size: 12 }),
|
|
3382
|
+
h(HeatBadge, { value: m.heat }),
|
|
3187
3383
|
m.source && h("span", { className: "mneme-cardsrc", title: m.source }, m.source)
|
|
3188
3384
|
)
|
|
3189
3385
|
)),
|
|
@@ -3276,7 +3472,7 @@ window.__ModuleLoader__.load({
|
|
|
3276
3472
|
})
|
|
3277
3473
|
),
|
|
3278
3474
|
view === "entity" && h(EntityPanel, { t, focusEntity: graphFocus, onJumpMemory: jumpToMemory }),
|
|
3279
|
-
view === "status" && h(StatusPanel, { t }),
|
|
3475
|
+
view === "status" && h(StatusPanel, { t, onBrowse: browseWithFilter }),
|
|
3280
3476
|
view === "settings" && h("div", { className: "mneme-set" },
|
|
3281
3477
|
h("div", { className: "mneme-set-inner" }, h(SettingsContent, { t }))
|
|
3282
3478
|
),
|
|
@@ -3369,6 +3565,7 @@ window.__ModuleLoader__.load({
|
|
|
3369
3565
|
}
|
|
3370
3566
|
|
|
3371
3567
|
function apply(ctx) {
|
|
3568
|
+
const t = ctx.locale.bind(NS);
|
|
3372
3569
|
ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-mneme: dictionaries");
|
|
3373
3570
|
|
|
3374
3571
|
// 记忆库唯一入口:sidebar.footer.action 注册作为锚点与回退;真实按钮
|
|
@@ -3377,7 +3574,6 @@ window.__ModuleLoader__.load({
|
|
|
3377
3574
|
// v0.7.15 起不再注册 conversation.view tab——对话内嵌的面板被悬浮
|
|
3378
3575
|
// 输入框遮挡、挤压会话布局,sheet 页是更舒服的承载方式。
|
|
3379
3576
|
ctx.slots.inject("sidebar.footer.action", () => {
|
|
3380
|
-
const t = ctx.locale.bind(NS);
|
|
3381
3577
|
return ctx.slots.register({
|
|
3382
3578
|
name: "sidebar.footer.action",
|
|
3383
3579
|
id: "dsh-mneme",
|
|
@@ -3392,6 +3588,48 @@ window.__ModuleLoader__.load({
|
|
|
3392
3588
|
h(MemoryOverlay, { t })
|
|
3393
3589
|
));
|
|
3394
3590
|
});
|
|
3591
|
+
|
|
3592
|
+
// --- better-sidebar 生态 tab(可选软依赖)---
|
|
3593
|
+
// ⚠️ 平台事实(issue #88 实测 + dsh-server-deck 同款方案):模块级
|
|
3594
|
+
// inject 声明 betterSidebar 是硬等待——未安装 bs 的环境整个 entry
|
|
3595
|
+
// pending("1 entry did not activate" → Failed to load plugins),
|
|
3596
|
+
// v0.7.18 即因此对无 bs 用户启动失败。正确双模式(server-deck 验证
|
|
3597
|
+
// 过):外层入口零 inject 立即激活(独立模式保底);tab 注册挂在内层
|
|
3598
|
+
// 动态子插件 `ctx.plugin({ inject: ['betterSidebar'] })`,由 cordis
|
|
3599
|
+
// 原生等待服务——bs 未装时该内层 fiber 永远 INACTIVE,静默无害。
|
|
3600
|
+
// 兄弟上下文取未声明属性会直接抛错("cannot get property without
|
|
3601
|
+
// inject"),轮询探测方案不可用。
|
|
3602
|
+
ctx.effect(() => {
|
|
3603
|
+
let dead = false;
|
|
3604
|
+
try {
|
|
3605
|
+
ctx.plugin?.({
|
|
3606
|
+
name: "dsh-mneme:bs-tab",
|
|
3607
|
+
inject: ["betterSidebar"],
|
|
3608
|
+
apply: (bsCtx) => {
|
|
3609
|
+
bsCtx.effect(() => {
|
|
3610
|
+
if (dead) return;
|
|
3611
|
+
const reg = bsCtx.betterSidebar;
|
|
3612
|
+
if (!reg || typeof reg.registerTab !== "function") return;
|
|
3613
|
+
try {
|
|
3614
|
+
reg.registerTab({
|
|
3615
|
+
id: "dsh-mneme:memory",
|
|
3616
|
+
title: () => t("memory.view.label"),
|
|
3617
|
+
icon: (size) => h(IconArchiveOutline20, { size }),
|
|
3618
|
+
order: 60,
|
|
3619
|
+
component: () => h(MemoryExplorer, { t })
|
|
3620
|
+
});
|
|
3621
|
+
} catch (err) {
|
|
3622
|
+
// id 重复等注册失败不应拖垮其余功能,留一条线索即可
|
|
3623
|
+
console.error("[dsh-mneme] better-sidebar registerTab failed:", err);
|
|
3624
|
+
}
|
|
3625
|
+
}, "dsh-mneme: better-sidebar tab");
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3628
|
+
} catch (err) {
|
|
3629
|
+
console.error("[dsh-mneme] better-sidebar mount failed:", err);
|
|
3630
|
+
}
|
|
3631
|
+
return () => { dead = true; };
|
|
3632
|
+
}, "dsh-mneme: better-sidebar mount");
|
|
3395
3633
|
}
|
|
3396
3634
|
|
|
3397
3635
|
exports.apply = apply;
|
package/lib/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import { TYPE_DECAY_DEFAULTS } from "./heat.js";
|
|
2
3
|
|
|
3
4
|
export const Config = z.object({
|
|
4
5
|
memoryDir: z.string().default("~/.dsh/memory"),
|
|
@@ -301,6 +302,24 @@ export const Config = z.object({
|
|
|
301
302
|
// reaches any service; a persisted panel_mode="light" (settings kv) counts
|
|
302
303
|
// as lightMode=true too and wins over the bundle config.
|
|
303
304
|
lightMode: z.boolean().default(false),
|
|
305
|
+
|
|
306
|
+
// --- heat: v0.7.0 self-evolution (heat + interest drift) ----------------
|
|
307
|
+
// 总开关,默认关(v0.7.12+ 用户已习惯无 heat 行为,默认开=全员行为变更)。
|
|
308
|
+
// 开启后:提供热度字段 / sleep 降级联合判定保护 / 前端热度投影,不改变
|
|
309
|
+
// 召回排序。关闭则跳过所有 heat 计算与热度触达,sleep 降级退回纯时间分层。
|
|
310
|
+
// 也走 feature_flags(FEATURE_FLAG_BOOLEANS 白名单),面板可启停=线上回滚开关。
|
|
311
|
+
heatEnabled: z.boolean().default(false),
|
|
312
|
+
// 幂律形状参数 α(heat = 1/(1+λΔt)^α),越大衰减越快。
|
|
313
|
+
heatGlobalAlpha: z.number().min(0.1).max(5).default(1.2),
|
|
314
|
+
// per-type 衰减因子 λ;λ=0 的类型免疫(热度恒 1.0,sleep 永不降级)。
|
|
315
|
+
// 未知类型走默认 0.002。dict 的键为 type 字符串、值为数字 λ。
|
|
316
|
+
heatTypeDecay: z.dict(z.number(), z.string()).default({ ...TYPE_DECAY_DEFAULTS }),
|
|
317
|
+
// sleep 降级联合判定的热度下限:heat < 该值 且 importance<5 才允许降级。
|
|
318
|
+
sleepHeatThreshold: z.number().min(0).max(1).default(0.05),
|
|
319
|
+
// recordRecall 默认值(recall_runs 记录默认开;显式传 false 的调用方不受影响)。
|
|
320
|
+
recallRecordDefault: z.boolean().default(true),
|
|
321
|
+
// recall_runs 滚动清理保留天数。
|
|
322
|
+
recallRetentionDays: z.natural().min(1).max(3650).default(90),
|
|
304
323
|
});
|
|
305
324
|
|
|
306
325
|
// Fields forced to false by the light-mode preset. Everything not listed here
|
|
@@ -315,7 +334,9 @@ const LIGHT_MODE_OFF = [
|
|
|
315
334
|
"hybridInject",
|
|
316
335
|
"searchSemanticDedup",
|
|
317
336
|
"selectiveInjectEnabled",
|
|
318
|
-
"bm25SearchEnabled"
|
|
337
|
+
"bm25SearchEnabled",
|
|
338
|
+
// 轻量模式不开热计算(heat 属于重型增强;关掉后 sleep 降级也退回纯时间分层)。
|
|
339
|
+
"heatEnabled"
|
|
319
340
|
];
|
|
320
341
|
|
|
321
342
|
/**
|
package/lib/dream/sleep.js
CHANGED
|
@@ -18,6 +18,7 @@ import { randomUUID, createHash } from "node:crypto";
|
|
|
18
18
|
import { validateDecisions, applyDecisions } from "./decisions.js";
|
|
19
19
|
import { findPotentialConflicts } from "./clustering.js";
|
|
20
20
|
import { buildReceipt, withEffortFallback } from "../dream.js";
|
|
21
|
+
import { computeHeat } from "../heat.js";
|
|
21
22
|
|
|
22
23
|
const SUMMARY_MAX = 120;
|
|
23
24
|
// Conflict similarity threshold per strictness level (v0.4.0):
|
|
@@ -242,10 +243,22 @@ function phaseDemotion(service, config, logger, runId, signal = null) {
|
|
|
242
243
|
for (const m of service.all()) {
|
|
243
244
|
if (signal?.aborted) break;
|
|
244
245
|
if (m.archived || m.forgotten) continue;
|
|
245
|
-
const ref = m.last_accessed_at ?? m.
|
|
246
|
+
const ref = m.last_accessed_at ?? m.created_at;
|
|
246
247
|
if (!ref) continue;
|
|
247
248
|
const t = new Date(ref).getTime();
|
|
248
249
|
if (Number.isNaN(t)) continue;
|
|
250
|
+
// v0.7.0 热联合判定(仅 heatEnabled 时启用;默认关则退回纯时间分层,
|
|
251
|
+
// 与 v0.7.12 行为一致):时间窗之外再加两道保护闸——热度低于
|
|
252
|
+
// sleepHeatThreshold 且 importance<5 才允许降级。λ=0 的免疫类型 heat 恒
|
|
253
|
+
// 1.0 天然豁免(preference/pattern/summary 永不因 sleep 降级);importance
|
|
254
|
+
// ≥5 的紧要记忆无论多冷都保留。`冷但重要` 与 `热但低值` 均不满足条件。
|
|
255
|
+
const heatOn = config.heatEnabled !== false;
|
|
256
|
+
if (heatOn) {
|
|
257
|
+
const heat = computeHeat(m, Date.now(), config);
|
|
258
|
+
const heatProtected = heat >= (config.sleepHeatThreshold ?? 0.05);
|
|
259
|
+
const important = (m.importance ?? 0) >= 5;
|
|
260
|
+
if (heatProtected || important) continue;
|
|
261
|
+
}
|
|
249
262
|
if (t < compressCut) {
|
|
250
263
|
service.setArchived(m.id, true);
|
|
251
264
|
archived.push(m.id);
|