dsh-prime-memory 0.24.1 → 0.25.0

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/dist/client.js CHANGED
@@ -2078,7 +2078,7 @@ var __defProp = Object.defineProperty;
2078
2078
  const showInput = scope === "all" || scope === "input";
2079
2079
  const effNote = layers.map((l) => eff[l[0]] || "?").join(" / ");
2080
2080
  const rowLabel = (key) => key === "extract" ? "抽取输出" : key === "dedup" ? "去重输出" : key === "l2" ? "L2 输出" : "L3 输出";
2081
- const rowStyle2 = { display: "flex", alignItems: "center", gap: 8 };
2081
+ const rowStyle = { display: "flex", alignItems: "center", gap: 8 };
2082
2082
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
2083
2083
  showOutputs ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 12 }, children: [
2084
2084
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
@@ -2089,7 +2089,7 @@ var __defProp = Object.defineProperty;
2089
2089
  children: "输出预算"
2090
2090
  }
2091
2091
  ),
2092
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: layers.map((l) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle2, children: [
2092
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: layers.map((l) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
2093
2093
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: rowLabel(l[0]) }),
2094
2094
  inputBox(
2095
2095
  l[0],
@@ -2111,7 +2111,7 @@ var __defProp = Object.defineProperty;
2111
2111
  children: "输入预算"
2112
2112
  }
2113
2113
  ),
2114
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle2, children: [
2114
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: rowStyle, children: [
2115
2115
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { width: 68, flexShrink: 0, fontSize: 12, color: "var(--dsh-mem-text-2)" }, children: "单次输入" }),
2116
2116
  inputBox(
2117
2117
  "input",
@@ -4495,6 +4495,39 @@ var __defProp = Object.defineProperty;
4495
4495
  children: "用 " + m.usage.used + "/" + m.usage.attempts
4496
4496
  }
4497
4497
  ) : null,
4498
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4499
+ "span",
4500
+ {
4501
+ title: m.name && m.name.length > 0 ? "名称(名称蒸馏产物;投射文件的命名取它):" + m.name : "尚未命名(名称蒸馏未产出);投射回落 mem-" + m.id.slice(-8),
4502
+ style: {
4503
+ fontSize: 11,
4504
+ padding: "1px 6px",
4505
+ borderRadius: 999,
4506
+ whiteSpace: "nowrap",
4507
+ maxWidth: 220,
4508
+ overflow: "hidden",
4509
+ textOverflow: "ellipsis",
4510
+ border: "1px solid " + (m.name && m.name.length > 0 ? "var(--dsh-mem-border)" : "var(--dsh-mem-danger)"),
4511
+ color: m.name && m.name.length > 0 ? "var(--dsh-mem-text-2)" : "var(--dsh-mem-danger)"
4512
+ },
4513
+ children: "名称 " + (m.name && m.name.length > 0 ? m.name : "未命名")
4514
+ }
4515
+ ),
4516
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4517
+ "span",
4518
+ {
4519
+ title: m.scope === "workspace" ? "作用范围:指定工作区(仅归属工作区可见)" + (m.workspaceId ? ":" + m.workspaceId : "") : "作用范围:用户默认(global,任何工作区可见)",
4520
+ style: {
4521
+ fontSize: 11,
4522
+ padding: "1px 6px",
4523
+ borderRadius: 999,
4524
+ whiteSpace: "nowrap",
4525
+ border: "1px solid var(--dsh-mem-border)",
4526
+ color: "var(--dsh-mem-text-2)"
4527
+ },
4528
+ children: m.scope === "workspace" ? "工作区" : "用户默认"
4529
+ }
4530
+ ),
4498
4531
  m.score !== null && m.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: "相关度 " + Number(m.score).toFixed(2) }) : null,
4499
4532
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.grow }),
4500
4533
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: S.muted, children: fmtTime(m.updatedAt) }),
@@ -4523,7 +4556,7 @@ var __defProp = Object.defineProperty;
4523
4556
  ) : null
4524
4557
  ] }),
