dsh-prime-memory 0.20.0-beta.14 → 0.20.0-beta.16

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,25 @@
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.16] — 2026-10-02
21
+
22
+ > **自生长未注册 slug 直接归入 hall**:底部「自生长未注册」区的每个 chip 现在可拖到 hall 头上——收编 + 归为其子类一步完成。
23
+
24
+ ### 新增
25
+
26
+ - **grown chip 拖拽/点选归类**:把未注册 slug(如 `git-commits`)拖到 hall 头(或点 chip 上「归」按钮进入挑选态,再点目标 hall)→ dryRun 预览 → 确认实跑。语义 = **原地收编(from,source=grown)→ rename 到 `hall/子类`**:记录 tags 随迁、旧名进目标别名、条目 active 且不戴 ⭐(收编身份)。落回自己 hall 的两级 slug(如 `dsh-plugin/xxx`)= 原地收编。
27
+ - 组合顺序钉死在集成测试(markMered 对未注册 from 会早退——必须先收编 from 再 rename);execMerge 对未注册 from 自动补收编。
28
+ ## [0.20.0-beta.15] — 2026-10-02
29
+
30
+ > 归类实测反馈两连修:①改名后的条目误带「已退役」(会掉出标注词表);②HTML5 拖拽在部分 webview 不生效 → 新增**点选归类**主路径。
31
+
32
+ ### 修复
33
+
34
+ - **归类(改名)后条目误标 retired**:renameRoom 复用 merge 编排,`markMerged` 的"旧条目转退役"被带了过来——归完类的新 room 显示「已退役」并**掉出标注器词表**(listActive 过滤)。现 rename 收尾恢复先前状态(active 条目改名后仍 active);真 merge(并集语义)不受影响。红测试先行(unit + HTTP 面集成)。
35
+
36
+ ### 变更
37
+
38
+ - **点选归类为主路径**:点 room 行「归类」→ 进入挑选态(按钮变「选 hall…」,所有 hall 头虚线高亮)→ 点目标 hall 头 → 出 dryRun 预览 → 确认实跑。拖拽保留(支持的環境仍可用),但不再依赖。
20
39
  ## [0.20.0-beta.14] — 2026-10-02
21
40
 
22
41
  > 记忆卡片新增**老化权重**与**召回使用**展示,并开始为"老化权重效果统计"积累数据。口径(用户裁定):**进 topN(被注入上下文)才算被使用**;参与尝试未进 = 仅计尝试。
package/dist/client.js CHANGED
@@ -4043,6 +4043,7 @@ var __defProp = Object.defineProperty;
4043
4043
  const [preview, setPreview] = (0, import_react16.useState)(null);
4044
4044
  const [grownOpen, setGrownOpen] = (0, import_react16.useState)(false);
4045
4045
  const [dragOverHall, setDragOverHall] = (0, import_react16.useState)(null);
