dsh-prime-memory 0.16.1-beta.0 → 0.16.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/client.js +41 -4
- package/dist/contract.d.ts +6 -0
- package/dist/stats.js +6 -1
- package/dist/store/l1.d.ts +2 -0
- package/dist/store/sqlite.d.ts +9 -0
- package/dist/store/sqlite.js +8 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -58,6 +58,10 @@
|
|
|
58
58
|
|
|
59
59
|
## [0.16.1] — 2026-09-24
|
|
60
60
|
|
|
61
|
+
### 新增
|
|
62
|
+
|
|
63
|
+
- **记录面板「退场筛查」**:软删记录按设计不隐藏(可恢复),但与活跃记录混排不便分辨——现在列表工具行加了三态筛选「**全部 / 仅活跃 / 仅退场**」。后端 `listL1` 补同口径三态过滤(缺省全量,快照/重建不受影响;`retired:true` 与 `listRetiredL1` 同一判据,两份视图看到同一批行);契约 `ListRecordsRequest.retired?: boolean`。筛查仅浏览路径生效——关键词检索只覆盖检索面,已退场记录本就不在其中。「加载更多」与删除/恢复后的自动刷新都会保持当前筛查态。
|
|
64
|
+
|
|
61
65
|
### 修复
|
|
62
66
|
|
|
63
67
|
- **面板「删除记忆」确认后看似毫无反应——退场态没有透传给 UI。** 软删(retire)在服务端一直正常(`valid_to` 闭合 + 撤出检索面 + 可恢复),但按设计活动列表**保留**退场记录(`l1-retire` 测试钉死「面板浏览路径不隐藏退场记录」),而 `UiRecord` 契约里根本没有"是否已退场"字段、活动列表的记录行也没有任何视觉差异,"已退场"区又默认折叠——用户确认删除后看到的是**一条一模一样的记录**,自然以为"删除没生效"。
|
package/dist/client.js
CHANGED
|
@@ -3226,6 +3226,11 @@ var __defProp = Object.defineProperty;
|
|
|
3226
3226
|
// client/src/tabs/RecordsTab.tsx
|
|
3227
3227
|
var import_react15 = require("react");
|
|
3228
3228
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
3229
|
+
function retiredSelOf(r) {
|
|
3230
|
+
if (r === "active") return false;
|
|
3231
|
+
if (r === "retired") return true;
|
|
3232
|
+
return void 0;
|
|
3233
|
+
}
|
|
3229
3234
|
var TYPE_CHOICES = [
|
|
3230
3235
|
"persona",
|
|
3231
3236
|
"episodic",
|
|
@@ -3255,9 +3260,10 @@ var __defProp = Object.defineProperty;
|
|
|
3255
3260
|
const [sceneFilter, setSceneFilter] = (0, import_react15.useState)("");
|
|
3256
3261
|
const [hallFilter, setHallFilter] = (0, import_react15.useState)([]);
|
|
3257
3262
|
const [tagFilter, setTagFilter] = (0, import_react15.useState)("");
|
|
3263
|
+
const [retiredFilter, setRetiredFilter] = (0, import_react15.useState)("");
|
|
3258
3264
|
const [rooms, setRooms] = (0, import_react15.useState)([]);
|
|
3259
3265
|
const [wingCatalog, setHallCatalog] = (0, import_react15.useState)(null);
|
|
3260
|
-
const [last, setLast] = (0, import_react15.useState)({ query: "", type: "", scene: "", halls: [], tag: "" });
|
|
3266
|
+
const [last, setLast] = (0, import_react15.useState)({ query: "", type: "", scene: "", halls: [], tag: "", retired: "" });
|
|
3261
3267
|
const seqRef = (0, import_react15.useRef)(0);
|
|
3262
3268
|
const fetchPage = (0, import_react15.useCallback)(
|
|
3263
3269
|
(conds, offset, append) => {
|
|
@@ -3270,6 +3276,8 @@ var __defProp = Object.defineProperty;
|
|
|
3270
3276
|
if (conds.scene) payload.scene = conds.scene;
|
|
3271
3277
|
if (conds.halls.length > 0) payload.halls = conds.halls;
|
|
3272
3278
|
if (conds.tag) payload.tag = conds.tag;
|
|
3279
|
+
const retiredSel = retiredSelOf(conds.retired);
|
|
3280
|
+
if (retiredSel !== void 0) payload.retired = retiredSel;
|
|
3273
3281
|
rpc("dsh-memory/list-records", payload).then((r) => {
|
|
3274
3282
|
if (token !== seqRef.current) return;
|
|
3275
3283
|
setLoading(false);
|
|
@@ -3294,7 +3302,13 @@ var __defProp = Object.defineProperty;
|
|
|
3294
3302
|
[rpc]
|
|
3295
3303
|
);
|
|
3296
3304
|
const search = () => {
|
|
3297
|
-
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter };
|
|
3305
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter, retired: retiredFilter };
|
|
3306
|
+
setLast(conds);
|
|
3307
|
+
fetchPage(conds, 0, false);
|
|
3308
|
+
};
|
|
3309
|
+
const applyRetiredFilter = (next) => {
|
|
3310
|
+
setRetiredFilter(next);
|
|
3311
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: tagFilter, retired: next };
|
|
3298
3312
|
setLast(conds);
|
|
3299
3313
|
fetchPage(conds, 0, false);
|
|
3300
3314
|
};
|
|
@@ -3305,7 +3319,7 @@ var __defProp = Object.defineProperty;
|
|
|
3305
3319
|
});
|
|
3306
3320
|
}, [rpc]);
|
|
3307
3321
|
(0, import_react15.useEffect)(() => {
|
|
3308
|
-
fetchPage({ query: "", type: "", scene: "", halls: [], tag: "" }, 0, false);
|
|
3322
|
+
fetchPage({ query: "", type: "", scene: "", halls: [], tag: "", retired: "" }, 0, false);
|
|
3309
3323
|
loadRooms();
|
|
3310
3324
|
}, [fetchPage, loadRooms]);
|
|
3311
3325
|
const loadHiPriv = (0, import_react15.useCallback)(() => {
|
|
@@ -3486,7 +3500,7 @@ var __defProp = Object.defineProperty;
|
|
|
3486
3500
|
onClick: () => {
|
|
3487
3501
|
const next = on ? "" : r.room;
|
|
3488
3502
|
setTagFilter(next);
|
|
3489
|
-
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: next };
|
|
3503
|
+
const conds = { query: query.trim(), type: typeFilter, scene: sceneFilter, halls: hallFilter, tag: next, retired: retiredFilter };
|
|
3490
3504
|
setLast(conds);
|
|
3491
3505
|
fetchPage(conds, 0, false);
|
|
3492
3506
|
},
|
|
@@ -3508,6 +3522,29 @@ var __defProp = Object.defineProperty;
|
|
|
3508
3522
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { style: { ...S.muted, marginBottom: 10 }, children: "Room 分类:暂无(由反刍涌现的标签自动生成,无需手工建立)" }),
|
|
3509
3523
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
|
|
3510
3524
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
|
|
3525
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "退场筛查" }),
|
|
3526
|
+
[["", "全部"], ["active", "仅活跃"], ["retired", "仅退场"]].map(([val, label]) => {
|
|
3527
|
+
const on = retiredFilter === val;
|
|
3528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3529
|
+
"button",
|
|
3530
|
+
{
|
|
3531
|
+
type: "button",
|
|
3532
|
+
title: val === "" ? "活跃与已退场混排(已退场的带徽标)" : val === "active" ? "只看未退场的记忆" : "只看已退场(软删)的记忆,可在此恢复",
|
|
3533
|
+
onClick: () => applyRetiredFilter(val),
|
|
3534
|
+
style: {
|
|
3535
|
+
cursor: "pointer",
|
|
3536
|
+
fontSize: 12,
|
|
3537
|
+
padding: "2px 10px",
|
|
3538
|
+
borderRadius: 999,
|
|
3539
|
+
border: on ? "1px solid var(--dsh-mem-accent)" : "1px solid var(--dsh-mem-border)",
|
|
3540
|
+
background: on ? "var(--dsh-mem-bg-inset)" : "transparent",
|
|
3541
|
+
color: on ? "var(--dsh-mem-accent)" : "var(--dsh-mem-text-2)"
|
|
3542
|
+
},
|
|
3543
|
+
children: label
|
|
3544
|
+
},
|
|
3545
|
+
val
|
|
3546
|
+
);
|
|
3547
|
+
}),
|
|
3511
3548
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
3512
3549
|
NButton,
|
|
3513
3550
|
{
|
package/dist/contract.d.ts
CHANGED
|
@@ -650,6 +650,12 @@ export interface ListRecordsRequest {
|
|
|
650
650
|
* Room 由 tags 派生(自生长),故这里传的是**具体 tag 名**而非枚举 id。
|
|
651
651
|
*/
|
|
652
652
|
tag?: string;
|
|
653
|
+
/**
|
|
654
|
+
* 退场(软删)筛查:三态。省略 = 全部(活跃+已退场混排,靠 `UiRecord.retired`
|
|
655
|
+
* 徽标区分);`false` = 仅活跃;`true` = 仅已退场。
|
|
656
|
+
* **仅浏览路径生效**:关键词检索只覆盖检索面,已退场记录本就不在其中。
|
|
657
|
+
*/
|
|
658
|
+
retired?: boolean;
|
|
653
659
|
/** 1~200,默认 50。 */
|
|
654
660
|
limit?: number;
|
|
655
661
|
/** 0~1_000_000。 */
|
package/dist/stats.js
CHANGED
|
@@ -746,6 +746,9 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
746
746
|
const p = (payload ?? {});
|
|
747
747
|
if (p.query !== undefined && p.query.length > 4096)
|
|
748
748
|
throw new Error('query 过长(≤4096 字符)');
|
|
749
|
+
// 退场筛查:三态(缺省=全部混排/false=仅活跃/true=仅已退场)。显式布尔才透传,
|
|
750
|
+
// 其余值(字符串/数字等)一律视为缺省——宁可宽看,不可静默筛掉一半。
|
|
751
|
+
const retiredSel = typeof p.retired === 'boolean' ? p.retired : undefined;
|
|
749
752
|
// Room 过滤:tag 是**自生长**的 slug(无枚举),只做形状与长度校验(SQL 侧参数化)
|
|
750
753
|
const tagSel = typeof p.tag === 'string' ? p.tag.trim().slice(0, 64) : '';
|
|
751
754
|
if (tagSel && !isTag(tagSel))
|
|
@@ -803,7 +806,9 @@ export async function handleEndpoint(endpoint, payload, deps) {
|
|
|
803
806
|
};
|
|
804
807
|
return resp;
|
|
805
808
|
}
|
|
806
|
-
|
|
809
|
+
// 退场筛查仅浏览路径透传;检索路径不接——FTS/向量里本就没有已退场行,
|
|
810
|
+
// 接了只会让「仅退场+关键词」永远空结果,不如如实不筛。
|
|
811
|
+
const { items, total } = stores.l1.list({ type: p.type || undefined, scene: p.scene || undefined, hall: p.hall || undefined, halls: wingSel.length > 0 ? wingSel : undefined, tag: tagSel || undefined, retired: retiredSel, limit, offset });
|
|
807
812
|
const resp = {
|
|
808
813
|
items: items.map(hitToUiRecord),
|
|
809
814
|
hasMore: offset + items.length < total,
|
package/dist/store/l1.d.ts
CHANGED
|
@@ -292,6 +292,8 @@ export declare class L1Store {
|
|
|
292
292
|
/** Room 过滤(metadata.tags 含该 slug)。 */
|
|
293
293
|
tag?: string;
|
|
294
294
|
workspaceId?: string;
|
|
295
|
+
/** 退场筛查:三态(`undefined` 全部 / `false` 仅活跃 / `true` 仅已退场)。 */
|
|
296
|
+
retired?: boolean;
|
|
295
297
|
limit: number;
|
|
296
298
|
offset: number;
|
|
297
299
|
}): {
|
package/dist/store/sqlite.d.ts
CHANGED
|
@@ -385,6 +385,15 @@ export declare class MemoryDb {
|
|
|
385
385
|
/** Room 过滤(metadata.tags 含该 slug)。 */
|
|
386
386
|
tag?: string;
|
|
387
387
|
workspaceId?: string;
|
|
388
|
+
/**
|
|
389
|
+
* 退场(软删)筛查:三态。
|
|
390
|
+
* - 省略(undefined):不过滤,活跃+已退场都列(快照导出/重建需要全量;面板「全部」同此);
|
|
391
|
+
* - `false`:只列**活跃**记录(`valid_to` 为空);
|
|
392
|
+
* - `true`:只列**已退场**记录(`valid_to` 非空)——与 `listRetiredL1` 同口径。
|
|
393
|
+
* 面板默认不过滤而是靠徽标区分(「退场了」与「根本没这条」必须能分辨),
|
|
394
|
+
* 筛查只是把混排的两种态**分开看**,不改变"浏览路径不隐藏退场记录"的设计。
|
|
395
|
+
*/
|
|
396
|
+
retired?: boolean;
|
|
388
397
|
limit: number;
|
|
389
398
|
offset: number;
|
|
390
399
|
}): {
|
package/dist/store/sqlite.js
CHANGED
|
@@ -1486,6 +1486,14 @@ export class MemoryDb {
|
|
|
1486
1486
|
where.push(`EXISTS (SELECT 1 FROM json_each(CASE WHEN json_valid(metadata_json) THEN metadata_json ELSE '{}' END, '$.tags') j WHERE j.value = ?)`);
|
|
1487
1487
|
params.push(opts.tag);
|
|
1488
1488
|
}
|
|
1489
|
+
// 退场筛查(三态,见 opts.retired 注释):与 listRetiredL1 同一判据,保证
|
|
1490
|
+
// 「仅退场」视图与「已退场」区看到同一批行。
|
|
1491
|
+
if (opts.retired === false) {
|
|
1492
|
+
where.push("COALESCE(valid_to, '') = ''");
|
|
1493
|
+
}
|
|
1494
|
+
else if (opts.retired === true) {
|
|
1495
|
+
where.push("COALESCE(valid_to, '') <> ''");
|
|
1496
|
+
}
|
|
1489
1497
|
const whereSql = where.length > 0 ? ` WHERE ${where.join(' AND ')}` : '';
|
|
1490
1498
|
const totalRow = this.db.prepare(`SELECT COUNT(*) AS n FROM l1_records${whereSql}`).get(...params);
|
|
1491
1499
|
const rows = this.db
|