4525
4558
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.content, children: m.content }),
4526
- open ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.detail, children: "id: " + m.id + "\n情境: " + (m.scene || "-") + "\n版本: v" + m.version + "(去重合并次数 " + m.version + ")\n创建: " + fmtTime(m.createdAt) + "\n活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" + (m.sourceAnchors && m.sourceAnchors.length > 0 ? "来源锚点: " + m.sourceAnchors.join(", ") : "来源锚点: -") }) : null
4559
+ open ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: S.detail, children: "id: " + m.id + "\n名称: " + (m.name && m.name.length > 0 ? m.name : "(未命名,投射回落 mem-" + m.id.slice(-8) + ")") + "\n情境: " + (m.scene || "-") + "\n关键词: " + (m.keywords && m.keywords.length > 0 ? m.keywords.join(",") : "(暂无 · 预留检索轴)") + "\n作用范围: " + (m.scope === "workspace" ? "指定工作区" + (m.workspaceId ? ":" + m.workspaceId : "(未记录路径)") : "用户默认(global,任何工作区可见)") + "\n版本: v" + m.version + "(去重合并次数 " + m.version + ")\n创建: " + fmtTime(m.createdAt) + "\n活跃时间: " + (m.timestamps && m.timestamps.length > 0 ? m.timestamps.map(fmtTime).join(" → ") : "-") + "\n" + (m.sourceAnchors && m.sourceAnchors.length > 0 ? "来源锚点: " + m.sourceAnchors.join(", ") : "来源锚点: -") }) : null
4527
4560
  ]
4528
4561
  },
4529
4562
  m.id
@@ -5836,7 +5869,7 @@ var __defProp = Object.defineProperty;
5836
5869
  }
5837
5870
 
5838
5871
  // client/src/pill/MemoryModePill.tsx
5839
- var import_react26 = require("react");
5872
+ var import_react25 = require("react");
5840
5873
 
5841
5874
  // src/util/context-occupancy.ts
5842
5875
  var CONTEXT_METER_CIRCUMFERENCE = 34.55751918948772;
@@ -6152,7 +6185,7 @@ var __defProp = Object.defineProperty;
6152
6185
  }
6153
6186
 
6154
6187
  // client/src/pill/WingWheel.tsx
6155
- var import_react25 = require("react");
6188
+ var import_react24 = require("react");
6156
6189
 
6157
6190
  // client/src/pill/SessionInfoArea.tsx
6158
6191
  var import_react23 = require("react");
@@ -6263,101 +6296,8 @@ var __defProp = Object.defineProperty;
6263
6296
  ] });
6264
6297
  }
6265
6298
 
