dsh-prime-memory 0.20.0-beta.20 → 0.20.0-beta.22

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 CHANGED
@@ -17,6 +17,26 @@
17
17
  - **建议状态(`longtask-hint-get`)以纯端点提供**:判定 = 上下文占用达 `contextThresholdPct` 或 todo 漂移达 `driftThreshold`(部署配置可调);配合 `longtask-compress-tail` 作手动压缩入口。
18
18
  - **撤下浮动球 UI(`client/src/longtask-fab.ts`;beta.1 起曾随包发布)**:悬浮建议面板的归属本在 context-compression 侧(「建议启动压缩」是其领域),pm 属误载——2026-10-02 用户裁定撤下。撤下不违座位契约(settings.section 恰一个 + 输入栏 pill,body 单例不占座位),但全应用级 overlay 会与其他插件的悬浮 UI 抢占屏幕角落;长任务开关与手动压缩入口待长任务定版时落记忆设置分节。
19
19
  - **`session-mode-set` 的 `mode` 转为可选**:与 `longTask` 二选一必传——纯切长任务开关不再被迫发送档位(旧客户端恒传 mode,行为不变)。响应 `mode` 改回当前生效值。
20
+ ## [0.20.0-beta.22] — 2026-10-02
21
+
22
+ > 退场效果可见性 + Room 页退场筛选(实测反馈:退场确认后界面无变化、无从筛选)。
23
+
24
+ ### 修复/新增
25
+
26
+ - **退场效果立现**:Room 页展开条目只拉**活跃**记录(`retired:false`)——退场后展开区即显示「该 Room 暂无记忆条目」;退场确认后若该 room 正展开则**自动刷新**其条目;混排兜底:已退场条目显示 [已退场] 徽标并置灰。
27
+ - **退场筛选**:Room 页新增「全部 / 活跃 / 已退役」chips(词表退役态过滤)。
28
+ ## [0.20.0-beta.21] — 2026-10-02
29
+
30
+ > **退役带记录**:room 退役时可选**连同其下全部活跃记忆一并退场**(此前只有动词表,自生长 room 尤其没有处理手段)。
31
+
32
+ ### 新增
33
+
34
+ - **`retire withRecords`**(Room/Hall 页「退场」按钮,红色):词表退役 + 该 room 下全部**活跃**记录分批软删(每批 ≤200,与 records-delete 同原语;reason=manual)——记录可在记忆页「已退场」区逐条恢复。自生长未注册行同样有「退场」(仅退场记录,无词表条目可动)。响应带 `retiredRecords` 条数。
35
+ - 「导出孤儿」按钮回迁**记忆查询面板**(孤儿筛选行内)。
36
+
37
+ ### 修复
38
+
39
+ - recall-usage 测试隔离(持久化回环用例并行竞态);RoomTab 退役确认框的字面 `\n` 显示修正。
20
40
  ## [0.20.0-beta.20] — 2026-10-02
21
41
 
22
42
  > **Room 独立标签页**(与 Hall 分离):room 的存在、名称、条目各有其管理面;记忆页不再堆 Room 标签。
package/dist/client.js CHANGED
@@ -3565,7 +3565,36 @@ var __defProp = Object.defineProperty;
3565
3565
  }
3566
3566
  )
3567
3567
  ] }),
3568
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "Room 标签/分类管理:见「Hall」标签页。" })
3568
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3569
+ "button",
3570
+ {
3571
+ type: "button",
3572
+ title: "导出无 Room 绑定的孤儿清单 CSV(含候选)",
3573
+ onClick: () => {
3574
+ rpc("dsh-memory/rooms-export", { kind: "orphans" }).then((r) => {
3575
+ if (!r || !r.ok || !r.value?.csv) return;
3576
+ const blob = new Blob([r.value.csv], { type: "text/csv;charset=utf-8" });
3577
+ const url = URL.createObjectURL(blob);
3578
+ const a = document.createElement("a");
3579
+ a.href = url;
3580
+ a.download = "dsh-memory-room-orphans.csv";
3581
+ a.click();
3582
+ URL.revokeObjectURL(url);
3583
+ }).catch(() => void 0);
3584
+ },
3585
+ style: {
3586
+ cursor: "pointer",
3587
+ fontSize: 12,
3588
+ padding: "2px 8px",
3589
+ borderRadius: 6,
3590
+ border: "1px solid var(--dsh-mem-border)",
3591
+ background: "transparent",
3592
+ color: "var(--dsh-mem-text-2)"
3593
+ },
3594
+ children: "导出孤儿"
3595
+ }
3596
+ ),
3597
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: "Room 标签/分类管理:见「Hall」「Room」标签页。" })
3569
3598
  ] }),
3570
3599
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { style: { ...S.flexRow, marginBottom: 10 }, children: [
3571
3600
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: S.muted, children: loading ? "加载中…" : countText }),
@@ -4012,11 +4041,12 @@ var __defProp = Object.defineProperty;
4012
4041
  loadAll();