4046
+ const [picking, setPicking] = (0, import_react16.useState)(null);
4046
4047
  const loadAll = (0, import_react16.useCallback)(() => {
4047
4048
  rpc("dsh-memory/rooms-get", {}).then((r) => {
4048
4049
  if (r && r.ok) {
@@ -4131,7 +4132,12 @@ var __defProp = Object.defineProperty;
4131
4132
  return;
4132
4133
  }
4133
4134
  if (majorOf(from) === hall) {
4134
- setMsg(`${from} 已在 ${hall} 下。`);
4135
+ if (registry.some((e) => e.slug === from)) {
4136
+ setMsg(`${from} 已在 ${hall} 下。`);
4137
+ return;
4138
+ }
4139
+ setPicking(null);
4140
+ void adopt(from);
4135
4141
  return;
4136
4142
  }
4137
4143
  const f = { action: "rename", from, to: `${hall}/${suffixOf(from)}` };
@@ -4141,6 +4147,13 @@ var __defProp = Object.defineProperty;
4141
4147
  };
4142
4148
  const execMerge = () => run(async () => {
4143
4149
  if (!preview) return;
4150
+ if (preview.action === "rename" && !registry.some((e) => e.slug === preview.from)) {
4151
+ const rr = await rpc("dsh-memory/room-register", { slug: preview.from, source: "grown" });
4152
+ if (!rr || !rr.ok) {
4153
+ setError(rr && rr.error ? rr.error.message : "收编源失败");
4154
+ return;
4155
+ }
4156
+ }
4144
4157
  const r = await rpc("dsh-memory/room-admin", { action: preview.action, from: preview.from, to: preview.to, dryRun: false });
4145
4158
  if (r && r.ok) {
4146
4159
  setMsg(String(r.value?.notice ?? "完成"));
@@ -4306,16 +4319,23 @@ var __defProp = Object.defineProperty;
4306
4319
  const from = ev.dataTransfer.getData("text/dsh-room");
4307
4320
  if (from) beginReclass(from, g.hall);
4308
4321
  },
4322
+ onClick: () => {
4323
+ if (!picking || busy) return;
4324
+ const from = picking;
4325
+ setPicking(null);
4326
+ beginReclass(from, g.hall);
4327
+ },
4309
4328
  style: {
4310
4329
  display: "flex",
4311
4330
  alignItems: "center",
4312
4331
  gap: 8,
4313
4332
  padding: "5px 10px",
4314
4333
  background: "var(--dsh-mem-bg-inset)",
4315
- outline: dragOverHall === g.hall ? "2px dashed var(--dsh-mem-accent)" : void 0,
4316
- outlineOffset: dragOverHall === g.hall ? "-2px" : void 0
4334
+ outline: dragOverHall === g.hall || picking !== null ? "2px dashed var(--dsh-mem-accent)" : void 0,
4335
+ outlineOffset: dragOverHall === g.hall || picking !== null ? "-2px" : void 0,
4336
+ cursor: picking ? "pointer" : void 0
4317
4337
  },
4318
- title: hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
4338
+ title: picking ? "点击此 hall:把「" + picking + "」归入 " + g.hall + " 下(major/minor,预览后确认)" : hiPriv ? "拖入 room 归类到该 hall 下(major/minor;记录随迁,旧名进别名,预览后确认)" : void 0,
4319
4339
  children: [
4320
4340
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { fontSize: 12, fontWeight: 600 }, children: [
4321
4341
  g.hall,
@@ -4365,7 +4385,27 @@ var __defProp = Object.defineProperty;
4365
4385
  (e.aliases ?? []).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "别名: " + (e.aliases ?? []).join(", ") }) : null,
4366
4386
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: S.grow }),
4367
4387
  !hiPriv ? null : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
4368
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: "归类到已有 hall 下(major/minor;记录随迁)", onClick: () => beginReclass(e.slug, groups.find((gg) => gg.hall !== majorOf(e.slug))?.hall ?? ""), style: btn, children: "归类" }),
4388
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4389
+ "button",
4390
+ {
4391
+ type: "button",
4392
+ disabled: busy,
4393
+ title: picking === e.slug ? "再点一次取消挑选;然后点击目标 hall 头完成归类" : "归类到已有 hall 下:点击后选目标 hall(记录随迁,旧名进别名)",
4394
+ onClick: () => {
4395
+ if (picking === e.slug) {
4396
+ setPicking(null);
4397
+ return;
4398
+ }
4399
+ setForm({ action: "rename", from: e.slug, to: "" });
4400
+ setPreview(null);
4401
+ setPicking(e.slug);
4402
+ setMsg(null);
4403
+ setError(null);
4404
+ },
4405
+ style: { ...btn, ...picking === e.slug ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : {} },
4406
+ children: picking === e.slug ? "选 hall…" : "归类"
4407
+ }
4408
+ ),
4369
4409
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: busy, title: `改名(= merge 1:1 + 注册表改名)`, onClick: () => {
4370
4410
  setForm({ action: "rename", from: e.slug, to: "" });
4371
4411
  setPreview(null);
@@ -4434,8 +4474,41 @@ var __defProp = Object.defineProperty;
4434
4474
  ] }),
4435
4475
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: S.muted, children: "(只在 tags 里涌现,不在词表 —— 收编后标注器才会优先挂靠)" }),
4436
4476
  grownOpen ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", marginTop: 6 }, children: grownOnly.map((r) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 4 }, children: [