6266
- // client/src/pill/SessionRecalledArea.tsx
6267
- var import_react24 = require("react");
6268
- var import_jsx_runtime24 = require("react/jsx-runtime");
6269
- var SHOW_CAP = 6;
6270
- var POLL_MS2 = 4e3;
6271
- var MATURITY_LABEL = { high: "成熟·高", mid: "成熟·中", low: "成熟·低" };
6272
- function SessionRecalledArea(props) {
6273
- const rpc = props.rpc;
6274
- const sessionId = props.sessionId;
6275
- const [records, setRecords] = (0, import_react24.useState)(void 0);
6276
- (0, import_react24.useEffect)(() => {
6277
- if (!rpc || !sessionId) return void 0;
6278
- let alive = true;
6279
- let timer = null;
6280
- let seq = 0;
6281
- const tick = () => {
6282
- const token = ++seq;
6283
- rpc("dsh-memory/session-recalled", { sessionId }).then((r) => {
6284
- if (!alive || token !== seq) return;
6285
- if (r && r.ok && r.value) {
6286
- registerTypeCatalog(r.value.typeCatalog);
6287
- setRecords(r.value.records);
6288
- }
6289
- }).catch(() => {
6290
- }).then(() => {
6291
- if (alive) timer = setTimeout(tick, POLL_MS2);
6292
- });
6293
- };
6294
- tick();
6295
- return () => {
6296
- alive = false;
6297
- if (timer) clearTimeout(timer);
6298
- };
6299
- }, [rpc, sessionId]);
6300
- if (records === void 0) return null;
6301
- const rows = records.filter((r) => r.superseded !== true || r.retired === true);
6302
- const recentFirst = [...rows].reverse();
6303
- const shown = recentFirst.slice(0, SHOW_CAP);
6304
- const hidden = recentFirst.length - shown.length;
6305
- const allSuperseded = rows.length === 0 && records.length > 0;
6306
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "dsh-mem-sinfo", children: [
6307
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6308
- "div",
6309
- {
6310
- style: { fontSize: 11, color: "var(--dsh-mem-text-3)", marginBottom: 6 },
6311
- title: "本会话召回、且正文仍在上下文里的记忆(正文被摘要顶掉的已折叠不显示;清空会话才清除;不是常驻注入)",
6312
- children: "本会话召回 " + rows.length + " 条"
6313
- }
6314
- ),
6315
- allSuperseded ? (
6316
- // 不是“还没召回到”——那批确实注入过,只是正文已被摘要顶掉。说谎的空态比不显示更糟。
6317
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", lineHeight: "16px" }, children: "本会话注入过的 " + records.length + " 条记忆正文已被摘要取代" })
6318
- ) : records.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)", lineHeight: "16px" }, children: "还没召回到记忆(聊几轮后按相关性自动浮现)" }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
6319
- shown.map((r) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: rowStyle, title: rowTitle(r), children: [
6320
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6321
- "span",
6322
- {
6323
- className: tagClass(r.type),
6324
- style: { flexShrink: 0, whiteSpace: "nowrap" },
6325
- children: typeLabel(r.type)
6326
- }
6327
- ),
6328
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "dsh-mem-sinfo-val", style: r.retired === true ? STALE_TEXT_STYLE : textStyle, children: r.content }),
6329
- r.retired === true ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 10, color: "var(--dsh-mem-text-3)", whiteSpace: "nowrap", flexShrink: 0 }, children: "已判过时" }) : null,
6330
- r.maturity ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 10, color: "var(--dsh-mem-text-3)", whiteSpace: "nowrap", flexShrink: 0 }, children: MATURITY_LABEL[r.maturity] }) : null
6331
- ] }, r.id)),
6332
- hidden > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: "+ " + hidden + " 条更早召回(详见面板「记忆」页)" }) : null
6333
- ] })
6334
- ] });
6335
- }
6336
- var rowStyle = {
6337
- display: "flex",
6338
- alignItems: "flex-start",
6339
- gap: 6,
6340
- lineHeight: "16px"
6341
- };
6342
- var textStyle = {
6343
- fontSize: 12,
6344
- lineHeight: "16px",
6345
- fontWeight: 400,
6346
- minWidth: 0,
6347
- display: "-webkit-box",
6348
- WebkitLineClamp: 2,
6349
- WebkitBoxOrient: "vertical",
6350
- overflow: "hidden"
6351
- };
6352
- var STALE_TEXT_STYLE = { ...textStyle, textDecoration: "line-through" };
6353
- function rowTitle(r) {
6354
- const head = "[" + typeLabel(r.type) + "] " + r.content;
6355
- const sc = r.usageScenario && r.usageScenario.length > 0 ? "\n使用场景:" + r.usageScenario.join(";") : "";
6356
- return head + sc;
6357
- }
6358
-
6359
6299
  // client/src/pill/WingWheel.tsx
6360
- var import_jsx_runtime25 = require("react/jsx-runtime");
6300
+ var import_jsx_runtime24 = require("react/jsx-runtime");
6361
6301
  var SIZE = 196;
6362
6302
  var CENTER = SIZE / 2;
6363
6303
  var R = 64;