4013
4042
  } else setError(r && r.error ? r.error.message : "执行失败");
4014
4043
  });
4015
- const retire = (slug, active) => run(async () => {
4016
- if (!active && !window.confirm(`退役 Room:${slug}?
4017
-
4018
- 存量记录的 tags 不动,仅词表不再推荐;可随时恢复。`)) return;
4019
- const r = await rpc("dsh-memory/room-admin", { action: "retire", slug, active });
4044
+ const retire = (slug, active, withRecords = false) => run(async () => {
4045
+ if (!active) {
4046
+ const tip = withRecords ? `退役 Room:${slug} 并退场其下全部记忆?(记录为软删,可在记忆页「已退场」区逐条恢复)` : `退役 Room:${slug}?(存量记录的 tags 不动,仅词表不再推荐;可随时恢复)`;
4047
+ if (!window.confirm(tip)) return;
4048
+ }
4049
+ const r = await rpc("dsh-memory/room-admin", { action: "retire", slug, active, withRecords: withRecords || void 0 });
4020
4050
  if (r && r.ok) {
4021
4051
  setMsg(String(r.value?.notice ?? "完成"));
4022
4052
  loadAll();
@@ -4363,7 +4393,8 @@ var __defProp = Object.defineProperty;
4363
4393
  setForm({ action: "merge", from: e.slug, to: "" });
4364
4394
  setPreview(null);
4365
4395
  }, style: btn, children: "合并" }),
4366
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: retired ? "恢复该 Room 到词表" : "退役该 Room(存量 tags 不动)", onClick: () => retire(e.slug, retired), style: { ...btn, ...retired ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : { color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" } }, children: retired ? "恢复" : "退役" })
4396
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: retired ? "恢复该 Room 到词表" : "退役该 Room(存量 tags 不动)", onClick: () => retire(e.slug, retired), style: { ...btn, ...retired ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : { color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" } }, children: retired ? "恢复" : "退役" }),
4397
+ !retired ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "退役并退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => retire(e.slug, false, true), style: { ...btn, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "退场" }) : null
4367
4398
  ] }),
4368
4399
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(e.slug), style: btn, children: "导出" })
4369
4400
  ] })
@@ -4557,11 +4588,12 @@ var __defProp = Object.defineProperty;
4557
4588
  loadAll();
4558
4589
  } else setError(r && r.error ? r.error.message : "更新失败");
4559
4590
  });
4560
- const retire = (slug, active) => run(async () => {
4561
- if (!active && !window.confirm(`退役 Room:${slug}?
4562
-
4563
- 存量记录的 tags 不动,仅词表不再推荐;可随时恢复。`)) return;
4564
- const r = await rpc("dsh-memory/room-admin", { action: "retire", slug, active });
4591
+ const retire = (slug, active, withRecords = false) => run(async () => {
4592
+ if (!active) {
4593
+ const tip = withRecords ? `退役 Room:${slug} 并退场其下全部记忆?(记录为软删,可在记忆页「已退场」区逐条恢复)` : `退役 Room:${slug}?(存量记录的 tags 不动,仅词表不再推荐;可随时恢复)`;
4594
+ if (!window.confirm(tip)) return;
4595
+ }
4596
+ const r = await rpc("dsh-memory/room-admin", { action: "retire", slug, active, withRecords: withRecords || void 0 });
4565
4597
  if (r && r.ok) {
4566
4598
  setMsg(String(r.value?.notice ?? "完成"));
4567
4599
  loadAll();
@@ -4709,9 +4741,16 @@ var __defProp = Object.defineProperty;
4709
4741
  style: { ...btn2, ...retired ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : { color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" } },
4710
4742
  children: retired ? "恢复" : "退役"
4711
4743
  }
4712
- )
4744
+ ),
4745
+ !retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "退役并退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => retire(row2.slug, false, true), style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)" }, children: "退场" }) : null
4713
4746
  ] }),
4714
- grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown)", onClick: () => adopt(row2.slug), style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }) : null,
4747
+ grownOnly ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
4748
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown)", onClick: () => adopt(row2.slug), style: { ...btn2, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }),
4749
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "退场该 Room 下全部记忆(软删,可在「已退场」区恢复)", onClick: () => {
4750
+ setRenaming(null);
4751
+ retire(row2.slug, false, true);
4752
+ }, style: { ...btn2, color: "var(--dsh-mem-danger)", borderColor: "var(--dsh-mem-danger)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "退场" })
4753
+ ] }) : null,
4715
4754
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", disabled: busy, title: "导出该 Room 的记录清单 CSV", onClick: () => exportRecords(row2.slug), style: btn2, children: "导出" })
4716
4755
  ] })
4717
4756
  ] }),
