@modusensus/dsh-mneme 0.7.16 → 0.7.18
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 +35 -10
- package/README.md +26 -45
- package/lib/api.js +6 -2
- package/lib/client.js +207 -47
- package/lib/store.js +16 -2
- package/package.json +10 -3
- package/src/api.js +6 -2
- package/src/store.js +16 -2
- package/test/api-routes-gaps.test.js +218 -0
- package/test/api.test.js +55 -0
- package/test/client.test.js +136 -0
- package/test/lib-smoke.test.js +96 -0
package/lib/client.js
CHANGED
|
@@ -74,6 +74,24 @@ window.__ModuleLoader__.load({
|
|
|
74
74
|
}, parts.map(([tag, attrs], i) => h(tag, { key: i, ...attrs })));
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
+
// Lucide v1.42 star path(morphicons 官方配套的数据包)。morphicons 本体
|
|
78
|
+
// 是 ESM-only 的变形动画引擎、插件运行时不允许 require 第三方库,故按
|
|
79
|
+
// 既有惯例内联图标数据静态渲染;实心/空心由 fill 区分,空心降透明度。
|
|
80
|
+
const STAR_PATH_D = "M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z";
|
|
81
|
+
const ImportanceStars = ({ value = 0, size = 13, className }) => {
|
|
82
|
+
const filled = Math.min(5, Math.max(0, Math.round(value || 0)));
|
|
83
|
+
return h("span", { className: `mneme-stars${className ? ` ${className}` : ""}`, role: "img", "aria-label": `${filled}/5` },
|
|
84
|
+
[0, 1, 2, 3, 4].map((i) => h("svg", { key: i, width: size, height: size, viewBox: "0 0 24 24", "aria-hidden": "true" },
|
|
85
|
+
h("path", {
|
|
86
|
+
d: STAR_PATH_D,
|
|
87
|
+
fill: i < filled ? "currentColor" : "none",
|
|
88
|
+
stroke: "currentColor",
|
|
89
|
+
strokeWidth: 1.6,
|
|
90
|
+
strokeLinejoin: "round",
|
|
91
|
+
opacity: i < filled ? 1 : 0.35
|
|
92
|
+
}))));
|
|
93
|
+
};
|
|
94
|
+
|
|
77
95
|
// Unified API fetcher: attaches the optional apiToken (set in the settings
|
|
78
96
|
// view, persisted in localStorage) as a Bearer header. When no token has
|
|
79
97
|
// been configured the header is omitted and the API stays open (default).
|
|
@@ -87,7 +105,7 @@ window.__ModuleLoader__.load({
|
|
|
87
105
|
return fetch(path, { ...opts, headers });
|
|
88
106
|
}
|
|
89
107
|
|
|
90
|
-
const inject = ["slots", "locale"];
|
|
108
|
+
const inject = ["slots", "locale", "betterSidebar"];
|
|
91
109
|
|
|
92
110
|
const NS = "memory";
|
|
93
111
|
|
|
@@ -156,6 +174,11 @@ window.__ModuleLoader__.load({
|
|
|
156
174
|
"memory.explorer.updated": "更新",
|
|
157
175
|
"memory.explorer.tags": "标签",
|
|
158
176
|
"memory.explorer.importance": "重要性",
|
|
177
|
+
"memory.explorer.sourceFilter": "来源",
|
|
178
|
+
"memory.explorer.filterDeposited": "沉淀",
|
|
179
|
+
"memory.explorer.filterDepositedHint": "只看 autoDream 巩固/更新过的记忆",
|
|
180
|
+
"memory.explorer.filterArchived": "已归档",
|
|
181
|
+
"memory.explorer.filterArchivedHint": "只看已归档的记忆(默认列表不含它们)",
|
|
159
182
|
"memory.explorer.topK": "返回数量",
|
|
160
183
|
"memory.explorer.topKOption": "返回 {n} 条",
|
|
161
184
|
"memory.card.open": "在记忆库中查看全文",
|
|
@@ -311,6 +334,8 @@ window.__ModuleLoader__.load({
|
|
|
311
334
|
"memory.explorer.detail.save": "保存",
|
|
312
335
|
"memory.explorer.detail.cancel": "取消",
|
|
313
336
|
"memory.explorer.detail.archive": "归档",
|
|
337
|
+
"memory.explorer.detail.restore": "恢复",
|
|
338
|
+
"memory.explorer.detail.restored": "已恢复到主列表",
|
|
314
339
|
"memory.explorer.detail.archived": "已归档",
|
|
315
340
|
"memory.explorer.detail.archiveFailed": "操作失败",
|
|
316
341
|
"memory.explorer.detail.saved": "已保存,镜像同步更新",
|
|
@@ -337,6 +362,9 @@ window.__ModuleLoader__.load({
|
|
|
337
362
|
"memory.status.consolidated": "沉淀的记忆",
|
|
338
363
|
"memory.status.consolidatedEmpty": "autoDream / autoSummarize 沉淀的记忆会出现在这里",
|
|
339
364
|
"memory.status.archivedMemories": "已归档的记忆",
|
|
365
|
+
"memory.status.viewAll": "查看全部",
|
|
366
|
+
"memory.status.depositedCount": "沉淀的记忆({n})",
|
|
367
|
+
"memory.status.archivedCount": "已归档的记忆({n})",
|
|
340
368
|
"memory.status.archivedEmpty": "没有归档的记忆",
|
|
341
369
|
"memory.status.restore": "恢复",
|
|
342
370
|
"memory.settings.extapi.reveal": "显示",
|
|
@@ -420,6 +448,11 @@ window.__ModuleLoader__.load({
|
|
|
420
448
|
"memory.explorer.updated": "Updated",
|
|
421
449
|
"memory.explorer.tags": "Tags",
|
|
422
450
|
"memory.explorer.importance": "Importance",
|
|
451
|
+
"memory.explorer.sourceFilter": "Source",
|
|
452
|
+
"memory.explorer.filterDeposited": "Deposited",
|
|
453
|
+
"memory.explorer.filterDepositedHint": "Only memories autoDream consolidated or updated",
|
|
454
|
+
"memory.explorer.filterArchived": "Archived",
|
|
455
|
+
"memory.explorer.filterArchivedHint": "Only archived memories (excluded from the default list)",
|
|
423
456
|
"memory.explorer.topK": "Results limit",
|
|
424
457
|
"memory.explorer.topKOption": "Return {n}",
|
|
425
458
|
"memory.card.open": "Open full text in the memory library",
|
|
@@ -575,6 +608,8 @@ window.__ModuleLoader__.load({
|
|
|
575
608
|
"memory.explorer.detail.save": "Save",
|
|
576
609
|
"memory.explorer.detail.cancel": "Cancel",
|
|
577
610
|
"memory.explorer.detail.archive": "Archive",
|
|
611
|
+
"memory.explorer.detail.restore": "Restore",
|
|
612
|
+
"memory.explorer.detail.restored": "Restored to the main list",
|
|
578
613
|
"memory.explorer.detail.archived": "Archived",
|
|
579
614
|
"memory.explorer.detail.archiveFailed": "Action failed",
|
|
580
615
|
"memory.explorer.detail.saved": "Saved; mirrors re-rendered",
|
|
@@ -601,6 +636,9 @@ window.__ModuleLoader__.load({
|
|
|
601
636
|
"memory.status.consolidated": "Deposited memories",
|
|
602
637
|
"memory.status.consolidatedEmpty": "Memories deposited by autoDream / autoSummarize appear here",
|
|
603
638
|
"memory.status.archivedMemories": "Archived memories",
|
|
639
|
+
"memory.status.viewAll": "View all",
|
|
640
|
+
"memory.status.depositedCount": "Deposited memories ({n})",
|
|
641
|
+
"memory.status.archivedCount": "Archived memories ({n})",
|
|
604
642
|
"memory.status.archivedEmpty": "Nothing archived",
|
|
605
643
|
"memory.status.restore": "Restore",
|
|
606
644
|
"memory.settings.extapi.reveal": "Reveal",
|
|
@@ -668,6 +706,8 @@ window.__ModuleLoader__.load({
|
|
|
668
706
|
// text-turns-brand-blue active states — no boxed panels).
|
|
669
707
|
const CSS_TAG = "@modusensus/dsh-mneme/drawer.css";
|
|
670
708
|
const css = [
|
|
709
|
+
// --- explorer shell: container queries let the toolbar adapt to the
|
|
710
|
+
// better-sidebar workbench pane (~500px) as well as the 1240px sheet ---
|
|
671
711
|
// --- sidebar foot trigger (wide row / collapsed rail icon) ---
|
|
672
712
|
".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}",
|
|
673
713
|
".mneme-trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
@@ -687,7 +727,11 @@ window.__ModuleLoader__.load({
|
|
|
687
727
|
".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}",
|
|
688
728
|
".mneme-entitychip:hover{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent)}",
|
|
689
729
|
// --- main-area memory library page ---
|
|
690
|
-
".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)}",
|
|
730
|
+
".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}",
|
|
731
|
+
// 窄容器(better-sidebar 工作台 ~500px)下中央 vtabs 会与右缘悬浮的
|
|
732
|
+
// 卡片/时间线切换器重叠:容器查询收窄时隐藏切换器,视图模式沿用
|
|
733
|
+
// localStorage 记忆,宽容器(sheet)不受影响。
|
|
734
|
+
"@container (max-width: 640px){.mneme-xtools .mneme-seg{display:none}}",
|
|
691
735
|
".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}",
|
|
692
736
|
".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)}",
|
|
693
737
|
".mneme-vtabs{display:flex;align-items:stretch;height:46px}",
|
|
@@ -695,7 +739,9 @@ window.__ModuleLoader__.load({
|
|
|
695
739
|
".mneme-vtab:hover{color:var(--dsw-alias-label-primary)}",
|
|
696
740
|
".mneme-vtab.mneme-active{color:var(--dsw-alias-state-business-primary)}",
|
|
697
741
|
".mneme-vtab.mneme-active::after{content:\"\";position:absolute;left:10px;right:10px;bottom:-1px;height:2.5px;border-radius:2px;background:var(--dsw-alias-state-business-primary)}",
|
|
698
|
-
|
|
742
|
+
// transform 会自建层叠上下文:层级必须给在容器上——3 高于吸顶月份头(2)、
|
|
743
|
+
// 低于详情抽屉(6),下拉菜单/导入对话框随容器整体上浮。
|
|
744
|
+
".mneme-xtools{position:absolute;right:14px;top:50%;transform:translateY(-50%);display:flex;align-items:center;gap:8px;padding:0;z-index:3}",
|
|
699
745
|
".mneme-xcount{flex:none;font-size:12px;line-height:16px;color:var(--dsw-alias-label-tertiary);white-space:nowrap}",
|
|
700
746
|
// --- three-column browse layout: hairline separators, no outer box ---
|
|
701
747
|
".mneme-xmain{flex:1;min-height:0;display:flex;flex-direction:row;overflow:hidden}",
|
|
@@ -824,10 +870,13 @@ window.__ModuleLoader__.load({
|
|
|
824
870
|
// --- 侧边栏顶部入口:借宿主「新会话」按钮的原生类名对齐 ---
|
|
825
871
|
// wrapper display:contents 隐身,按钮成为侧边栏弹性布局的直接子元素;
|
|
826
872
|
// 盒模型/间距/收起态 rail 几何全部继承宿主,我们只覆盖配色为次级观感。
|
|
873
|
+
".mneme-stars{display:inline-flex;align-items:center;gap:2px}",
|
|
874
|
+
// width:100% 与「新会话」同宽(同一弹性父容器);文案对齐交给复制来的
|
|
875
|
+
// 原生类,不再用 flex:1/text-align:left 覆盖成左对齐。
|
|
827
876
|
".mneme-topentry{display:contents}",
|
|
828
|
-
".mneme-topentry-native{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}",
|
|
877
|
+
".mneme-topentry-native{width:100%;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}",
|
|
829
878
|
".mneme-topentry-native:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
|
|
830
|
-
".mneme-topentry-native .mneme-topentry-label{
|
|
879
|
+
".mneme-topentry-native .mneme-topentry-label{white-space:nowrap}",
|
|
831
880
|
// --- 功能开关:一行一开关,Claude 式安静排版 ---
|
|
832
881
|
".mneme-featgroup{flex:none;font-size:12px;font-weight:600;letter-spacing:.02em;color:var(--dsw-alias-label-tertiary);margin:16px 0 2px}",
|
|
833
882
|
".mneme-featrow{display:flex;align-items:center;gap:14px;padding:11px 2px;border-bottom:1px solid var(--dsw-alias-border-l1)}",
|
|
@@ -915,7 +964,7 @@ window.__ModuleLoader__.load({
|
|
|
915
964
|
".mneme-statusgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;max-width:1000px;margin:0 auto}",
|
|
916
965
|
".mneme-statuscard{min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:14px}",
|
|
917
966
|
// --- 状态页工作台:让用户看见插件在干活(动态/沉淀/归档) ---
|
|
918
|
-
".mneme-wbhead{margin:28px auto 10px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);max-width:1000px}",
|
|
967
|
+
".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}",
|
|
919
968
|
".mneme-wblist{max-width:1000px;display:flex;flex-direction:column;gap:6px}",
|
|
920
969
|
".mneme-wbrow{display:flex;gap:10px;align-items:flex-start;padding:10px 14px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px}",
|
|
921
970
|
".mneme-wbdot{flex:none;width:8px;height:8px;border-radius:50%;margin-top:6px;background:var(--dsw-alias-state-success,#2a9d6a)}",
|
|
@@ -2356,11 +2405,14 @@ window.__ModuleLoader__.load({
|
|
|
2356
2405
|
return [];
|
|
2357
2406
|
}
|
|
2358
2407
|
|
|
2359
|
-
function WorkbenchSection({ t }) {
|
|
2408
|
+
function WorkbenchSection({ t, onBrowse }) {
|
|
2360
2409
|
const [feed, setFeed] = useState(null); // audit rows(已过滤巩固/总结)
|
|
2361
|
-
const [deposited, setDeposited] = useState(null); //
|
|
2362
|
-
const [
|
|
2410
|
+
const [deposited, setDeposited] = useState(null); // 沉淀记忆首页(服务端 deposited 视图)
|
|
2411
|
+
const [depositedTotal, setDepositedTotal] = useState(0);
|
|
2412
|
+
const [archived, setArchived] = useState(null); // 归档记忆首页
|
|
2413
|
+
const [archivedTotal, setArchivedTotal] = useState(0);
|
|
2363
2414
|
const [restoring, setRestoring] = useState("");
|
|
2415
|
+
const [wbReload, setWbReload] = useState(0);
|
|
2364
2416
|
|
|
2365
2417
|
const load = useCallback(() => {
|
|
2366
2418
|
let cancelled = false;
|
|
@@ -2368,30 +2420,31 @@ window.__ModuleLoader__.load({
|
|
|
2368
2420
|
.then((r) => (r.ok ? r.json() : { items: [] }))
|
|
2369
2421
|
.then((d) => {
|
|
2370
2422
|
if (cancelled) return;
|
|
2371
|
-
|
|
2372
|
-
r.trigger_source === "autoDream" || r.trigger_source === "autoSummarize");
|
|
2373
|
-
setFeed(rows);
|
|
2374
|
-
const ids = [];
|
|
2375
|
-
for (const row of rows) {
|
|
2376
|
-
for (const id of parseRelatedIds(row.related_memory_ids)) {
|
|
2377
|
-
if (!ids.includes(id)) ids.push(id);
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
return apiFetch("/api/dsh-mneme/list?limit=300&order=chrono")
|
|
2381
|
-
.then((r2) => (r2.ok ? r2.json() : { items: [] }))
|
|
2382
|
-
.then((d2) => {
|
|
2383
|
-
if (cancelled) return;
|
|
2384
|
-
const byId = new Map((d2.items || []).map((m) => [m.id, m]));
|
|
2385
|
-
setDeposited(ids.map((id) => byId.get(id)).filter(Boolean).slice(0, 8));
|
|
2386
|
-
});
|
|
2423
|
+
setFeed((d.items || []).filter((r) =>
|
|
2424
|
+
r.trigger_source === "autoDream" || r.trigger_source === "autoSummarize"));
|
|
2387
2425
|
})
|
|
2388
2426
|
.catch(() => { if (!cancelled) setFeed([]); });
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2427
|
+
// 仪表盘数据源:沉淀/归档各取一小页 + 服务端 total(/list 的 total
|
|
2428
|
+
// 与行同过滤)。长列表不再进状态页——「查看全部」跳记忆库的筛选
|
|
2429
|
+
// 视图,查询收敛到有搜索/分页的地方。
|
|
2430
|
+
apiFetch("/api/dsh-mneme/list?deposited=only&limit=8&order=chrono")
|
|
2431
|
+
.then((r) => (r.ok ? r.json() : { items: [], total: 0 }))
|
|
2432
|
+
.then((d) => {
|
|
2433
|
+
if (cancelled) return;
|
|
2434
|
+
setDeposited(d.items || []);
|
|
2435
|
+
setDepositedTotal(d.total || 0);
|
|
2436
|
+
})
|
|
2437
|
+
.catch(() => { if (!cancelled) setDeposited([]); });
|
|
2438
|
+
apiFetch("/api/dsh-mneme/list?archived=only&limit=3&order=chrono")
|
|
2439
|
+
.then((r) => (r.ok ? r.json() : { items: [], total: 0 }))
|
|
2440
|
+
.then((d) => {
|
|
2441
|
+
if (cancelled) return;
|
|
2442
|
+
setArchived(d.items || []);
|
|
2443
|
+
setArchivedTotal(d.total || 0);
|
|
2444
|
+
})
|
|
2392
2445
|
.catch(() => { if (!cancelled) setArchived([]); });
|
|
2393
2446
|
return () => { cancelled = true; };
|
|
2394
|
-
}, []);
|
|
2447
|
+
}, [wbReload]);
|
|
2395
2448
|
useEffect(() => { load(); }, [load]);
|
|
2396
2449
|
|
|
2397
2450
|
const restore = (id) => {
|
|
@@ -2402,7 +2455,10 @@ window.__ModuleLoader__.load({
|
|
|
2402
2455
|
body: JSON.stringify({ id, archived: false })
|
|
2403
2456
|
})
|
|
2404
2457
|
.then((r) => { if (!r.ok) throw new Error("http"); })
|
|
2405
|
-
.then(() =>
|
|
2458
|
+
.then(() => {
|
|
2459
|
+
setArchived((cur) => (cur || []).filter((m) => m.id !== id));
|
|
2460
|
+
setArchivedTotal((n) => Math.max(0, n - 1));
|
|
2461
|
+
})
|
|
2406
2462
|
.catch(() => {})
|
|
2407
2463
|
.finally(() => setRestoring(""));
|
|
2408
2464
|
};
|
|
@@ -2434,8 +2490,14 @@ window.__ModuleLoader__.load({
|
|
|
2434
2490
|
row.error_message ? ` · ${row.error_message}` : "")
|
|
2435
2491
|
));
|
|
2436
2492
|
})),
|
|
2437
|
-
deposited !== null &&
|
|
2438
|
-
h("div", { className: "mneme-wbhead" },
|
|
2493
|
+
deposited !== null && depositedTotal > 0 && h(react.Fragment, null,
|
|
2494
|
+
h("div", { className: "mneme-wbhead" },
|
|
2495
|
+
h("span", null, t("memory.status.depositedCount").replace("{n}", String(depositedTotal))),
|
|
2496
|
+
onBrowse && depositedTotal > deposited.length && h("button", {
|
|
2497
|
+
type: "button",
|
|
2498
|
+
className: "mneme-footbtn",
|
|
2499
|
+
onClick: () => onBrowse({ deposited: true })
|
|
2500
|
+
}, t("memory.status.viewAll"))),
|
|
2439
2501
|
h("div", { className: "mneme-wblist" },
|
|
2440
2502
|
deposited.map((m) => h("div", { className: "mneme-wbmemo", key: m.id },
|
|
2441
2503
|
h("span", { className: "mneme-xdot", style: { color: memoryTypeColor(m.type) }, "aria-hidden": "true" }),
|
|
@@ -2445,7 +2507,13 @@ window.__ModuleLoader__.load({
|
|
|
2445
2507
|
`${typeLabel(t, m.type)} · ${formatRelativeTime(m.updated_at || m.created_at, t)}`))
|
|
2446
2508
|
)))),
|
|
2447
2509
|
archived !== null && h(react.Fragment, null,
|
|
2448
|
-
h("div", { className: "mneme-wbhead" },
|
|
2510
|
+
h("div", { className: "mneme-wbhead" },
|
|
2511
|
+
h("span", null, t("memory.status.archivedCount").replace("{n}", String(archivedTotal))),
|
|
2512
|
+
onBrowse && archivedTotal > archived.length && h("button", {
|
|
2513
|
+
type: "button",
|
|
2514
|
+
className: "mneme-footbtn",
|
|
2515
|
+
onClick: () => onBrowse({ archived: true })
|
|
2516
|
+
}, t("memory.status.viewAll"))),
|
|
2449
2517
|
archived.length === 0
|
|
2450
2518
|
? h("div", { className: "mneme-featsubhint" }, t("memory.status.archivedEmpty"))
|
|
2451
2519
|
: h("div", { className: "mneme-wblist" },
|
|
@@ -2465,7 +2533,7 @@ window.__ModuleLoader__.load({
|
|
|
2465
2533
|
);
|
|
2466
2534
|
}
|
|
2467
2535
|
|
|
2468
|
-
function StatusPanel({ t }) {
|
|
2536
|
+
function StatusPanel({ t, onBrowse }) {
|
|
2469
2537
|
return h("div", { className: "mneme-status" },
|
|
2470
2538
|
h("div", { className: "mneme-statusgrid" },
|
|
2471
2539
|
h(MemoriesStatusCard, { t }),
|
|
@@ -2474,7 +2542,7 @@ window.__ModuleLoader__.load({
|
|
|
2474
2542
|
h(LlmStatusCard, { t }),
|
|
2475
2543
|
h(DreamStatusCards, { t })
|
|
2476
2544
|
),
|
|
2477
|
-
h(WorkbenchSection, { t })
|
|
2545
|
+
h(WorkbenchSection, { t, onBrowse })
|
|
2478
2546
|
);
|
|
2479
2547
|
}
|
|
2480
2548
|
|
|
@@ -2560,7 +2628,7 @@ window.__ModuleLoader__.load({
|
|
|
2560
2628
|
value: importance,
|
|
2561
2629
|
onChange: (e) => setImportance(Number(e.target.value))
|
|
2562
2630
|
}, [1, 2, 3, 4, 5].map((n) => h("option", { key: n, value: n }, "★".repeat(n))))
|
|
2563
|
-
: h(
|
|
2631
|
+
: h(ImportanceStars, { className: "mneme-dmetaval", value: memory.importance || 0 }),
|
|
2564
2632
|
memory.source && h(react.Fragment, null,
|
|
2565
2633
|
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.source")),
|
|
2566
2634
|
h("span", { className: "mneme-dmetaval", title: memory.source }, memory.source)),
|
|
@@ -2610,8 +2678,11 @@ window.__ModuleLoader__.load({
|
|
|
2610
2678
|
)
|
|
2611
2679
|
: h(react.Fragment, null,
|
|
2612
2680
|
h("button", { type: "button", className: "mneme-btn", onClick: () => setEditing(true) }, t("memory.explorer.detail.edit")),
|
|
2613
|
-
|
|
2614
|
-
|
|
2681
|
+
memory.archived
|
|
2682
|
+
? h("button", { type: "button", className: "mneme-btn", disabled: busy, onClick: () => postUpdate({ archived: false }, { restored: true }) },
|
|
2683
|
+
t("memory.explorer.detail.restore"))
|
|
2684
|
+
: h("button", { type: "button", className: "mneme-btn", disabled: busy, onClick: () => postUpdate({ archived: true }, { archived: true }) },
|
|
2685
|
+
t("memory.explorer.detail.archive")),
|
|
2615
2686
|
h("button", {
|
|
2616
2687
|
type: "button",
|
|
2617
2688
|
className: "mneme-btn",
|
|
@@ -2724,6 +2795,9 @@ window.__ModuleLoader__.load({
|
|
|
2724
2795
|
catch { return "timeline"; }
|
|
2725
2796
|
});
|
|
2726
2797
|
const [dateRange, setDateRange] = useState("all"); // all | 7d | 30d | 90d
|
|
2798
|
+
// 方案 A:沉淀/归档筛选 chip——状态页「查看全部」也会带着它们跳转过来。
|
|
2799
|
+
const [depositedOnly, setDepositedOnly] = useState(false); // 只看 autoDream 巩固
|
|
2800
|
+
const [archivedOnly, setArchivedOnly] = useState(false); // 只看已归档
|
|
2727
2801
|
const [pendingIds, setPendingIds] = useState(() => new Set());
|
|
2728
2802
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
2729
2803
|
const [importOpen, setImportOpen] = useState(false);
|
|
@@ -2762,7 +2836,9 @@ window.__ModuleLoader__.load({
|
|
|
2762
2836
|
).toISOString();
|
|
2763
2837
|
const filterQS = (type === "all" ? "" : `&type=${encodeURIComponent(type)}`)
|
|
2764
2838
|
+ (minImp ? `&minImportance=${minImp}` : "")
|
|
2765
|
-
+ (dateFromIso ? `&updatedFrom=${encodeURIComponent(dateFromIso)}` : "")
|
|
2839
|
+
+ (dateFromIso ? `&updatedFrom=${encodeURIComponent(dateFromIso)}` : "")
|
|
2840
|
+
+ (depositedOnly ? "&deposited=only" : "")
|
|
2841
|
+
+ (archivedOnly ? "&archived=only" : "");
|
|
2766
2842
|
const listUrl = (offset) => `/api/dsh-mneme/list?limit=${PAGE_SIZE}&offset=${offset}${filterQS}&order=chrono`;
|
|
2767
2843
|
|
|
2768
2844
|
// Browse = paged, pure-chronological pages (order=chrono). The default
|
|
@@ -2948,7 +3024,7 @@ window.__ModuleLoader__.load({
|
|
|
2948
3024
|
|
|
2949
3025
|
// 编辑保存 / 归档的本地落账:替换或移除对应行;镜像由后端重渲染。
|
|
2950
3026
|
// 归档从所有视图移除(列表默认只看未归档),并清空选中。
|
|
2951
|
-
const applyMemoryUpdate = (updated, { archived = false } = {}) => {
|
|
3027
|
+
const applyMemoryUpdate = (updated, { archived = false, restored = false } = {}) => {
|
|
2952
3028
|
if (archived) {
|
|
2953
3029
|
setItems((cur) => cur.filter((m) => m.id !== updated.id));
|
|
2954
3030
|
setRemoteItems((cur) => (cur ? cur.filter((m) => m.id !== updated.id) : cur));
|
|
@@ -2958,6 +3034,17 @@ window.__ModuleLoader__.load({
|
|
|
2958
3034
|
setTimeout(() => setToastMsg(""), 2500);
|
|
2959
3035
|
return;
|
|
2960
3036
|
}
|
|
3037
|
+
if (restored) {
|
|
3038
|
+
// 恢复:行离开当前视图(抽屉只能从归档浏览列表打开它),回主列表
|
|
3039
|
+
// 由下一次筛选切换/刷新自然带出。
|
|
3040
|
+
setItems((cur) => cur.filter((m) => m.id !== updated.id));
|
|
3041
|
+
setRemoteItems((cur) => (cur ? cur.filter((m) => m.id !== updated.id) : cur));
|
|
3042
|
+
setTotal((n) => Math.max(0, n - 1));
|
|
3043
|
+
setSelectedId(null);
|
|
3044
|
+
setToastMsg(t("memory.explorer.detail.restored"));
|
|
3045
|
+
setTimeout(() => setToastMsg(""), 2500);
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
2961
3048
|
const replace = (m) => (m.id === updated.id ? updated : m);
|
|
2962
3049
|
setItems((cur) => cur.map(replace));
|
|
2963
3050
|
setRemoteItems((cur) => (cur ? cur.map(replace) : cur));
|
|
@@ -3003,6 +3090,16 @@ window.__ModuleLoader__.load({
|
|
|
3003
3090
|
setView("entity");
|
|
3004
3091
|
};
|
|
3005
3092
|
|
|
3093
|
+
// 状态页「查看全部」入口:跳回记忆库并预置对应筛选(清搜索态,避免
|
|
3094
|
+
// remoteItems 盖过浏览列表)。
|
|
3095
|
+
const browseWithFilter = (patch) => {
|
|
3096
|
+
setDepositedOnly(!!patch.deposited);
|
|
3097
|
+
setArchivedOnly(!!patch.archived);
|
|
3098
|
+
setQuery("");
|
|
3099
|
+
setRemoteItems(null);
|
|
3100
|
+
setView("memory");
|
|
3101
|
+
};
|
|
3102
|
+
|
|
3006
3103
|
const subviews = [
|
|
3007
3104
|
{ key: "memory", label: t("memory.explorer.tabMemory") },
|
|
3008
3105
|
{ key: "entity", label: t("memory.explorer.tabEntities") },
|
|
@@ -3121,6 +3218,19 @@ window.__ModuleLoader__.load({
|
|
|
3121
3218
|
onClick: () => setMinImp(v)
|
|
3122
3219
|
}, v === 0 ? t("memory.tab.all") : `★${v}+`))
|
|
3123
3220
|
),
|
|
3221
|
+
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.sourceFilter")),
|
|
3222
|
+
h("div", { className: "mneme-xrow" },
|
|
3223
|
+
h("button", {
|
|
3224
|
+
className: depositedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
3225
|
+
title: t("memory.explorer.filterDepositedHint"),
|
|
3226
|
+
onClick: () => setDepositedOnly(!depositedOnly)
|
|
3227
|
+
}, t("memory.explorer.filterDeposited")),
|
|
3228
|
+
h("button", {
|
|
3229
|
+
className: archivedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
3230
|
+
title: t("memory.explorer.filterArchivedHint"),
|
|
3231
|
+
onClick: () => setArchivedOnly(!archivedOnly)
|
|
3232
|
+
}, t("memory.explorer.filterArchived"))
|
|
3233
|
+
),
|
|
3124
3234
|
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.types")),
|
|
3125
3235
|
h("button", {
|
|
3126
3236
|
className: type === "all" ? "mneme-xtype mneme-active" : "mneme-xtype",
|
|
@@ -3160,7 +3270,7 @@ window.__ModuleLoader__.load({
|
|
|
3160
3270
|
h("div", { className: "mneme-cardtitle" }, m.title || (m.content || "").slice(0, 60)),
|
|
3161
3271
|
h("div", { className: "mneme-cardexcerpt" }, m.content || ""),
|
|
3162
3272
|
h("div", { className: "mneme-cardfoot" },
|
|
3163
|
-
h(
|
|
3273
|
+
h(ImportanceStars, { value: m.importance || 0, size: 12 }),
|
|
3164
3274
|
m.source && h("span", { className: "mneme-cardsrc", title: m.source }, m.source)
|
|
3165
3275
|
)
|
|
3166
3276
|
)),
|
|
@@ -3253,7 +3363,7 @@ window.__ModuleLoader__.load({
|
|
|
3253
3363
|
})
|
|
3254
3364
|
),
|
|
3255
3365
|
view === "entity" && h(EntityPanel, { t, focusEntity: graphFocus, onJumpMemory: jumpToMemory }),
|
|
3256
|
-
view === "status" && h(StatusPanel, { t }),
|
|
3366
|
+
view === "status" && h(StatusPanel, { t, onBrowse: browseWithFilter }),
|
|
3257
3367
|
view === "settings" && h("div", { className: "mneme-set" },
|
|
3258
3368
|
h("div", { className: "mneme-set-inner" }, h(SettingsContent, { t }))
|
|
3259
3369
|
),
|
|
@@ -3287,19 +3397,34 @@ window.__ModuleLoader__.load({
|
|
|
3287
3397
|
const [nativeCls, setNativeCls] = useState("");
|
|
3288
3398
|
useEffect(() => {
|
|
3289
3399
|
if (!reactDom || typeof document === "undefined") return undefined;
|
|
3290
|
-
let tries = 0, timer = null, created = null;
|
|
3400
|
+
let tries = 0, timer = null, created = null, mo = null;
|
|
3401
|
+
const findNative = (region) =>
|
|
3402
|
+
region.parentElement.querySelector('[class*="newSession"]')
|
|
3403
|
+
|| region.previousElementSibling;
|
|
3291
3404
|
const attempt = () => {
|
|
3292
3405
|
const region = document.querySelector('[class*="regionArea"]');
|
|
3293
3406
|
if (region && region.parentElement) {
|
|
3294
3407
|
// 「新会话」按钮与 regionArea 同级且紧邻其前,借它的类名获得
|
|
3295
3408
|
// 与原生侧边栏项完全一致的盒模型(含收起态 rail 几何)。
|
|
3296
|
-
|
|
3297
|
-
|
|
3409
|
+
// 宿主/皮肤会异步改写按钮类名,用 MutationObserver 持续同步,
|
|
3410
|
+
// 否则 portal 按钮会停留在捕获时刻的旧类上(宽度/对齐失配)。
|
|
3298
3411
|
created = document.createElement("div");
|
|
3299
3412
|
created.dataset.pluginEntry = "@modusensus/dsh-mneme";
|
|
3300
3413
|
region.parentElement.insertBefore(created, region);
|
|
3301
3414
|
setHost(created);
|
|
3302
|
-
setNativeCls(
|
|
3415
|
+
setNativeCls(findNative(region)?.className || "");
|
|
3416
|
+
mo = new MutationObserver(() => {
|
|
3417
|
+
const cur = document.querySelector('[class*="regionArea"]');
|
|
3418
|
+
if (!cur || !cur.parentElement) return;
|
|
3419
|
+
const cls = findNative(cur)?.className || "";
|
|
3420
|
+
setNativeCls((prev) => (prev === cls ? prev : cls));
|
|
3421
|
+
});
|
|
3422
|
+
mo.observe(region.parentElement, {
|
|
3423
|
+
attributes: true,
|
|
3424
|
+
attributeFilter: ["class"],
|
|
3425
|
+
childList: true,
|
|
3426
|
+
subtree: true
|
|
3427
|
+
});
|
|
3303
3428
|
return;
|
|
3304
3429
|
}
|
|
3305
3430
|
if (++tries > 40) return; // 放弃 portal,footer 回退保持可用
|
|
@@ -3308,6 +3433,7 @@ window.__ModuleLoader__.load({
|
|
|
3308
3433
|
attempt();
|
|
3309
3434
|
return () => {
|
|
3310
3435
|
clearTimeout(timer);
|
|
3436
|
+
if (mo) mo.disconnect();
|
|
3311
3437
|
if (created && created.parentElement) created.parentElement.removeChild(created);
|
|
3312
3438
|
};
|
|
3313
3439
|
}, []);
|
|
@@ -3330,6 +3456,7 @@ window.__ModuleLoader__.load({
|
|
|
3330
3456
|
}
|
|
3331
3457
|
|
|
3332
3458
|
function apply(ctx) {
|
|
3459
|
+
const t = ctx.locale.bind(NS);
|
|
3333
3460
|
ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-mneme: dictionaries");
|
|
3334
3461
|
|
|
3335
3462
|
// 记忆库唯一入口:sidebar.footer.action 注册作为锚点与回退;真实按钮
|
|
@@ -3338,7 +3465,6 @@ window.__ModuleLoader__.load({
|
|
|
3338
3465
|
// v0.7.15 起不再注册 conversation.view tab——对话内嵌的面板被悬浮
|
|
3339
3466
|
// 输入框遮挡、挤压会话布局,sheet 页是更舒服的承载方式。
|
|
3340
3467
|
ctx.slots.inject("sidebar.footer.action", () => {
|
|
3341
|
-
const t = ctx.locale.bind(NS);
|
|
3342
3468
|
return ctx.slots.register({
|
|
3343
3469
|
name: "sidebar.footer.action",
|
|
3344
3470
|
id: "dsh-mneme",
|
|
@@ -3353,6 +3479,40 @@ window.__ModuleLoader__.load({
|
|
|
3353
3479
|
h(MemoryOverlay, { t })
|
|
3354
3480
|
));
|
|
3355
3481
|
});
|
|
3482
|
+
|
|
3483
|
+
// --- better-sidebar 生态 tab(可选软依赖,官方指南 §2.2 契约)---
|
|
3484
|
+
// 'betterSidebar' 声明进 inject + package.json optional peer:未安装
|
|
3485
|
+
// better-sidebar 时服务解析为 undefined,探测条件不成立即静默跳过,
|
|
3486
|
+
// 现有 footer 锚点 + 顶部入口 + sheet 完全不受影响(实测:DSH 运行时
|
|
3487
|
+
// 对 ctx 属性访问有 inject 闸门,未声明就取值会直接 fail 整个 loader
|
|
3488
|
+
// entry,§15 的「不声明纯探测」模式在 DSH 上不可用)。
|
|
3489
|
+
// 服务提供时序仍无保证(better-sidebar 可能晚于本插件加载),短重试
|
|
3490
|
+
// 兜底;注册包在 ctx.effect 里,HMR/禁用时随作用域自动清理。
|
|
3491
|
+
ctx.effect(() => {
|
|
3492
|
+
let tries = 0, timer = null;
|
|
3493
|
+
const attempt = () => {
|
|
3494
|
+
const bs = ctx.betterSidebar;
|
|
3495
|
+
if (bs && typeof bs.registerTab === "function") {
|
|
3496
|
+
try {
|
|
3497
|
+
bs.registerTab({
|
|
3498
|
+
id: "dsh-mneme:memory",
|
|
3499
|
+
title: () => t("memory.view.label"),
|
|
3500
|
+
icon: (size) => h(IconArchiveOutline20, { size }),
|
|
3501
|
+
order: 60,
|
|
3502
|
+
component: () => h(MemoryExplorer, { t })
|
|
3503
|
+
});
|
|
3504
|
+
return;
|
|
3505
|
+
} catch (err) {
|
|
3506
|
+
// id 重复等注册失败不应拖垮其余功能,留一条线索即可
|
|
3507
|
+
console.error("[dsh-mneme] better-sidebar registerTab failed:", err);
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
if (++tries <= 10) timer = setTimeout(attempt, 1000);
|
|
3512
|
+
};
|
|
3513
|
+
attempt();
|
|
3514
|
+
return () => clearTimeout(timer);
|
|
3515
|
+
}, "dsh-mneme: better-sidebar tab");
|
|
3356
3516
|
}
|
|
3357
3517
|
|
|
3358
3518
|
exports.apply = apply;
|
package/lib/store.js
CHANGED
|
@@ -637,7 +637,7 @@ export function createStore(path) {
|
|
|
637
637
|
return ts;
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
-
function count(type, { minImportance = null, source = null, includeForgotten = false, includeArchived = false, onlyArchived = false, updatedFrom = null, updatedTo = null } = {}) {
|
|
640
|
+
function count(type, { minImportance = null, source = null, includeForgotten = false, includeArchived = false, onlyArchived = false, depositedOnly = false, updatedFrom = null, updatedTo = null } = {}) {
|
|
641
641
|
const clauses = [];
|
|
642
642
|
const params = [];
|
|
643
643
|
if (type !== undefined) {
|
|
@@ -673,6 +673,12 @@ export function createStore(path) {
|
|
|
673
673
|
} else if (!includeArchived) {
|
|
674
674
|
clauses.push("archived = 0");
|
|
675
675
|
}
|
|
676
|
+
// 与 list() 同过滤:deposited 视图的 total 才能和行保持一致。
|
|
677
|
+
if (depositedOnly) {
|
|
678
|
+
clauses.push(
|
|
679
|
+
"(id IN (SELECT record_id FROM receipt_chain WHERE kind IN ('merge', 'update') AND verdict = 'live') OR source = 'dream')"
|
|
680
|
+
);
|
|
681
|
+
}
|
|
676
682
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
677
683
|
return db.prepare(`SELECT count(*) AS c FROM memories ${where}`).get(...params).c;
|
|
678
684
|
}
|
|
@@ -924,7 +930,7 @@ export function createStore(path) {
|
|
|
924
930
|
return rows.map(toRow);
|
|
925
931
|
}
|
|
926
932
|
|
|
927
|
-
function list({ type, limit = 50, offset = 0, order = "importance", includeForgotten = false, includeArchived = false, onlyArchived = false, minImportance = null, source = null, updatedFrom = null, updatedTo = null } = {}) {
|
|
933
|
+
function list({ type, limit = 50, offset = 0, order = "importance", includeForgotten = false, includeArchived = false, onlyArchived = false, depositedOnly = false, minImportance = null, source = null, updatedFrom = null, updatedTo = null } = {}) {
|
|
928
934
|
const clauses = [];
|
|
929
935
|
const params = [];
|
|
930
936
|
if (type) {
|
|
@@ -962,6 +968,14 @@ export function createStore(path) {
|
|
|
962
968
|
} else if (!includeArchived) {
|
|
963
969
|
clauses.push("archived = 0");
|
|
964
970
|
}
|
|
971
|
+
// depositedOnly:只看 autoDream 巩固过的记忆——receipt_chain 的 merge /
|
|
972
|
+
// update live verdict(record_id 即保留/更新目标)∪ source="dream" 的
|
|
973
|
+
// 直写沉淀(记忆库总览)。conflict 不算沉淀:两侧只被仲裁,内容未落。
|
|
974
|
+
if (depositedOnly) {
|
|
975
|
+
clauses.push(
|
|
976
|
+
"(id IN (SELECT record_id FROM receipt_chain WHERE kind IN ('merge', 'update') AND verdict = 'live') OR source = 'dream')"
|
|
977
|
+
);
|
|
978
|
+
}
|
|
965
979
|
const { limit: lim, offset: off } = sanitizePage(limit, offset, 50);
|
|
966
980
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
967
981
|
// "chrono" is pure newest-first — the stable order paged browsing (month
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modusensus/dsh-mneme",
|
|
3
3
|
"description": "Cross-session memory plugin for DeepSeek Harness with autoDream consolidation: SQLite store, Markdown mirrors, 7 model tools, automatic injection, session summarization, user profile/rules, custom slash commands, vector (semantic) search, and a Web GUI panel",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.18",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"slots",
|
|
40
40
|
"locale",
|
|
41
41
|
"layout",
|
|
42
|
-
"connection"
|
|
42
|
+
"connection",
|
|
43
|
+
"betterSidebar"
|
|
43
44
|
],
|
|
44
45
|
"platform": "web"
|
|
45
46
|
},
|
|
@@ -53,7 +54,13 @@
|
|
|
53
54
|
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
54
55
|
"@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
|
|
55
56
|
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
|
|
56
|
-
"@deepseek-ai/schemastery": "^3.18.1"
|
|
57
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
58
|
+
"dsh-better-sidebar": "*"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"dsh-better-sidebar": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
57
64
|
},
|
|
58
65
|
"devDependencies": {
|
|
59
66
|
"@deepseek-ai/cordis": "^4.0.1",
|
package/src/api.js
CHANGED
|
@@ -178,7 +178,11 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
178
178
|
// archived=only:只看归档(状态页的归档列表用)。归档行不进默认列表,
|
|
179
179
|
// 所以这是独立的视图开关,而不是 includeArchived 的混看模式。
|
|
180
180
|
const onlyArchived = url.searchParams.get("archived") === "only";
|
|
181
|
-
|
|
181
|
+
// deposited=only:只看 autoDream 巩固过的记忆(receipt_chain 的
|
|
182
|
+
// merge/update verdict ∪ source=dream 直写)——状态页「查看全部」
|
|
183
|
+
// 与记忆库的「沉淀」筛选 chip 共用这个视图。
|
|
184
|
+
const depositedOnly = url.searchParams.get("deposited") === "only";
|
|
185
|
+
const rows = service.list({ type, limit, offset, order, minImportance, source, updatedFrom, updatedTo, onlyArchived, depositedOnly });
|
|
182
186
|
// 面板行在 wire DTO 之上补 archived/quality_score——模型工具的输出
|
|
183
187
|
// schema 严格复用 toApiList,扩展只发生在 HTTP 层。
|
|
184
188
|
const items = service.toApiList(rows).map((m, i) => ({
|
|
@@ -189,7 +193,7 @@ export function createApi(ctx, service, settings, commands, embedder, semantic =
|
|
|
189
193
|
// Total honors the same filters as the rows, or the pager's
|
|
190
194
|
// has-more math breaks whenever minImportance/source/updated-at
|
|
191
195
|
// bounds are active.
|
|
192
|
-
sendJson(res, 200, { items, total: service.count(type, { minImportance, source, updatedFrom, updatedTo, onlyArchived }) });
|
|
196
|
+
sendJson(res, 200, { items, total: service.count(type, { minImportance, source, updatedFrom, updatedTo, onlyArchived, depositedOnly }) });
|
|
193
197
|
} catch {
|
|
194
198
|
sendJson(res, 500, { error: "internal" });
|
|
195
199
|
}
|