@@ -6400,17 +6340,19 @@ var __defProp = Object.defineProperty;
6400
6340
  function WingWheel(props) {
6401
6341
  ensureThemeStyle();
6402
6342
  const isOff = props.mode === "off";
6403
- const [overview, setOverview] = (0, import_react25.useState)(null);
6404
- const [backfillBusy, setBackfillBusy] = (0, import_react25.useState)(false);
6405
- const [localError, setLocalError] = (0, import_react25.useState)(null);
6406
- const timersRef = (0, import_react25.useRef)([]);
6407
- (0, import_react25.useEffect)(
6343
+ const [overview, setOverview] = (0, import_react24.useState)(null);
6344
+ const [backfillBusy, setBackfillBusy] = (0, import_react24.useState)(false);
6345
+ const [kwBusy, setKwBusy] = (0, import_react24.useState)(false);
6346
+ const [kwSummary, setKwSummary] = (0, import_react24.useState)(null);
6347
+ const [localError, setLocalError] = (0, import_react24.useState)(null);
6348
+ const timersRef = (0, import_react24.useRef)([]);
6349
+ (0, import_react24.useEffect)(
6408
6350
  () => () => {
6409
6351
  for (const t of timersRef.current) window.clearTimeout(t);
6410
6352
  },
6411
6353
  []
6412
6354
  );
6413
- (0, import_react25.useEffect)(() => {
6355
+ (0, import_react24.useEffect)(() => {
6414
6356
  let alive = true;
6415
6357
  props.rpc("dsh-memory/wing-overview", {}).then((r) => {
6416
6358
  if (!alive) return;
@@ -6456,28 +6398,80 @@ var __defProp = Object.defineProperty;
6456
6398
  finish();
6457
6399
  });
6458
6400
  };
6459
- const boxRef = (0, import_react25.useRef)(null);
6460
- const startRef = (0, import_react25.useRef)(null);
6461
- const movedRef = (0, import_react25.useRef)(false);
6462
- const downIndexRef = (0, import_react25.useRef)(null);
6463
- const dragTargetRef = (0, import_react25.useRef)(null);
6464
- const [drag, setDrag] = (0, import_react25.useState)(null);
6401
+ const keywordBackfill = () => {
6402
+ if (kwBusy) return;
6403
+ setKwBusy(true);
6404
+ setKwSummary(null);
6405
+ setLocalError(null);
6406
+ let polls = 0;
6407
+ const finish = () => setKwBusy(false);
6408
+ const showResult = (v) => {
6409
+ const fail = v.failed ?? 0;
6410
+ setKwSummary(`关键词回填完成:成功 ${v.updated ?? 0} 条${fail > 0 ? `,失败 ${fail} 条` : ""}`);
6411
+ };
6412
+ const failMsg = (m) => {
6413
+ setLocalError(m);
6414
+ finish();
6415
+ };
6416
+ const tick = () => {
6417
+ polls++;
6418
+ props.rpc("dsh-memory/keyword-backfill", {}).then((r) => {
6419
+ const v = r && r.ok ? r.value : null;
6420
+ if (!v) {
6421
+ failMsg("关键词回填失败");
6422
+ return;
6423
+ }
6424
+ if (!v.enabled) {
6425
+ failMsg("关键词回填未开启:请先在设置里打开 keyword.enabled");
6426
+ return;
6427
+ }
6428
+ if (!v.running) {
6429
+ showResult(v);
6430
+ finish();
6431
+ return;
6432
+ }
6433
+ if (polls >= 20) {
6434
+ showResult(v);
6435
+ finish();
6436
+ return;
6437
+ }
6438
+ timersRef.current.push(window.setTimeout(tick, 3e3));
6439
+ }).catch(() => failMsg("关键词回填失败"));
6440
+ };
6441
+ props.rpc("dsh-memory/keyword-backfill", {}).then((r) => {
6442
+ if (!r || !r.ok) {
6443
+ failMsg(r && r.error ? "关键词回填失败:" + r.error.message : "关键词回填失败");
6444
+ return;
6445
+ }
6446
+ if (!r.value || !r.value.enabled) {
6447
+ failMsg("关键词回填未开启:请先在设置里打开 keyword.enabled");
6448
+ return;
6449
+ }
6450
+ timersRef.current.push(window.setTimeout(tick, 3e3));
6451
+ }).catch((e) => failMsg("关键词回填失败:" + String(e && e.message || e)));
6452
+ };
6453
+ const boxRef = (0, import_react24.useRef)(null);
6454
+ const startRef = (0, import_react24.useRef)(null);
6455
+ const movedRef = (0, import_react24.useRef)(false);
6456
+ const downIndexRef = (0, import_react24.useRef)(null);
6457
+ const dragTargetRef = (0, import_react24.useRef)(null);
6458
+ const [drag, setDrag] = (0, import_react24.useState)(null);
6465
6459
  const lockedIndex = (() => {
6466
6460
  if (props.halls.length !== 1 || !overview) return null;
6467
6461
  const i = overview.corners.findIndex((c) => c.id === props.halls[0]);
6468
6462
  return i >= 0 ? i : null;
6469
6463
  })();
6470
6464
  const activeCorner = drag ? drag.index : lockedIndex;
6471
- const blockPosRef = (0, import_react25.useRef)(
6465
+ const blockPosRef = (0, import_react24.useRef)(
6472
6466
  activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER }
6473
6467
  );
6474
- const blockTargetRef = (0, import_react25.useRef)(blockPosRef.current);
6475
- const [blockPos, setBlockPos] = (0, import_react25.useState)(blockPosRef.current);
6476
- (0, import_react25.useEffect)(() => {
6468
+ const blockTargetRef = (0, import_react24.useRef)(blockPosRef.current);
6469
+ const [blockPos, setBlockPos] = (0, import_react24.useState)(blockPosRef.current);
6470
+ (0, import_react24.useEffect)(() => {
6477
6471
  blockTargetRef.current = activeCorner != null ? cornerXY(activeCorner) : { x: CENTER, y: CENTER };
6478
6472
  }, [activeCorner]);
6479
- const reducedMotionRef = (0, import_react25.useRef)(false);
6480
- (0, import_react25.useEffect)(() => {
6473
+ const reducedMotionRef = (0, import_react24.useRef)(false);
6474
+ (0, import_react24.useEffect)(() => {
6481
6475
  const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
6482
6476
  const apply2 = () => {
6483
6477
  reducedMotionRef.current = mq.matches;
@@ -6486,7 +6480,7 @@ var __defProp = Object.defineProperty;
6486
6480
  mq.addEventListener("change", apply2);
6487
6481
  return () => mq.removeEventListener("change", apply2);
6488
6482
  }, []);
6489
- (0, import_react25.useEffect)(() => {
6483
+ (0, import_react24.useEffect)(() => {
6490
6484
  let raf = 0;
6491
6485
  const loop = () => {
6492
6486
  const cur = blockPosRef.current;
@@ -6575,8 +6569,8 @@ var __defProp = Object.defineProperty;
6575
6569
  }).join(" ");
6576
6570
  const grayStyle = isOff ? { opacity: 0.45, pointerEvents: "none" } : {};
6577
6571
  const boundariesDisabled = props.halls.length === 0 || isOff;
6578
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { style: { width: SIZE }, children: [
6579
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6572
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { width: SIZE }, children: [
6573
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6580
6574
  "div",
6581
6575
  {
6582
6576
  ref: boxRef,
@@ -6586,7 +6580,7 @@ var __defProp = Object.defineProperty;
6586
6580
  onPointerUp: onBoxUp,
6587
6581
  onPointerCancel: onBoxUp,
6588
6582
  children: [
6589
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6583
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6590
6584
  "svg",
6591
6585
  {
6592
6586
  width: SIZE,
@@ -6594,7 +6588,7 @@ var __defProp = Object.defineProperty;
6594
6588
  style: { position: "absolute", inset: 0, pointerEvents: "none" },
6595
6589
  "aria-hidden": "true",
6596
6590
  children: [
6597
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6591
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6598
6592
  "polygon",
6599
6593
  {
6600
6594
  points: polyPoints,
@@ -6605,7 +6599,7 @@ var __defProp = Object.defineProperty;
6605
6599
  opacity: 0.45
6606
6600
  }
6607
6601
  ),
6608
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6602
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6609
6603
  "polygon",
6610
6604
  {
6611
6605
  points: polyPoints,
@@ -6616,7 +6610,7 @@ var __defProp = Object.defineProperty;
6616
6610
  ),
6617
6611
  [0, 1, 2, 3, 4, 5, 6, 7].map((i) => {
6618
6612
  const p = cornerPos(i);
6619
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6613
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6620
6614
  "line",
6621
6615
  {
6622
6616
  x1: CENTER,
@@ -6632,7 +6626,7 @@ var __defProp = Object.defineProperty;
6632
6626
  ]
6633
6627
  }
6634
6628
  ),
6635
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6629
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6636
6630
  "div",
6637
6631
  {
6638
6632
  "aria-hidden": "true",
@@ -6651,7 +6645,7 @@ var __defProp = Object.defineProperty;
6651
6645
  }
6652
6646
  }
6653
6647
  ),
6654
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6648
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6655
6649
  "button",
6656
6650
  {
6657
6651
  type: "button",
@@ -6685,7 +6679,7 @@ var __defProp = Object.defineProperty;
6685
6679
  const dim = activeCorner != null && !active;
6686
6680
  const empty = count === 0;
6687
6681
  const p = cornerPos(i);
6688
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6682
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6689
6683
  "div",
6690
6684
  {
6691
6685
  title: id ? `「${label}」${count ?? 0} 条
@@ -6714,8 +6708,8 @@ var __defProp = Object.defineProperty;
6714
6708
  zIndex: 2
6715
6709
  },
6716
6710
  children: [
6717
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: label }),
6718
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { style: { fontSize: 8, opacity: 0.75, fontVariantNumeric: "tabular-nums" }, children: count === null ? " " : empty ? "空角" : `${count} 条` })
6711
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: label }),
6712
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 8, opacity: 0.75, fontVariantNumeric: "tabular-nums" }, children: count === null ? " " : empty ? "空角" : `${count} 条` })
6719
6713
  ]
6720
6714
  },
6721
6715
  id ?? i
@@ -6724,7 +6718,7 @@ var __defProp = Object.defineProperty;
6724
6718
  ]
6725
6719
  }
6726
6720
  ),
6727
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6721
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6728
6722
  "div",
6729
6723
  {
6730
6724
  title: props.halls.length === 0 ? "选定某个角(单 Wing)后,这两项才生效" : "锁定 Wing 时两类无角记忆是否参与召回",
@@ -6738,9 +6732,9 @@ var __defProp = Object.defineProperty;
6738
6732
  pointerEvents: boundariesDisabled ? "none" : void 0
6739
6733
  },
6740
6734
  children: [
6741
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `另有 ${overview.unlabeled} 条未打标` : "未打标" }),
6742
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
6743
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6735
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `另有 ${overview.unlabeled} 条未打标` : "未打标" }),
6736
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
6737
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6744
6738
  Segmented,
6745
6739
  {
6746
6740
  value: props.hallIncludeUnlabeled ? "in" : "ex",
@@ -6751,7 +6745,7 @@ var __defProp = Object.defineProperty;
6751
6745
  onChange: (key) => props.onCommitWingBoundaries({ includeUnlabeled: key === "in" })
6752
6746
  }
6753
6747
  ),
6754
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6748
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6755
6749
  Segmented,
6756
6750
  {
6757
6751
  value: props.hallIncludeGeneral ? "in" : "ex",
@@ -6766,7 +6760,7 @@ var __defProp = Object.defineProperty;
6766
6760
  ]
6767
6761
  }
6768
6762
  ),
6769
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Row, { label: "会话", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6763
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Row, { label: "会话", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6770
6764
  Segmented,
6771
6765
  {
6772
6766
  value: isOff ? "off" : "on",
@@ -6781,7 +6775,7 @@ var __defProp = Object.defineProperty;
6781
6775
  onChange: (key) => props.onCommit(key === "off" ? "off" : "auto")
6782
6776
  }
6783
6777
  ) }),
6784
- props.recall !== void 0 && props.onCommitRecall ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Row, { label: "注入", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6778
+ props.recall !== void 0 && props.onCommitRecall ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Row, { label: "注入", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6785
6779
  Segmented,
6786
6780
  {
6787
6781
  value: props.recall === null ? "follow" : props.recall ? "on" : "off",
@@ -6794,7 +6788,7 @@ var __defProp = Object.defineProperty;
6794
6788
  onChange: (key) => props.onCommitRecall(key === "on" ? true : key === "off" ? false : null)
6795
6789
  }
6796
6790
  ) }) : null,
6797
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6791
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6798
6792
  "div",
6799
6793
  {
6800
6794
  style: {
@@ -6805,8 +6799,8 @@ var __defProp = Object.defineProperty;
6805
6799
  marginTop: 10
6806
6800
  },
6807
6801
  children: [
6808
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `未打标 ${overview.unlabeled} 条` : "未打标计数加载中" }),
6809
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
6802
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: overview ? `未打标 ${overview.unlabeled} 条` : "未打标计数加载中" }),
6803
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6810
6804
  ActionButton,
6811
6805
  {
6812
6806
  label: backfillBusy ? "回填中…" : "回填",
@@ -6818,13 +6812,36 @@ var __defProp = Object.defineProperty;
6818
6812
  ]
6819
6813
  }
6820
6814
  ),
6821
- props.error || localError ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-danger)", marginTop: 8 }, children: props.error || localError }) : null,
6822
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SessionRecalledArea, { rpc: props.rpc, sessionId: props.sessionId }),
6823
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(SessionInfoArea, { rpc: props.rpc, sessionId: props.sessionId })
6815
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6816
+ "div",
6817
+ {
6818
+ style: {
6819
+ display: "flex",
6820
+ justifyContent: "space-between",
6821
+ alignItems: "center",
6822
+ gap: 8,
6823
+ marginTop: 10
6824
+ },
6825
+ children: [
6826
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-mem-text-3)" }, children: kwSummary || "为存量记忆补检索关键词" }),
6827
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
6828
+ ActionButton,
6829
+ {
6830
+ label: kwBusy ? "回填中…" : "关键词回填",
6831
+ title: "对缺 dsh_keywords 的存量记忆批量补关键词,喂 FTS 关键词检索轴(需先开 keyword.enabled)",
6832
+ disabled: kwBusy,
6833
+ onClick: () => keywordBackfill()
6834
+ }
6835
+ )
6836
+ ]
6837
+ }
6838
+ ),
6839
+ props.error || localError ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-mem-danger)", marginTop: 8 }, children: props.error || localError }) : null,
6840
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SessionInfoArea, { rpc: props.rpc, sessionId: props.sessionId })
6824
6841
  ] });
6825
6842
  }
6826
6843
  function Row(props) {
6827
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
6844
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
6828
6845
  "div",
6829
6846
  {
6830
6847
  style: {
@@ -6834,7 +6851,7 @@ var __defProp = Object.defineProperty;
6834
6851
  marginTop: 10
6835
6852
  },
6836
6853
  children: [
6837
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: props.label }),
6854
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 12, color: "var(--dsh-mem-text-3)" }, children: props.label }),
6838
6855
  props.children
6839
6856
  ]
6840
6857
  }
@@ -6842,9 +6859,9 @@ var __defProp = Object.defineProperty;
6842
6859
  }
6843
6860
  var LABEL_FALLBACK = ["工作", "人际", "学习", "创作娱乐", "居家", "健康", "财务", "出行"];
6844
6861
  function useViewportClamp(popRef) {
6845
- const shiftRef = (0, import_react25.useRef)(0);
6846
- const [shiftX, setShiftX] = (0, import_react25.useState)(0);
6847
- (0, import_react25.useEffect)(() => {
6862
+ const shiftRef = (0, import_react24.useRef)(0);
6863
+ const [shiftX, setShiftX] = (0, import_react24.useState)(0);
6864
+ (0, import_react24.useEffect)(() => {
6848
6865
  const clamp = () => {
6849
6866
  const el = popRef.current;
6850
6867
  if (!el) return;
@@ -6874,24 +6891,24 @@ var __defProp = Object.defineProperty;
6874
6891
  }
6875
6892
 
6876
6893
  // client/src/pill/MemoryModePill.tsx
6877
- var import_jsx_runtime26 = require("react/jsx-runtime");
6894
+ var import_jsx_runtime25 = require("react/jsx-runtime");
6878
6895
  function MemoryModePill(props) {
6879
6896
  const rpc = props.rpc;
6880
6897
  const sessionId = props.sessionId || props.session && props.session.sessionId;
6881
- const [mode, setMode] = (0, import_react26.useState)(null);
6882
- const [recall, setRecall] = (0, import_react26.useState)(null);
6883
- const [recallResolved, setRecallResolved] = (0, import_react26.useState)(true);
6884
- const [halls, setWings] = (0, import_react26.useState)([]);
6885
- const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0, import_react26.useState)(true);
6886
- const [hallIncludeGeneral, setWingIncludeGeneral] = (0, import_react26.useState)(false);
6887
- const [wingLabels, setHallLabels] = (0, import_react26.useState)({});
6888
- const [error, setError] = (0, import_react26.useState)(null);
6889
- const [open, setOpen] = (0, import_react26.useState)(false);
6890
- const wrapRef = (0, import_react26.useRef)(null);
6891
- const popRef = (0, import_react26.useRef)(null);
6898
+ const [mode, setMode] = (0, import_react25.useState)(null);
6899
+ const [recall, setRecall] = (0, import_react25.useState)(null);
6900
+ const [recallResolved, setRecallResolved] = (0, import_react25.useState)(true);
6901
+ const [halls, setWings] = (0, import_react25.useState)([]);
6902
+ const [hallIncludeUnlabeled, setWingIncludeUnlabeled] = (0, import_react25.useState)(true);
6903
+ const [hallIncludeGeneral, setWingIncludeGeneral] = (0, import_react25.useState)(false);
6904
+ const [wingLabels, setHallLabels] = (0, import_react25.useState)({});
6905
+ const [error, setError] = (0, import_react25.useState)(null);
6906
+ const [open, setOpen] = (0, import_react25.useState)(false);
6907
+ const wrapRef = (0, import_react25.useRef)(null);
6908
+ const popRef = (0, import_react25.useRef)(null);
6892
6909
  const shiftX = useViewportClamp(popRef);
6893
- const seqRef = (0, import_react26.useRef)(0);
6894
- const load = (0, import_react26.useCallback)(() => {
6910
+ const seqRef = (0, import_react25.useRef)(0);
6911
+ const load = (0, import_react25.useCallback)(() => {
6895
6912
  if (!sessionId || !rpc) return;
6896
6913
  const token = ++seqRef.current;
6897
6914
  setError(null);
@@ -6916,13 +6933,13 @@ var __defProp = Object.defineProperty;
6916
6933
  }).catch(() => {
6917
6934
  });
6918
6935
  }, [sessionId, rpc]);
6919
- (0, import_react26.useEffect)(() => {
6936
+ (0, import_react25.useEffect)(() => {
6920
6937
  load();
6921
6938
  }, [load]);
6922
- (0, import_react26.useEffect)(() => {
6939
+ (0, import_react25.useEffect)(() => {
6923
6940
  watchSidebarIcon();
6924
6941
  }, []);
6925
- (0, import_react26.useEffect)(() => {
6942
+ (0, import_react25.useEffect)(() => {
6926
6943
  initOccupancyIndicator(
6927
6944
  (endpoint, payload) => rpc(endpoint, payload)
6928
6945
  );
@@ -6930,7 +6947,7 @@ var __defProp = Object.defineProperty;
6930
6947
  watchContextMeter();
6931
6948
  noteOccupancySession(sessionId ?? null);
6932
6949
  }, [sessionId, rpc]);
6933
- (0, import_react26.useEffect)(() => {
6950
+ (0, import_react25.useEffect)(() => {
6934
6951
  if (!open) return;
6935
6952
  const onDown = (e) => {
6936
6953
  if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
@@ -7070,8 +7087,8 @@ var __defProp = Object.defineProperty;
7070
7087
  pillStyle.boxShadow = "0 0 12px color-mix(in srgb, " + info.color + " 30%, transparent)";
7071
7088
  pillStyle["--dsh-mem-pill-tint"] = info.color;
7072
7089
  }
7073
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
7074
- /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
7090
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { ref: wrapRef, style: { position: "relative", display: "inline-flex" }, children: [
7091
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
7075
7092
  "button",
7076
7093
  {
7077
7094
  type: "button",
@@ -7084,11 +7101,11 @@ var __defProp = Object.defineProperty;
7084
7101
  style: pillStyle,
7085
7102
  children: [
7086
7103
  "记忆 · ",
7087
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { children: faceLabel })
7104
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: faceLabel })
7088
7105
  ]
7089
7106
  }
7090
7107
  ),
7091
- open ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
7108
+ open ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7092
7109
  "div",
7093
7110
  {
7094
7111
  ref: popRef,
@@ -7099,12 +7116,12 @@ var __defProp = Object.defineProperty;
7099
7116
  transform: "translateX(calc(-50% + " + shiftX + "px))",
7100
7117
  zIndex: 1e3
7101
7118
  },
7102
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
7119
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7103
7120
  "div",
7104
7121
  {
7105
7122
  className: "dsh-mem-popover",
7106
7123
  style: { position: "relative", padding: "10px 12px" },
7107
- children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
7124
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
7108
7125
  WingWheel,
7109
7126
  {
7110
7127
  mode: mode || "auto",
package/dist/config.d.ts CHANGED
@@ -193,6 +193,18 @@ export interface MemoryConfig {
193
193
  usageScenario: {
194
194
  enabled: boolean;
195
195
  };
196
+ /**
197
+ * 关键词蒸馏回填(keyword 检索轴的产出方):**独立回填批扫**,为缺 `metadata.dsh_keywords` 的
198
+ * L1 记录批量补关键词,喂 `l1_fts` 的关键词轴(`ftsKeywordText` 读该键)。由
199
+ * `dsh-memory/keyword-backfill` 端点触发,镜像 wing 回填(单飞/后台/有界)。
200
+ *
201
+ * **默认关**:关时不回填、不产该键、`l1_fts.keywords` 列逐字不变(零漂移)。护栏复用 `backfill`。
202
+ * 注:写回走 `patchMetadata` 只改主表,回填会**额外重索引该记录的 fts 行**(见 keyword-backfill),
203
+ * 否则关键词轴对存量记录不生效。
204
+ */
205
+ keyword: {
206
+ enabled: boolean;
207
+ };
196
208
  /**
197
209
  * 类型叙述模板(G2):抽取时按 7 类型各自的“前因后果”骨架写 content。
198
210
  *