@@ -4719,8 +4758,10 @@ var __defProp = Object.defineProperty;
4719
4758
  (records[row2.slug]?.items ?? []).map((it) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { style: { fontSize: 11, padding: "3px 0", borderBottom: "1px solid var(--dsh-mem-border)" }, children: [
4720
4759
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "dsh-mem-tag dsh-mem-tag-" + it.type, children: TYPE_LABELS[it.type] || it.type }),
4721
4760
  " ",
4761
+ it.retired ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { ...S.muted, color: "var(--dsh-mem-danger)" }, children: "[已退场]" }) : null,
4762
+ " ",
4722
4763
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: S.muted, children: it.updatedAt ? fmtTime(it.updatedAt) : "" }),
4723
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginTop: 2, wordBreak: "break-all", color: "var(--dsh-mem-text-2)" }, children: it.content.length > 140 ? it.content.slice(0, 140) + "…" : it.content })
4764
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { marginTop: 2, wordBreak: "break-all", color: "var(--dsh-mem-text-2)", opacity: it.retired ? 0.55 : 1 }, children: it.content.length > 140 ? it.content.slice(0, 140) + "…" : it.content })
4724
4765
  ] }, it.id)),
4725
4766
  (records[row2.slug]?.items.length ?? 0) === 0 ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: S.muted, children: "该 Room 暂无记忆条目。" }) : null,
4726
4767
  (records[row2.slug]?.total ?? 0) > (records[row2.slug]?.items.length ?? 0) ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { ...S.muted, fontSize: 11 }, children: "共 " + records[row2.slug].total + " 条,仅显示前 " + records[row2.slug].items.length + " 条" }) : null
@@ -574,6 +574,8 @@ export interface RoomAdminRequest {
574
574
  slug?: string;
575
575
  /** retire:true=恢复 active;缺省/false=退役。 */
576
576
  active?: boolean;
577
+ /** retire:同时把该 room 下全部**活跃**记录退场(软删,可在「已退场」区恢复)。 */
578
+ withRecords?: boolean;
577
579
  /** merge/rename:缺省 true 只返预览;实跑需显式 false。 */
578
580
  dryRun?: boolean;
579
581
  }
@@ -589,6 +591,8 @@ export interface RoomAdminResponse {
589
591
  hasMore?: boolean;
590
592
  /** 实跑时改写前备份文件名(rooms-merge-backups/)。 */
591
593
  backupFile?: string;
594
+ /** retire withRecords 实际退场的记录条数(软删,可恢复)。 */
595
+ retiredRecords?: number;
592
596
  }
593
597
  /** dsh-memory/wing-backfill(一键回填,后台任务;端点立即返回,进度以 wing-overview 轮询)。 */
594
598
  export interface WingBackfillResponse {
package/dist/stats.js CHANGED
@@ -549,12 +549,27 @@ export async function handleEndpoint(endpoint, payload, deps) {
549
549
  throw new Error('retire 需要 slug');
550
550
  const active = p.active === true;
551
551
  const changed = await roomRegistry.setStatus(slug, active ? 'active' : 'retired');
552
+ // 带记录退场(软删,可恢复):该 room 下全部**活跃**记录分批 retire(每批 ≤200,与 records-delete 同原语)
553
+ let retiredRecords = 0;
554
+ if (p.withRecords === true && !active) {
555
+ for (let offset = 0;; offset += 200) {
556
+ const page = stores.l1.list({ tag: slug, retired: false, limit: 200, offset });
557
+ if (page.items.length === 0)
558
+ break;
559
+ const n = stores.l1.retire(page.items.map((r) => r.id), { at: new Date().toISOString(), reason: 'manual' });
560
+ retiredRecords += n;
561
+ if (page.items.length < 200)
562
+ break;
563
+ }
564
+ deps.logger.info(`[memory] Room 退役带记录:${slug},退场 ${retiredRecords} 条(软删,可恢复)`);
565
+ }
552
566
  const v = {
567
+ retiredRecords: retiredRecords > 0 ? retiredRecords : undefined,
553
568
  notice: changed
554
569
  ? active
555
570
  ? `已恢复:${slug}`
556
- : `已退役:${slug}(存量记录的 tags 不动,仅词表不再推荐)。`
557
- : `未找到注册条目或状态无变化:${slug}(自生长 Room 无需退役,会随 tags 自然消失)。`,
571
+ : `已退役:${slug}(存量记录的 tags 不动,仅词表不再推荐)。${retiredRecords > 0 ? `已连同退场 ${retiredRecords} 条记录(软删,可在「已退场」区恢复)。` : ''}`
572
+ : `未找到注册条目或状态无变化:${slug}。${retiredRecords > 0 ? `已退场 ${retiredRecords} 条记录(软删,可恢复)。` : ''}`,
558
573
  };
559
574
  return v;
560
575
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prime-memory",
3
- "version": "0.20.0-beta.20",
3
+ "version": "0.20.0-beta.22",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",