4437
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { ...btn, cursor: "default", borderRadius: 999, padding: "2px 8px" }, title: "自生长 slug(未注册)", children: r.room + " · " + r.count }),
4438
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown)", onClick: () => adopt(r.room), style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" })
4477
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4478
+ "span",
4479
+ {
4480
+ style: { ...btn, cursor: hiPriv && !busy ? "grab" : "default", borderRadius: 999, padding: "2px 8px" },
4481
+ draggable: hiPriv && !busy,
4482
+ onDragStart: (ev) => {
4483
+ ev.dataTransfer.setData("text/dsh-room", r.room);
4484
+ ev.dataTransfer.effectAllowed = "move";
4485
+ },
4486
+ title: hiPriv && !busy ? "自生长 slug(未注册)——拖到某个 hall 头上 = 收编并归为其子类" : "自生长 slug(未注册)",
4487
+ children: r.room + " · " + r.count
4488
+ }
4489
+ ),
4490
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", disabled: !hiPriv || busy, title: "收编进注册表(source=grown,原地)", onClick: () => adopt(r.room), style: { ...btn, color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)", opacity: !hiPriv || busy ? 0.5 : 1 }, children: "收编" }),
4491
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
4492
+ "button",
4493
+ {
4494
+ type: "button",
4495
+ disabled: !hiPriv || busy,
4496
+ title: "归类到已有 hall 下:点击后选目标 hall(收编+子类一步,记录随迁)",
4497
+ onClick: () => {
4498
+ if (picking === r.room) {
4499
+ setPicking(null);
4500
+ return;
4501
+ }
4502
+ setForm({ action: "rename", from: r.room, to: "" });
4503
+ setPreview(null);
4504
+ setPicking(r.room);
4505
+ setMsg(null);
4506
+ setError(null);
4507
+ },
4508
+ style: { ...btn, opacity: !hiPriv || busy ? 0.5 : 1, ...picking === r.room ? { color: "var(--dsh-mem-accent)", borderColor: "var(--dsh-mem-accent)" } : {} },
4509
+ children: picking === r.room ? "选 hall…" : "归"
4510
+ }
4511
+ )
4439
4512
  ] }, r.room)) }) : null
4440
4513
  ] }) : null
4441
4514
  ] });
@@ -65,6 +65,11 @@ export interface RoomMergeStore extends RoomReviewStore {
65
65
  export interface RoomRegistryMerge {
66
66
  markMerged(fromSlug: string, toSlug: string): Promise<boolean>;
67
67
  renameSlug(oldSlug: string, newSlug: string): Promise<boolean>;
68
+ /** rename 收尾恢复先前状态用(active 条目改名后不掉出标注词表)。 */
69
+ bySlug?(slug: string): {
70
+ status: 'active' | 'retired';
71
+ } | undefined;
72
+ setStatus?(slug: string, status: 'active' | 'retired'): Promise<boolean>;
68
73
  }
69
74
  export interface RoomCandidate {
70
75
  id: string;
@@ -153,7 +158,9 @@ export declare function mergeRoom(io: {
153
158
  dryRun?: boolean;
154
159
  cap?: number;
155
160
  }): Promise<MergeRoomResult>;
156
- /** rename = merge 1:1 + 注册表改名(冲突时抛错提示走 merge)。 */
161
+ /** rename = merge 1:1 + 注册表改名(冲突时抛错提示走 merge)。
162
+ * 归类语义:改名**不是退役**——merge 编排的 markMerged 会把旧条目置 retired,
163
+ * 这里在改名后恢复其先前状态(active 条目改名后仍 active,不掉出标注词表)。 */
157
164
  export declare function renameRoom(io: {
158
165
  l1: RoomMergeStore;
159
166
  registry: RoomRegistryMerge;
@@ -202,11 +202,17 @@ export async function mergeRoom(io, from, to, opts = {}) {
202
202
  roomMergeRunning = false;
203
203
  }
204
204
  }
205
- /** rename = merge 1:1 + 注册表改名(冲突时抛错提示走 merge)。 */
205
+ /** rename = merge 1:1 + 注册表改名(冲突时抛错提示走 merge)。
206
+ * 归类语义:改名**不是退役**——merge 编排的 markMerged 会把旧条目置 retired,
207
+ * 这里在改名后恢复其先前状态(active 条目改名后仍 active,不掉出标注词表)。 */
206
208
  export async function renameRoom(io, from, to, opts = {}) {
209
+ const priorStatus = io.registry.bySlug?.(from)?.status ?? 'active';
207
210
  const r = await mergeRoom(io, from, to, opts);
208
- if (!r.dryRun && r.applied > 0)
211
+ if (!r.dryRun && r.applied > 0) {
209
212
  await io.registry.renameSlug(from, to);
213
+ if (priorStatus === 'active')
214
+ await io.registry.setStatus?.(to, 'active');
215
+ }
210
216
  return r;
211
217
  }
212
218
  /** 下一条待复查(供 agent 逐个过):记录摘要 + 候选 + 现有 Room 词表 + 剩余数。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-prime-memory",
3
- "version": "0.20.0-beta.14",
3
+ "version": "0.20.0-beta.16",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org",
6
6
  "access": "public",