dsh-engram 0.7.2 → 0.7.4

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/lib/client.js CHANGED
@@ -1163,7 +1163,7 @@ var input = {
1163
1163
  };
1164
1164
 
1165
1165
  // client/src/EngramSection.tsx
1166
- var import_react7 = require("react");
1166
+ var import_react8 = require("react");
1167
1167
 
1168
1168
  // client/src/EngramGraph.tsx
1169
1169
  var import_react3 = require("react");
@@ -2299,1862 +2299,1864 @@ function EngramTelemetry({ api, workspace }) {
2299
2299
  ] });
2300
2300
  }
2301
2301
 
2302
- // client/src/EngramDetail.tsx
2302
+ // client/src/EngramConfigCard.tsx
2303
2303
  var import_react6 = require("react");
2304
2304
  var import_jsx_runtime6 = require("react/jsx-runtime");
2305
- var KIND_COLORS = {
2306
- preference: "#b45309",
2307
- decision: "#2563eb",
2308
- error: "#dc2626",
2309
- procedure: "#7c3aed",
2310
- fact: "#059669",
2311
- insight: "#d97706",
2312
- handoff: "#0891b2",
2313
- task: "#4f46e5"
2314
- };
2315
- var KIND_LABEL = {
2316
- preference: "\u504F\u597D",
2317
- decision: "\u51B3\u5B9A",
2318
- error: "\u9519\u8BEF",
2319
- procedure: "\u6D41\u7A0B",
2320
- fact: "\u4E8B\u5B9E",
2321
- insight: "\u6D1E\u5BDF",
2322
- handoff: "\u4EA4\u63A5",
2323
- task: "\u4EFB\u52A1"
2324
- };
2325
- var REL_COLORS = {
2326
- depends_on: "#d97706",
2327
- implements: "#2563eb",
2328
- refines: "#7c3aed",
2329
- contradicts: "#dc2626",
2330
- tracks: "#059669"
2331
- };
2332
- function fmtDate(ts) {
2333
- if (!ts) return "\u2013";
2334
- const d = new Date(ts);
2335
- const mm = String(d.getMonth() + 1).padStart(2, "0");
2336
- const dd = String(d.getDate()).padStart(2, "0");
2337
- return `${d.getFullYear()}-${mm}-${dd} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
2338
- }
2339
- function daysLeft(ts) {
2340
- if (ts === null || ts === void 0) return "\u221E";
2341
- const days = Math.ceil((ts - Date.now()) / 864e5);
2342
- return days > 0 ? `${days}d` : "\u5DF2\u8FC7\u671F";
2343
- }
2344
- function gapsOf(t) {
2345
- const gaps = [];
2346
- if (!t.artifact) gaps.push("artifact");
2347
- if (!t.evaluation) gaps.push("evaluation");
2348
- if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
2349
- return gaps;
2350
- }
2351
- function EngramDetail({ target, api, tasks, nodes, links, memories = [], onClose, onNavigateMemory, onChanged }) {
2352
- const { dark, vars } = useEngramTheme();
2353
- const [gArt, setGArt] = (0, import_react6.useState)("");
2354
- const [gEval, setGEval] = (0, import_react6.useState)("");
2355
- const [gRefs, setGRefs] = (0, import_react6.useState)("");
2356
- const [busy, setBusy] = (0, import_react6.useState)(false);
2357
- const [closing, setClosing] = (0, import_react6.useState)(false);
2358
- const [notice, setNotice] = (0, import_react6.useState)(null);
2359
- const task = target.kind === "task" ? tasks.find((t) => t.id === target.id) ?? null : null;
2360
- const node = target.kind === "node" ? nodes.find((n) => n.id === target.id) ?? null : null;
2361
- const link = target.kind === "link" ? target.link : null;
2362
- const nameOf = (0, import_react6.useMemo)(() => {
2363
- const nodeName = (id) => nodes.find((n) => n.id === id)?.name;
2364
- const taskName = (id) => tasks.find((t) => t.id === id)?.name;
2365
- return (id) => nodeName(id) ?? taskName(id) ?? id;
2366
- }, [nodes, tasks]);
2367
- const incident = (0, import_react6.useMemo)(() => {
2368
- if (target.kind !== "node" || !node) return [];
2369
- return links.filter((l) => l.source === node.id || l.target === node.id).map((l) => ({ l, outgoing: l.source === node.id }));
2370
- }, [links, node, target.kind]);
2371
- const memoryLinks = (0, import_react6.useMemo)(() => {
2372
- if (target.kind !== "memory") return null;
2373
- const m = target.memory;
2374
- return {
2375
- m,
2376
- replacedBy: memories.filter((x) => x.status === "active" && x.supersedes === m.id),
2377
- contradictedBy: memories.filter((x) => x.status === "active" && x.contradicts === m.id)
2378
- };
2379
- }, [target, memories]);
2380
- const hb2 = {
2381
- root: {
2382
- ...vars,
2383
- border: "1px solid var(--dsh-color-border, #d1d5db)",
2384
- borderRadius: 12,
2385
- background: dark ? "rgba(10,14,22,.55)" : "var(--dsh-color-surface, #ffffff)",
2386
- boxShadow: "0 1px 2px rgba(15,23,42,.05), 0 8px 24px rgba(15,23,42,.07)",
2387
- overflow: "hidden",
2388
- display: "flex",
2389
- flexDirection: "column",
2390
- maxHeight: "72vh"
2391
- },
2392
- head: {
2393
- display: "flex",
2394
- alignItems: "center",
2395
- gap: 8,
2396
- padding: "10px 12px",
2397
- borderBottom: "1px solid var(--dsh-color-border, #e5e7eb)",
2398
- background: dark ? "rgba(15,23,42,.6)" : "rgba(15,23,42,.04)"
2399
- },
2400
- title: { fontWeight: 700, fontSize: 13, flex: "1 1 auto", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
2401
- close: { border: "none", background: "none", cursor: "pointer", fontSize: 13, color: "var(--dsh-color-muted, #6b7280)", padding: "2px 6px", borderRadius: 6 },
2402
- body: { padding: "10px 12px", overflowY: "auto", display: "flex", flexDirection: "column", gap: 8, fontSize: 12.5 },
2403
- kv: { display: "flex", flexDirection: "column", gap: 3 },
2404
- k: { fontSize: 10.5, fontWeight: 700, color: "var(--dsh-color-muted-weak, #9ca3af)", textTransform: "uppercase", letterSpacing: 0.4 },
2405
- v: { color: "var(--dsh-color-muted-strong, #374151)", wordBreak: "break-word", lineHeight: 1.5 },
2406
- mono: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 },
2407
- badge: { fontSize: 10.5, fontWeight: 700, padding: "2px 8px", borderRadius: 999, color: "#fff" },
2408
- tag: { fontSize: 11, padding: "1px 8px", borderRadius: 999, background: "var(--dsh-color-hover-bg, #f3f4f6)", color: "var(--dsh-color-muted, #6b7280)", border: "1px solid var(--dsh-color-border, #e5e7eb)" },
2409
- chip: { fontSize: 11, padding: "2px 9px", borderRadius: 999, background: "rgba(37,99,235,.1)", color: "#2563eb", border: "1px solid rgba(37,99,235,.25)", cursor: "pointer" },
2410
- chipSup: { fontSize: 11, padding: "2px 9px", borderRadius: 999, background: "rgba(124,58,237,.1)", color: "#7c3aed", border: "1px solid rgba(124,58,237,.25)", cursor: "pointer" },
2411
- chipDanger: { fontSize: 11, padding: "2px 9px", borderRadius: 999, background: "rgba(220,38,38,.1)", color: "#dc2626", border: "1px solid rgba(220,38,38,.25)", cursor: "pointer" },
2412
- input: { fontSize: 12, padding: "5px 8px", border: "1px solid var(--dsh-color-border, #d1d5db)", borderRadius: 8, background: "var(--dsh-color-surface, #ffffff)", color: "var(--dsh-color-muted-strong, #374151)", width: "100%", boxSizing: "border-box" },
2413
- btn: { fontSize: 11.5, fontWeight: 600, padding: "5px 10px", borderRadius: 8, cursor: "pointer", border: "1px solid var(--dsh-color-border, #d1d5db)", background: "transparent", color: "var(--dsh-color-muted-strong, #374151)" },
2414
- btnPrimary: { fontSize: 11.5, fontWeight: 700, padding: "5px 12px", borderRadius: 8, cursor: "pointer", border: "none", background: "#2563eb", color: "#fff" },
2415
- relRow: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap", fontSize: 12 },
2416
- relType: { border: "1px dashed var(--dsh-color-border, #cbd5e1)", borderRadius: 999, padding: "1px 7px", fontSize: 11, color: "var(--dsh-color-muted, #6b7280)" },
2417
- dot: { width: 8, height: 8, borderRadius: 999, display: "inline-block" },
2418
- section: { marginTop: 2 }
2419
- };
2420
- async function submitClose() {
2421
- if (!task) return;
2422
- setBusy(true);
2423
- setNotice(null);
2424
- try {
2425
- const refs = gRefs.split(/[,,\s]+/).map((s4) => s4.trim()).filter(Boolean);
2426
- const res = await api.closeTask(task.workspace, task.id, { artifact: gArt.trim() || void 0, evaluation: gEval.trim() || void 0, memoryRefs: refs.length ? refs : void 0 });
2427
- setNotice(res.state === "stable" ? `\u5DF2\u95ED\u73AF \u2192 STABLE \u2713` : `\u4ECD\u6709\u7F3A\u53E3\uFF1A${(res.gaps ?? []).join(", ") || "\u2014"}${res.artifactReason ? `\uFF08${res.artifactReason}\uFF09` : ""}`);
2428
- onChanged?.();
2429
- } catch (e) {
2430
- setNotice(`\u5173\u95ED\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
2431
- } finally {
2432
- setBusy(false);
2433
- }
2305
+ var COMMON_FIELDS = [
2306
+ { key: "autoCapture", label: "\u81EA\u52A8\u6355\u83B7", hint: "\u96F6 LLM \u4ECE\u5DE5\u5177\u7ED3\u679C\u63D0\u53D6\u8BB0\u5FC6\uFF08git/\u5173\u952E\u6587\u4EF6/\u9519\u8BEF\uFF09", kind: "bool" },
2307
+ { key: "autoCapturePreferences", label: "\u504F\u597D\u6355\u83B7", hint: "\u7528\u6237\u7EA0\u6B63/\u504F\u597D\u5B66\u4E60\uFF1A\u68C0\u6D4B\u6D88\u606F\u91CC\u7684\u300C\u4E0D\u8981\u7528X/\u6539\u7528Y/prefer/never use\u300D\u53E5\u5F0F\uFF0C\u81EA\u52A8\u6C89\u6DC0\u4E3A preference \u8BB0\u5FC6\u5E76\u7F6E\u9876 [ENGRAM]\uFF08correx/PAHF \u5F0F\uFF0C\u96F6 LLM\uFF09", kind: "bool" },
2308
+ { key: "boundaryPrune", label: "\u8FB9\u754C\u526A\u679D", hint: "ResultPack L1\uFF1Atodo \u5B8C\u6210/goal \u7EC8\u6001\u8FB9\u754C\u4E3B\u52A8\u526A\u6389\u8D85\u9608\u503C\u65E7\u5DE5\u5177\u7ED3\u679C\uFF08DSH \u786E\u5B9A\u6027 surface-replace\u3001\u507F\u8FD8\u95E8\u3001\u96F6 LLM\uFF1B\u9ED8\u8BA4\u5F00\uFF0C\u5173=opt-out\uFF09", kind: "bool" },
2309
+ { key: "boundaryPruneAdaptive", label: "\u526A\u679D\u81EA\u9002\u5E94", hint: "\u8D26\u672C\u5426\u51B3\uFF1A\u8BE5 workspace \u771F\u5B9E\u526A\u679D\u540E\u51C0\u6536\u76CA\u4E3A\u8D1F\uFF08\u22653 \u6B21\uFF09\u65F6\u81EA\u52A8\u505C\u6B62\u7EE7\u7EED\u526A\uFF1B\u8BC1\u636E\u53EA\u80FD\u5173\u3001\u4E0D\u4F1A\u81EA\u884C\u5F00", kind: "bool" },
2310
+ { key: "boundaryPruneThresholdChars", label: "\u526A\u679D\u9608\u503C(\u5B57\u7B26)", hint: "\u8986\u76D6\u5BBF\u4E3B 8192\uFF1A\u8BBE 2048 \u53EF\u66F4\u65E9\u5F00\u526A\uFF0C\u5E76\u914D L2 \u5F52\u6863\u627E\u56DE\u88AB\u526A\u4E2D\u6BB5\uFF1B\u7559\u7A7A=\u5BBF\u4E3B\u9ED8\u8BA4\u3002\u5EFA\u8BAE\u540C\u65F6\u628A\u4FDD\u7559\u8FB9\u7F18\u8C03\u5C0F\uFF08\u5982 1024/256\uFF09\u4EE5\u83B7\u5F97\u6700\u5927\u8282\u7701", kind: "num", min: 256, max: 16384, step: 256 },
2311
+ { key: "resultPack", label: "\u7ED3\u679C\u5F52\u6863\u53EC\u56DE", hint: "ResultPack L2\uFF1A\u526A\u6389\u7684\u4E2D\u6BB5\u8131\u654F\u5F52\u6863\uFF0C\u8282\u70B9\u5C3E\u90E8\u9644 handle\uFF0C\u6A21\u578B\u53EF\u7528 result_recall(callId) \u6309\u9875\u53EC\u56DE\uFF08\u5B8C\u6574 ObservationPack\uFF1B\u9ED8\u8BA4\u5F00\uFF0C\u4FDD\u8BC1\u9ED8\u8BA4\u526A\u679D\u65E0\u635F\uFF1B\u9700\u4E0E\u8FB9\u754C\u526A\u679D\u4E00\u8D77\u5F00\uFF09", kind: "bool" },
2312
+ { key: "autoRecallOnStart", label: "\u4F1A\u8BDD\u542F\u52A8\u81EA\u52A8\u53EC\u56DE", hint: "\u65B0\u4F1A\u8BDD\u6309\u9996\u6761\u6D88\u606F\u81EA\u52A8\u628A\u6700\u76F8\u5173\u8BB0\u5FC6\u6CE8\u5165 [RECALL] \u5757\uFF08\u65E0\u9700\u6A21\u578B\u60F3\u8D77\u8C03 engram_recall\uFF09\uFF1B\u5173\u6389=\u56DE\u5230\u7EAF\u62C9\u53D6", kind: "bool" },
2313
+ { key: "jitEnabled", label: "JIT \u63A8\u9001", hint: "\u9519\u8BEF\u590D\u53D1 / \u6587\u4EF6\u7126\u70B9 / goal \u5B8C\u6210\u65F6\uFF0C\u628A\u5DF2\u6709\u8BB0\u5FC6\u538B\u6210\u4E00\u884C\u63A8\u8FDB\u4E0A\u4E0B\u6587\uFF08append-only\u3001\u6BCF\u4F1A\u8BDD\u22643 \u884C\u3001\u65E0\u547D\u4E2D\u6C89\u9ED8\uFF1B\u6BCF\u6B21\u63A8\u9001\u6309\u524D\u7F00\u91CD\u5199\u8BA1\u5165\u8D26\u672C\uFF09", kind: "bool" },
2314
+ { key: "episodeEnabled", label: "\u4F1A\u8BDD\u7EAA\u8981", hint: "\u4F1A\u8BDD\u7ED3\u675F\u65F6\u7528\u96F6 LLM \u89C4\u5219\u751F\u6210\u4E00\u6761 episode\uFF08\u6587\u4EF6/\u547D\u4EE4/\u5931\u8D25\u2192\u4FEE\u590D/\u51B3\u7B56/\u672A\u95ED\u73AF\uFF09\uFF0C\u4E0D\u8FDB\u7D22\u5F15\uFF0C\u4F9B\u6062\u590D\u7B80\u62A5\u4F7F\u7528\uFF1B\u6BCF workspace \u4FDD\u7559\u6700\u8FD1 20 \u6761", kind: "bool" },
2315
+ { key: "resumeBriefEnabled", label: "\u6062\u590D\u7B80\u62A5", hint: "\u65B0\u4F1A\u8BDD\u542F\u52A8\u65F6\u6CE8\u5165\u6700\u8FD1 7 \u5929\u7684\u6700\u65B0 episode\uFF08\u2264240 \u5B57\u7B26 + engram_detail \u6307\u9488\uFF09\uFF0C\u9694\u5929\u7EE7\u7EED\u4E0D\u7528\u91CD\u65B0\u4EA4\u4EE3\u80CC\u666F", kind: "bool" },
2316
+ { key: "entityCardsEnabled", label: "\u5B9E\u4F53\u5361", hint: "[ENGRAM] \u7D22\u5F15\u4E3A\u6D3B\u8DC3\u5B9E\u4F53\uFF08\u6709\u951A\u5B9A\u8BB0\u5FC6\u6216\u672A\u95ED\u73AF\u4EFB\u52A1\uFF09\u6E32\u67D3\u4E00\u884C\u300C\u5F53\u524D\u771F\u76F8 + \u6253\u5F00\u4EFB\u52A1 + engram_detail(entity=\u2026) \u6307\u9488\u300D\uFF0C\u6700\u591A 2 \u5F20\uFF0C\u66FF\u4EE3\u8FD9\u4E9B\u5B9E\u4F53\u7684\u5E73\u94FA\u8BB0\u5FC6\u884C", kind: "bool" },
2317
+ { key: "maxRecallPerSession", label: "\u6BCF\u4F1A\u8BDD\u53BB\u91CD\u4E0A\u9650", hint: "recall \u6BCF\u4F1A\u8BDD\u6700\u591A\u4FDD\u7559 N \u6761\uFF08\u8DE8\u4F1A\u8BDD\u53BB\u91CD\uFF09", kind: "num", min: 1, max: 10 },
2318
+ { key: "expireDays", label: "TTL\uFF08\u5929\uFF09", hint: "0 = \u4E0D\u8FC7\u671F", kind: "num", min: 0, max: 3650 },
2319
+ { key: "indexMaxChars", label: "\u7D22\u5F15\u5B57\u7B26\u4E0A\u9650", hint: "[ENGRAM] \u5757 token \u9884\u7B97", kind: "num", min: 0, max: 4e3 }
2320
+ ];
2321
+ var ADVANCED_GROUPS = [
2322
+ {
2323
+ id: "capture-adv",
2324
+ title: "\u6355\u83B7\u4E0E\u68C0\u7D22\uFF08\u9AD8\u7EA7\uFF09",
2325
+ description: "\u5176\u4F59\u6355\u83B7/\u641C\u7D22\u65CB\u94AE",
2326
+ fields: [
2327
+ { key: "autoCapturePerSession", label: "\u6BCF\u4F1A\u8BDD\u6355\u83B7\u4E0A\u9650", hint: "\u5355\u4F1A\u8BDD\u81EA\u52A8\u6355\u83B7\u6761\u6570\u4E0A\u9650", kind: "num", min: 0, max: 1e3 },
2328
+ { key: "boundaryPruneHeadChars", label: "\u526A\u679D\u4FDD\u7559\u5934\u90E8(\u5B57\u7B26)", hint: "\u9608\u503C\u8986\u76D6\u751F\u6548\u65F6\u7ED3\u679C\u5F00\u5934\u4FDD\u7559\u957F\u5EA6\uFF08\u9ED8\u8BA4 4096\uFF09\uFF1B\u8C03\u5C0F\uFF08\u5982 1024\uFF09\u914D\u5408 T2048 \u66F4\u7701", kind: "num", min: 0, max: 16384, step: 128 },
2329
+ { key: "boundaryPruneTailChars", label: "\u526A\u679D\u4FDD\u7559\u5C3E\u90E8(\u5B57\u7B26)", hint: "\u9608\u503C\u8986\u76D6\u751F\u6548\u65F6\u7ED3\u679C\u7ED3\u5C3E\u4FDD\u7559\u957F\u5EA6\uFF08\u9ED8\u8BA4 1024\uFF09\uFF1B\u8C03\u5C0F\uFF08\u5982 256\uFF09\u914D\u5408 T2048 \u66F4\u7701", kind: "num", min: 0, max: 16384, step: 128 },
2330
+ { key: "boundaryPruneIdleCadence", label: "\u7A7A\u95F2\u6B65\u6570\u8FB9\u754C", hint: "\u6700\u591A\u6BCF N \u8BF7\u6C42\u89E6\u53D1\u4E00\u6B21\u526A\u679D\uFF0C\u8986\u76D6\u65E0 todo/goal \u7684\u4F1A\u8BDD\uFF08\u7EA6 62% \u4F1A\u8BDD\uFF09\uFF1B\u4ECD\u53D7\u507F\u8FD8\u95E8\u7EA6\u675F\uFF1B\u7559\u7A7A=\u4EC5\u4E8B\u4EF6\u8FB9\u754C", kind: "num", min: 10, max: 1e3, step: 10 },
2331
+ { key: "autoRecallLimit", label: "\u81EA\u52A8\u53EC\u56DE\u6761\u6570", hint: "[RECALL] \u5757\u6700\u591A\u6CE8\u5165\u7684\u8BB0\u5FC6\u6761\u6570\uFF08\u5C11\u800C\u51C6\uFF09", kind: "num", min: 1, max: 8 },
2332
+ { key: "sessionSearch", label: "\u4F1A\u8BDD\u5386\u53F2\u641C\u7D22", hint: "engram_recall \u652F\u6301\u8DE8\u4F1A\u8BDD FTS \u515C\u5E95", kind: "bool" },
2333
+ { key: "recallScope", label: "\u53EC\u56DE\u8303\u56F4", hint: "workspace=\u4E25\u683C\u5F53\u524D\u5DE5\u4F5C\u533A\u9694\u79BB\uFF08\u9ED8\u8BA4\uFF0Ctoken \u7EAA\u5F8B\uFF09\uFF1Bglobal=\u672C\u5DE5\u4F5C\u533A\u547D\u4E2D\u5F31/\u7A7A\u65F6\u4ECE\u5176\u5B83\u5DE5\u4F5C\u533A\u6C60\u53EC\u56DE\u5E76\u6807 [W:<workspace>] \u6765\u6E90\uFF08\u4F1A\u6253\u7834\u9694\u79BB\uFF09", kind: "select", options: ["workspace", "global"] }
2334
+ ]
2335
+ },
2336
+ {
2337
+ id: "index-adv",
2338
+ title: "\u7D22\u5F15\uFF08\u9AD8\u7EA7\uFF09",
2339
+ description: "\u884C\u6570\u3001\u5165\u7D22\u5F15\u4FE1\u53F7\u4E0E\u664B\u5347\u89C4\u5219",
2340
+ fields: [
2341
+ { key: "indexMaxLines", label: "\u7D22\u5F15\u6700\u5927\u884C\u6570", hint: "[ENGRAM] \u5757\u6700\u591A\u663E\u793A\u7684\u6761\u76EE\u884C\u6570", kind: "num", min: 0, max: 50 },
2342
+ { key: "minIndexSignal", label: "\u5165\u7D22\u5F15\u4FE1\u53F7\u9608\u503C", hint: "signal \u2265 \u6B64\u503C\u7684\u81EA\u52A8\u6355\u83B7\u624D\u8FDB\u7D22\u5F15", kind: "num", min: 0, max: 1, step: 0.05 },
2343
+ { key: "promoteHits", label: "\u664B\u5347\u547D\u4E2D\u6570", hint: "hit \u6570\u8FBE\u6B64\u503C\u7684\u4F4E\u4FE1\u53F7\u6761\u76EE\u8FDB\u7D22\u5F15", kind: "num", min: 0, max: 20 }
2344
+ ]
2345
+ },
2346
+ {
2347
+ id: "retention-adv",
2348
+ title: "\u751F\u547D\u5468\u671F\u4E0E GC\uFF08\u9AD8\u7EA7\uFF09",
2349
+ description: "\u5BB9\u91CF\u4E0A\u9650\u4E0E\u5B9A\u671F\u56DE\u6536\uFF08\u81EA\u52A8 GC = \u66FF\u4EE3 DSH \u81EA\u52A8 compact\uFF09",
2350
+ fields: [
2351
+ { key: "maxMemoriesPerWorkspace", label: "\u5DE5\u4F5C\u533A\u8BB0\u5FC6\u4E0A\u9650", hint: "\u5355\u5DE5\u4F5C\u533A\u5BB9\u91CF\u4E0A\u9650\uFF08GC \u5F52\u6863\u8D85\u9650\u6761\u76EE\uFF09", kind: "num", min: 0, max: 1e4 },
2352
+ { key: "gcEnabled", label: "\u8BB0\u5FC6 GC", hint: "\u5B9A\u65F6\u56DE\u6536\uFF08\u8FC7\u671F/\u8D85\u5BB9\u91CF/stable \u8D85\u7A97/\u60AC\u7A7A\u94FE\u63A5\uFF09", kind: "bool" },
2353
+ { key: "gcNarrative", label: "Context GC \u53D9\u4E8B\u515C\u5E95", hint: "\u81EA\u52A8 compact \u65F6\u65E0\u951A\u8F6E\u6B21\u7528 LLM \u6458\u8981\uFF1B\u5173\u6389=\u7EAF\u673A\u68B0\uFF08\u96F6 LLM\uFF09", kind: "bool" },
2354
+ { key: "gcStableRetentionDays", label: "stable \u4EFB\u52A1\u4FDD\u7559\uFF08\u5929\uFF09", hint: "\u8D85\u7A97\u540E\u7531 GC \u5F52\u6863\u3001\u79BB\u5F00 [ESR] \u8868\u9762", kind: "num", min: 0, max: 3650 },
2355
+ { key: "verifyArtifact", label: "\u6821\u9A8C artifact \u5B58\u5728", hint: "esr_close \u7684 artifact \u987B\u5728\u5DE5\u4F5C\u533A\u78C1\u76D8\u4E0A\u771F\u5B9E\u5B58\u5728\uFF08URL \u8C41\u514D\uFF09\uFF1Bforce:true/\u5173\u6389\u53EF\u8DF3\u8FC7", kind: "bool" },
2356
+ { key: "autoSinkTodosOnEnd", label: "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u6C89\u6DC0 todo", hint: "\u4F1A\u8BDD\u7ED3\u675F\u65F6\u4ECD pending \u7684 todo \u81EA\u52A8\u8F6C\u6210 ESR draft\uFF08\u6309\u540D\u53BB\u91CD\u3001\u53D7\u4EFB\u52A1\u4E0A\u9650\u7EA6\u675F\uFF09\uFF1B\u5173\u6389=\u968F\u4F1A\u8BDD\u5F03\u53BB", kind: "bool" },
2357
+ { key: "autoWebCompaction", label: "web \u9762\u81EA\u52A8\u542F\u7528 Context GC", hint: "\u542F\u52A8\u65F6\u628A\u5168\u90E8 stock \u5E03\u5C40\u7684 agent \u9884\u8BBE\uFF08shipped + \u81EA\u5B9A\u4E49\u6839\uFF09\u7684 compaction \u7EC4\u81EA\u52A8\u6362\u6210 dsh-engram/compaction\uFF1B\u5173\u6389=\u4E0D\u518D\u81EA\u52A8\u63A5\u7BA1\uFF08\u5DF2\u63A5\u7BA1\u7684\u9700 web-compaction:revert \u8FD8\u539F\uFF09", kind: "bool" }
2358
+ ]
2359
+ },
2360
+ {
2361
+ id: "semantics-adv",
2362
+ title: "\u8BB0\u5FC6\u8BED\u4E49\uFF08\u9AD8\u7EA7\uFF09",
2363
+ description: "supersedes / contradict \u7684\u542F\u53D1\u5F0F",
2364
+ fields: [
2365
+ { key: "autoSupersede", label: "\u81EA\u52A8 supersedes", hint: "\u5B9E\u4F53\u951A\u5B9A + \u66FF\u6362\u5F0F\u66F4\u65B0\uFF08\u6539\u7528/\u4E0D\u518D/no longer\u2026\uFF09\u81EA\u52A8\u628A\u65E7\u8BB0\u5FC6\u6807\u4E3A superseded\uFF1B\u65E7\u884C\u4FDD\u7559\u3001\u53EC\u56DE\u964D\u7EA7\u3002\u9ED8\u8BA4\u5173\uFF0C\u663E\u5F0F supersedes \u6C38\u8FDC\u4F18\u5148", kind: "bool" }
2366
+ ]
2367
+ },
2368
+ {
2369
+ id: "security",
2370
+ title: "\u5B89\u5168",
2371
+ description: "\u8BBF\u95EE\u56F4\u680F\u4E0E\u96A7\u9053\u6388\u6743",
2372
+ fields: [
2373
+ { key: "trustedHosts", label: "\u53D7\u4FE1\u96A7\u9053\u57DF\u540D", hint: "\u5141\u8BB8\u7ECF\u96A7\u9053\u8BBF\u95EE\u8BB0\u5FC6\u67E5\u770B\u5668\u7684\u57DF\u540D\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A = \u4EC5\u672C\u673A\u3002\u6539\u540E\u9700\u91CD\u542F dsh web \u751F\u6548", kind: "text", width: 260 }
2374
+ ]
2434
2375
  }
2435
- const kindBadge = (kind, label) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...hb2.badge, background: KIND_COLORS[kind] ?? "#6b7280" }, children: label });
2436
- const headerTitle = target.kind === "task" ? task?.name ?? target.id : target.kind === "memory" ? target.memory.text.slice(0, 40) : target.kind === "node" ? node?.name ?? target.id : link ? nameOf(link.source) : "";
2437
- const headerKind = target.kind === "task" ? task?.state === "stable" ? { k: "task", label: "STABLE" } : task ? { k: "task", label: task.state === "draft" ? "DRAFT" : "\u4EFB\u52A1" } : { k: "task", label: "\u4EFB\u52A1" } : target.kind === "memory" ? { k: target.memory.kind, label: KIND_LABEL[target.memory.kind] ?? target.memory.kind } : target.kind === "node" ? { k: "node", label: node?.kind || "\u8282\u70B9" } : { k: "link", label: "\u5173\u7CFB" };
2438
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.root, children: [
2439
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.head, children: [
2440
- kindBadge(headerKind.k, headerKind.label),
2441
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.title, title: headerTitle, children: headerTitle }),
2442
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: hb2.close, onClick: onClose, "aria-label": "\u5173\u95ED\u8BE6\u60C5", children: "\u2715" })
2443
- ] }),
2444
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.body, children: [
2445
- target.kind === "task" && !task && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.v, children: "\u4EFB\u52A1\u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u5DF2\u88AB\u5173\u95ED/\u5F52\u6863\uFF09\u3002" }),
2446
- target.kind === "task" && task && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2447
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2448
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(EvidenceRing, { artifact: Boolean(task.artifact), evaluation: Boolean(task.evaluation), refs: (task.memoryRefs?.length ?? 0) > 0, size: 40 }),
2449
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
2450
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 12, fontWeight: 700 }, children: task.name }),
2451
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.mono, children: [
2452
- task.id,
2453
- " \xB7 ",
2454
- task.workspace
2455
- ] })
2456
- ] })
2457
- ] }),
2458
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2459
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u72B6\u6001" }),
2460
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 5, alignItems: "center", flexWrap: "wrap", marginTop: 3 }, children: [
2461
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...hb2.badge, background: task.state === "stable" ? "#059669" : task.state === "draft" ? "#6b7280" : gapsOf(task).length === 0 ? "#2563eb" : "#d97706" }, children: task.state === "stable" ? "STABLE" : task.state === "draft" ? "DRAFT" : gapsOf(task).length === 0 ? "READY" : "ACTIVE" }),
2462
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.tag, children: [
2463
- "\u521B\u5EFA ",
2464
- fmtDate(task.createdAt)
2465
- ] }),
2466
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.tag, children: [
2467
- "\u66F4\u65B0 ",
2468
- fmtDate(task.updatedAt)
2469
- ] })
2470
- ] })
2471
- ] }),
2472
- task.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2473
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u63CF\u8FF0" }),
2474
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...hb2.v, marginTop: 3 }, children: task.description })
2475
- ] }),
2476
- task.state === "active" && gapsOf(task).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2477
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u8BC1\u636E\u7F3A\u53E3" }),
2478
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: gapsOf(task).map((g) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.tag, children: [
2479
- g,
2480
- " \u2717"
2481
- ] }, g)) })
2482
- ] }),
2483
- task.artifact && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2484
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "artifact" }),
2485
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...hb2.mono, marginTop: 3, color: "#059669" }, children: [
2486
- "\u2713 ",
2487
- task.artifact
2488
- ] })
2489
- ] }),
2490
- task.evaluation && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2491
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "evaluation" }),
2492
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 3, color: "#059669" }, children: [
2493
- "\u2713 ",
2494
- task.evaluation
2495
- ] })
2496
- ] }),
2497
- task.memoryRefs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2498
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u8BB0\u5FC6\u5F15\u7528" }),
2499
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: task.memoryRefs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.chip, title: `\u6253\u5F00\u8BB0\u5FC6 ${r}`, onClick: () => onNavigateMemory(r), children: [
2500
- "#",
2501
- r.slice(0, 8)
2502
- ] }, r)) })
2503
- ] }),
2504
- task.state === "active" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 4, padding: 8, border: "1px solid var(--dsh-color-border, #d1d5db)", borderRadius: 10, background: dark ? "rgba(15,23,42,.35)" : "rgba(15,23,42,.03)" }, children: [
2505
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-color-muted, #6b7280)", marginBottom: 6 }, children: "\u8865\u9F50\u8BC1\u636E \u2192 \u5173\u95ED\uFF08\u4E09\u9879\u5168\u9F50\u8F6C STABLE\uFF09" }),
2506
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
2507
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { style: hb2.input, placeholder: "artifact \u4EA7\u7269\uFF08\u6587\u4EF6/PR/\u8DEF\uFF09", value: gArt, onChange: (e) => setGArt(e.target.value) }),
2508
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { style: hb2.input, placeholder: "evaluation \u8BC4\u4F30\uFF08\u6D4B\u8BD5/\u8BC4\u5BA1/\u5206\u6570\uFF09", value: gEval, onChange: (e) => setGEval(e.target.value) }),
2509
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { style: hb2.input, placeholder: "memory_refs\uFF08#id, \u9017\u53F7\u5206\u9694\uFF09", value: gRefs, onChange: (e) => setGRefs(e.target.value) }),
2510
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 6 }, children: [
2511
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: hb2.btnPrimary, disabled: busy, onClick: () => setClosing(true), children: busy ? "\u2026" : "\u6309\u8BC1\u636E\u95ED\u73AF" }),
2512
- closing && !busy && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2513
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)", alignSelf: "center" }, children: "\u786E\u8BA4\u63D0\u4EA4\uFF1F" }),
2514
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: hb2.btn, onClick: () => {
2515
- void submitClose();
2516
- setClosing(false);
2517
- }, children: "\u786E\u8BA4" }),
2518
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { style: hb2.btn, onClick: () => setClosing(false), children: "\u53D6\u6D88" })
2519
- ] })
2520
- ] })
2521
- ] })
2522
- ] }),
2523
- task.state === "stable" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11.5, color: "#059669", fontWeight: 700 }, children: "\u5DF2\u95ED\u73AF \u2713 \u2014 \u5168\u90E8\u8BC1\u636E\u95E8\u5DF2\u6EE1\u8DB3\uFF0C\u53EF\u4EE5\u4ECE\u770B\u677F/\u6E05\u5355\u79FB\u9664" }),
2524
- notice && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11.5, fontWeight: 600, color: "#d97706" }, children: notice })
2525
- ] }),
2526
- target.kind === "memory" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2527
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: 6 }, children: [
2528
- kindBadge(target.memory.kind, KIND_LABEL[target.memory.kind] ?? target.memory.kind),
2529
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.tag, children: [
2530
- "signal ",
2531
- target.memory.signal.toFixed(2)
2532
- ] }),
2533
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.tag, children: [
2534
- "hits ",
2535
- target.memory.hits
2536
- ] }),
2537
- target.memory.status === "archived" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: hb2.tag, children: "archived" })
2538
- ] }),
2539
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2540
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u5185\u5BB9" }),
2541
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...hb2.v, whiteSpace: "pre-wrap", background: dark ? "rgba(15,23,42,.5)" : "rgba(15,23,42,.05)", borderRadius: 8, padding: 8, marginTop: 3 }, children: target.memory.text })
2542
- ] }),
2543
- target.memory.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2544
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u6807\u7B7E" }),
2545
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: target.memory.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.tag, children: [
2546
- "#",
2547
- t
2548
- ] }, t)) })
2549
- ] }),
2550
- memoryLinks && (memoryLinks.m.supersedes || memoryLinks.m.contradicts || memoryLinks.replacedBy.length > 0 || memoryLinks.contradictedBy.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2551
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u66FF\u6362 / \u51B2\u7A81" }),
2552
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: [
2553
- memoryLinks.m.supersedes && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.chipSup, title: `\u672C\u6761\u66FF\u4EE3 ${memoryLinks.m.supersedes}\uFF08\u65E7\u884C\u4FDD\u7559\u3001\u53EC\u56DE\u964D\u7EA7\uFF09`, onClick: () => onNavigateMemory(memoryLinks.m.supersedes), children: [
2554
- "\u66FF\u4EE3 #",
2555
- memoryLinks.m.supersedes.slice(0, 8)
2556
- ] }),
2557
- memoryLinks.m.contradicts && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.chipDanger, title: `\u672C\u6761\u4E0E ${memoryLinks.m.contradicts} \u51B2\u7A81\uFF08\u4E24\u6761\u90FD\u4FDD\u7559\uFF09`, onClick: () => onNavigateMemory(memoryLinks.m.contradicts), children: [
2558
- "\u51B2\u7A81 #",
2559
- memoryLinks.m.contradicts.slice(0, 8)
2560
- ] }),
2561
- memoryLinks.replacedBy.map((x) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.chipSup, title: `\u5DF2\u88AB ${x.text.slice(0, 40)}\u2026 \u66FF\u4EE3`, onClick: () => onNavigateMemory(x.id), children: [
2562
- "\u2190 \u88AB #",
2563
- x.id.slice(0, 8),
2564
- " \u66FF\u4EE3"
2565
- ] }, x.id)),
2566
- memoryLinks.contradictedBy.map((x) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: hb2.chipDanger, title: `${x.text.slice(0, 40)}\u2026 \u4E0E\u672C\u6761\u51B2\u7A81`, onClick: () => onNavigateMemory(x.id), children: [
2567
- "\u21C4 #",
2568
- x.id.slice(0, 8),
2569
- " \u51B2\u7A81"
2570
- ] }, x.id))
2571
- ] })
2572
- ] }),
2573
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2574
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u5143\u6570\u636E" }),
2575
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 2, marginTop: 3, fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "var(--dsh-color-muted, #6b7280)" }, children: [
2576
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2577
- "id ",
2578
- target.memory.id
2579
- ] }),
2580
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2581
- "ws ",
2582
- target.memory.workspace
2583
- ] }),
2584
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2585
- "entity ",
2586
- target.memory.entity ?? "\u2014"
2587
- ] }),
2588
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2589
- "session ",
2590
- target.memory.sessionId,
2591
- " \xB7 seq ",
2592
- target.memory.seq
2593
- ] }),
2594
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2595
- "created ",
2596
- fmtDate(target.memory.createdAt),
2597
- " \xB7 updated ",
2598
- fmtDate(target.memory.updatedAt)
2599
- ] }),
2600
- target.memory.source && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2601
- "source ",
2602
- target.memory.source
2603
- ] }),
2604
- target.memory.conditions && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2605
- "conditions ",
2606
- target.memory.conditions
2607
- ] }),
2608
- target.memory.filePath && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2609
- "file ",
2610
- target.memory.filePath
2611
- ] }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2613
- "TTL ",
2614
- daysLeft(target.memory.expiresAt)
2615
- ] })
2616
- ] })
2617
- ] })
2618
- ] }),
2619
- target.kind === "node" && !node && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.v, children: "\u8282\u70B9\u4E0D\u5B58\u5728\u3002" }),
2620
- target.kind === "node" && node && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2621
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
2622
- kindBadge("node", node.kind || "\u8282\u70B9"),
2623
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: node.name })
2624
- ] }),
2625
- node.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2626
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u63CF\u8FF0" }),
2627
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { ...hb2.v, marginTop: 3 }, children: node.description })
2628
- ] }),
2629
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2630
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u5143\u6570\u636E" }),
2631
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 3, fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "var(--dsh-color-muted, #6b7280)", display: "flex", flexDirection: "column", gap: 2 }, children: [
2632
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2633
- node.id,
2634
- " \xB7 ",
2635
- node.workspace
2636
- ] }),
2637
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2638
- "created ",
2639
- fmtDate(node.createdAt)
2640
- ] })
2641
- ] })
2642
- ] }),
2643
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2644
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.k, children: [
2645
- "\u5173\u8054\u5173\u7CFB\uFF08",
2646
- incident.length,
2647
- "\uFF09"
2648
- ] }),
2649
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 5, marginTop: 3 }, children: [
2650
- incident.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.v, children: "\u6682\u65E0\u5173\u7CFB \u2014 \u7528 esr_link \u5173\u8054" }),
2651
- incident.map(({ l, outgoing }) => {
2652
- const other = outgoing ? l.target : l.source;
2653
- const color = REL_COLORS[l.relation] ?? "#6b7280";
2654
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.relRow, children: [
2655
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...hb2.dot, background: color } }),
2656
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "mono", style: { fontSize: 11 }, children: [
2657
- outgoing ? "\u2192" : "\u2190",
2658
- " ",
2659
- nameOf(other)
2660
- ] }),
2661
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: hb2.relType, children: l.relation }),
2662
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { fontSize: 11, color: "var(--dsh-color-muted-weak, #9ca3af)" }, children: [
2663
- Math.round(l.confidence * 100),
2664
- "%"
2665
- ] }),
2666
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-color-muted-weak, #9ca3af)" }, children: fmtDate(l.createdAt) })
2667
- ] }, l.id);
2668
- })
2669
- ] })
2670
- ] })
2671
- ] }),
2672
- target.kind === "link" && link && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2673
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.relRow, children: [
2674
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mono", style: { fontSize: 12, fontWeight: 600 }, children: nameOf(link.source) }),
2675
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { ...hb2.relType, color: REL_COLORS[link.relation] ?? "var(--dsh-color-muted, #6b7280)", borderColor: (REL_COLORS[link.relation] ?? "#cbd5e1") + "66" }, children: link.relation }),
2676
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { "aria-hidden": true, children: "\u2192" }),
2677
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mono", style: { fontSize: 12, fontWeight: 600 }, children: nameOf(link.target) })
2678
- ] }),
2679
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: hb2.section, children: [
2680
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: hb2.k, children: "\u5143\u6570\u636E" }),
2681
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: 3, fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "var(--dsh-color-muted, #6b7280)", display: "flex", flexDirection: "column", gap: 2 }, children: [
2682
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2683
- "id ",
2684
- link.id,
2685
- " \xB7 ",
2686
- link.workspace
2687
- ] }),
2688
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2689
- "confidence ",
2690
- Math.round(link.confidence * 100),
2691
- "%"
2692
- ] }),
2693
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2694
- "created ",
2695
- fmtDate(link.createdAt)
2696
- ] }),
2697
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
2698
- link.source,
2699
- " \u2192 ",
2700
- link.target
2701
- ] })
2702
- ] })
2703
- ] })
2704
- ] })
2705
- ] })
2706
- ] });
2707
- }
2708
-
2709
- // client/src/EngramSection.tsx
2710
- var import_jsx_runtime7 = require("react/jsx-runtime");
2711
- function signedTokens(n) {
2712
- const sign = n > 0 ? "+" : n < 0 ? "\u2212" : "";
2713
- const abs = Math.abs(n);
2714
- return abs >= 1e3 ? `${sign}${(abs / 1e3).toFixed(1)}k` : `${sign}${abs}`;
2715
- }
2716
- var KIND_COLORS2 = {
2717
- preference: "#b45309",
2718
- episode: "#0e7490",
2719
- decision: "#2563eb",
2720
- error: "#dc2626",
2721
- procedure: "#7c3aed",
2722
- fact: "#059669",
2723
- insight: "#d97706",
2724
- handoff: "#0891b2",
2725
- task: "#4f46e5"
2726
- };
2727
- var KIND_LABEL2 = {
2728
- preference: "\u504F\u597D",
2729
- decision: "\u51B3\u5B9A",
2730
- error: "\u9519\u8BEF",
2731
- procedure: "\u6D41\u7A0B",
2732
- fact: "\u4E8B\u5B9E",
2733
- insight: "\u6D1E\u5BDF",
2734
- handoff: "\u4EA4\u63A5",
2735
- task: "\u4EFB\u52A1"
2736
- };
2376
+ ];
2377
+ var FIELDS = [...COMMON_FIELDS, ...ADVANCED_GROUPS.flatMap((group) => group.fields)];
2378
+ var CARD_DESCRIPTION = "\u63A7\u5236 engram \u8BB0\u5FC6\u7684\u6355\u83B7\u3001\u7D22\u5F15\u3001\u4FDD\u7559\u4E0E\u96A7\u9053\u8BBF\u95EE";
2737
2379
  var s2 = {
2738
- root: { padding: "2px 4px 40px" },
2739
- h1: { fontSize: 20, fontWeight: 700, margin: "0 0 4px" },
2740
- sub: { color: "var(--dsh-color-muted, #6b7280)", fontSize: 12, margin: "0 0 16px" },
2741
- stats: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 8, marginBottom: 16 },
2742
2380
  card: {
2743
- border: "1px solid var(--dsh-color-border, #e5e7eb)",
2381
+ listStyle: "none",
2382
+ border: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
2744
2383
  borderRadius: 12,
2745
- padding: "10px 12px",
2746
- background: "var(--dsh-color-surface, #ffffff)",
2747
- boxShadow: "0 1px 2px rgba(15,23,42,.04), 0 2px 8px rgba(15,23,42,.03)"
2384
+ background: "var(--dsw-alias-bg-layer-3, #ffffff)",
2385
+ transition: "border-color .16s, background .16s"
2748
2386
  },
2749
- cardNum: { fontSize: 22, fontWeight: 700, lineHeight: 1.2 },
2750
- cardLabel: { color: "var(--dsh-color-muted, #6b7280)", fontSize: 11, marginTop: 2 },
2751
- row: { display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: 12 },
2387
+ cardOpen: {
2388
+ background: "var(--dsw-alias-bg-layer-2, #ffffff)",
2389
+ borderColor: "var(--dsw-alias-label-dimmed, #9ca3af)"
2390
+ },
2391
+ header: {
2392
+ width: "100%",
2393
+ appearance: "none",
2394
+ border: 0,
2395
+ background: "none",
2396
+ font: "inherit",
2397
+ color: "inherit",
2398
+ textAlign: "left",
2399
+ cursor: "pointer",
2400
+ display: "flex",
2401
+ alignItems: "center",
2402
+ gap: 12,
2403
+ padding: "14px 16px",
2404
+ borderRadius: 12
2405
+ },
2406
+ headText: {
2407
+ flex: 1,
2408
+ minWidth: 0,
2409
+ display: "flex",
2410
+ flexDirection: "column",
2411
+ gap: 4
2412
+ },
2413
+ name: { fontSize: 15, fontWeight: 600, lineHeight: 1.4, color: "var(--dsw-alias-label-primary, #111827)" },
2414
+ description: { fontSize: 13, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary, #6b7280)" },
2415
+ pending: {
2416
+ flex: "none",
2417
+ borderRadius: 999,
2418
+ padding: "1px 8px",
2419
+ fontSize: 11,
2420
+ lineHeight: "17px",
2421
+ fontWeight: 500,
2422
+ whiteSpace: "nowrap",
2423
+ background: "var(--dsw-alias-bg-module-platform, #f3f4f6)",
2424
+ color: "var(--dsw-alias-label-secondary, #4b5563)"
2425
+ },
2426
+ body: {
2427
+ borderTop: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
2428
+ margin: "0 16px",
2429
+ paddingBottom: 8
2430
+ },
2431
+ readOnly: { margin: "12px 0 0", fontSize: 12, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary, #6b7280)" },
2432
+ groupPanel: {
2433
+ marginTop: 10,
2434
+ border: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
2435
+ borderRadius: 10,
2436
+ padding: "8px 12px 2px",
2437
+ background: "var(--dsw-alias-bg-layer-3, #ffffff)"
2438
+ },
2439
+ groupHead: { display: "flex", alignItems: "baseline", gap: 8, marginBottom: 2 },
2440
+ groupTitle: { fontSize: 13, fontWeight: 700, letterSpacing: 0.2, color: "var(--dsw-alias-label-primary, #111827)" },
2441
+ groupDesc: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #6b7280)" },
2442
+ row: {
2443
+ display: "flex",
2444
+ alignItems: "center",
2445
+ justifyContent: "space-between",
2446
+ gap: 12,
2447
+ padding: "8px 0",
2448
+ borderBottom: "1px solid var(--dsw-alias-border-l1, #f3f4f6)"
2449
+ },
2450
+ label: { fontSize: 13, fontWeight: 600, color: "var(--dsw-alias-label-primary, #111827)" },
2451
+ hint: { fontSize: 11.5, lineHeight: 1.4, color: "var(--dsw-alias-label-tertiary, #6b7280)", marginTop: 2 },
2752
2452
  input: {
2753
- border: "1px solid var(--dsh-color-border, #d1d5db)",
2453
+ width: 90,
2454
+ border: "1px solid var(--dsw-alias-border-l2, #d1d5db)",
2754
2455
  borderRadius: 6,
2755
- padding: "5px 8px",
2756
- fontSize: 13,
2757
- background: "var(--dsh-color-surface, #fff)",
2456
+ padding: "4px 7px",
2457
+ fontSize: 12.5,
2458
+ background: "var(--dsw-alias-bg-layer-3, #fff)",
2758
2459
  color: "inherit"
2759
2460
  },
2760
- btn: {
2761
- border: "1px solid var(--dsh-color-border, #d1d5db)",
2762
- borderRadius: 8,
2763
- padding: "5px 11px",
2461
+ footer: {
2462
+ display: "flex",
2463
+ alignItems: "center",
2464
+ justifyContent: "flex-end",
2465
+ gap: 8,
2466
+ padding: "12px 0 4px",
2467
+ borderTop: "1px solid var(--dsw-alias-border-l2, #e5e7eb)"
2468
+ },
2469
+ reset: {
2470
+ appearance: "none",
2471
+ border: 0,
2472
+ background: "none",
2473
+ font: "inherit",
2764
2474
  fontSize: 12,
2765
2475
  cursor: "pointer",
2766
- background: "var(--dsh-color-surface, #fff)",
2767
- transition: "background .15s ease, border-color .15s ease"
2476
+ color: "var(--dsw-alias-label-tertiary, #6b7280)"
2768
2477
  },
2769
- btnPrimary: {
2770
- border: "1px solid transparent",
2478
+ discard: {
2479
+ appearance: "none",
2480
+ border: "1px solid var(--dsw-alias-border-l2, #d1d5db)",
2771
2481
  borderRadius: 8,
2772
- padding: "5px 11px",
2773
- fontSize: 12,
2482
+ padding: "5px 14px",
2483
+ font: "inherit",
2484
+ fontSize: 13,
2485
+ lineHeight: 1.5,
2774
2486
  cursor: "pointer",
2775
- color: "#fff",
2776
- background: "linear-gradient(135deg, #6366f1 0%, #7c3aed 100%)",
2777
- boxShadow: "0 1px 3px rgba(99,102,241,.35)",
2778
- transition: "opacity .15s ease"
2487
+ background: "none",
2488
+ color: "var(--dsw-alias-label-secondary, #4b5563)"
2779
2489
  },
2780
- table: { width: "100%", borderCollapse: "collapse", fontSize: 12.5, tableLayout: "fixed" },
2781
- clamp3: { display: "-webkit-box", WebkitLineClamp: 3, WebkitBoxOrient: "vertical", overflow: "hidden", maxHeight: 60, minHeight: 18, lineHeight: 1.5, wordBreak: "break-word", whiteSpace: "normal" },
2782
- expanded: { whiteSpace: "pre-wrap", wordBreak: "break-word", lineHeight: 1.5 },
2783
- linkBtn: { border: "none", background: "none", padding: 0, fontSize: 11.5, cursor: "pointer", color: "var(--dsh-color-primary, #2563eb)", textDecoration: "underline" },
2784
- pageBar: { display: "flex", alignItems: "center", gap: 8, marginTop: 8, fontSize: 12.5, color: "var(--dsh-color-muted, #6b7280)", flexWrap: "wrap" },
2785
- tabBar: {
2786
- display: "inline-flex",
2787
- gap: 4,
2788
- padding: 3,
2789
- background: "var(--dsh-color-hover-bg, #f3f4f6)",
2790
- borderRadius: 999,
2791
- marginBottom: 14
2792
- },
2793
- tab: {
2794
- border: "none",
2795
- background: "none",
2796
- padding: "6px 16px",
2797
- fontSize: 13,
2798
- cursor: "pointer",
2799
- color: "var(--dsh-color-muted, #6b7280)",
2800
- borderRadius: 999,
2801
- fontWeight: 600,
2802
- margin: 0,
2803
- transition: "background .16s ease, color .16s ease"
2804
- },
2805
- tabActive: {
2806
- border: "none",
2807
- background: "var(--dsh-color-surface, #fff)",
2808
- color: "var(--dsh-color-primary, #2563eb)",
2809
- padding: "6px 16px",
2490
+ save: {
2491
+ appearance: "none",
2492
+ border: "1px solid transparent",
2493
+ borderRadius: 8,
2494
+ padding: "5px 14px",
2495
+ font: "inherit",
2810
2496
  fontSize: 13,
2497
+ lineHeight: 1.5,
2811
2498
  cursor: "pointer",
2812
- borderRadius: 999,
2813
- fontWeight: 700,
2814
- margin: 0,
2815
- boxShadow: "0 1px 3px rgba(15,23,42,.10)"
2816
- },
2817
- th: {
2818
- textAlign: "left",
2819
- padding: "6px 8px",
2820
- borderBottom: "1px solid var(--dsh-color-border, #e5e7eb)",
2821
- color: "var(--dsh-color-muted, #6b7280)",
2822
- fontWeight: 600,
2823
- whiteSpace: "nowrap"
2824
- },
2825
- td: { padding: "6px 8px", borderBottom: "1px solid var(--dsh-color-border, #f3f4f6)", verticalAlign: "top" },
2826
- wsHead: {
2827
- padding: "8px 8px 4px",
2828
- fontSize: 12.5,
2829
- fontWeight: 600,
2830
- color: "var(--dsh-color-muted, #4b5563)",
2831
- background: "var(--dsh-color-hover-bg, #f3f4f6)",
2832
- borderBottom: "1px solid var(--dsh-color-border, #e5e7eb)"
2833
- },
2834
- badge: { display: "inline-block", borderRadius: 5, padding: "1px 6px", fontSize: 11, color: "#fff", whiteSpace: "nowrap" },
2835
- tag: {
2836
- display: "inline-block",
2837
- borderRadius: 4,
2838
- padding: "0 5px",
2839
- fontSize: 11,
2840
- marginRight: 4,
2841
- background: "var(--dsh-color-hover-bg, #f3f4f6)"
2842
- },
2843
- panelTitle: { fontSize: 14, fontWeight: 700, margin: "18px 0 6px" },
2844
- groupLabel: { fontSize: 12.5, fontWeight: 600, color: "var(--dsh-color-muted, #4b5563)", margin: "8px 0 4px" },
2845
- mono: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11.5, color: "var(--dsh-color-muted, #4b5563)" },
2846
- empty: { color: "var(--dsh-color-muted, #9ca3af)", fontSize: 12.5, padding: "14px 4px" },
2847
- error: { color: "#dc2626", fontSize: 12.5, marginBottom: 10 },
2848
- subPanel: {
2849
- border: "1px solid var(--dsh-color-border, #e5e7eb)",
2850
- borderRadius: 12,
2851
- padding: "10px 12px",
2852
- marginBottom: 10,
2853
- background: "var(--dsh-color-surface, #fff)",
2854
- boxShadow: "0 1px 2px rgba(15,23,42,.03)"
2499
+ background: "var(--dsw-alias-label-primary, #111827)",
2500
+ color: "var(--dsw-alias-bg-layer-3, #ffffff)"
2855
2501
  },
2856
- relRow: {
2502
+ disabled: { opacity: 0.4, cursor: "default" },
2503
+ failed: { flex: 1, minWidth: 0, margin: 0, fontSize: 12, lineHeight: 1.5, color: "#dc2626" },
2504
+ note: { fontSize: 11.5, color: "var(--dsw-alias-label-tertiary, #9ca3af)", marginTop: 8 },
2505
+ advHead: {
2857
2506
  display: "flex",
2858
2507
  alignItems: "center",
2859
- gap: 6,
2860
- flexWrap: "wrap",
2861
- padding: "5px 8px",
2508
+ justifyContent: "space-between",
2509
+ gap: 8,
2510
+ width: "100%",
2511
+ appearance: "none",
2512
+ background: "none",
2513
+ border: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
2862
2514
  borderRadius: 10,
2863
- border: "1px solid var(--dsh-color-border, #f3f4f6)",
2864
- background: "var(--dsh-color-hover-bg, #f8fafc)"
2515
+ padding: "8px 12px",
2516
+ marginTop: 10,
2517
+ font: "inherit",
2518
+ cursor: "pointer",
2519
+ color: "var(--dsw-alias-label-secondary, #4b5563)",
2520
+ textAlign: "left"
2865
2521
  },
2866
- nodePill: {
2867
- display: "inline-flex",
2868
- alignItems: "center",
2869
- maxWidth: 220,
2870
- overflow: "hidden",
2871
- textOverflow: "ellipsis",
2872
- whiteSpace: "nowrap",
2873
- borderRadius: 8,
2874
- padding: "2px 9px",
2875
- fontSize: 12,
2876
- fontWeight: 600,
2877
- background: "rgba(99,102,241,.12)",
2878
- color: "#4338ca"
2879
- }
2522
+ advHeadLabel: { fontSize: 12.5, fontWeight: 600, letterSpacing: 0.2, color: "var(--dsw-alias-label-secondary, #4b5563)" }
2880
2523
  };
2881
- function fmtDate2(ts) {
2882
- const d = new Date(ts);
2883
- const mm = String(d.getMonth() + 1).padStart(2, "0");
2884
- const dd = String(d.getDate()).padStart(2, "0");
2885
- return `${mm}-${dd} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
2886
- }
2887
- var MEM_PAGE_SIZE = 20;
2888
- function daysLeft2(ts) {
2889
- if (ts === null || ts === void 0) return "\u221E";
2890
- const days = Math.ceil((ts - Date.now()) / 864e5);
2891
- return days > 0 ? `${days}d` : "expired";
2892
- }
2893
- function StatCard({ num, label }) {
2894
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.card, children: [
2895
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.cardNum, children: num }),
2896
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.cardLabel, children: label })
2897
- ] });
2898
- }
2899
- function groupByWorkspace(items) {
2900
- const groups = /* @__PURE__ */ new Map();
2901
- for (const m of items) {
2902
- const list = groups.get(m.workspace) ?? [];
2903
- list.push(m);
2904
- groups.set(m.workspace, list);
2905
- }
2906
- return [...groups.entries()].sort(
2907
- (a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])
2524
+ function Chevron({ open }) {
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2526
+ "svg",
2527
+ {
2528
+ width: "14",
2529
+ height: "14",
2530
+ viewBox: "0 0 14 14",
2531
+ "aria-hidden": "true",
2532
+ style: {
2533
+ flex: "none",
2534
+ color: "var(--dsw-alias-label-tertiary, #6b7280)",
2535
+ transition: "transform .16s",
2536
+ transform: open ? "rotate(180deg)" : void 0
2537
+ },
2538
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2539
+ "path",
2540
+ {
2541
+ d: "M3 5.5 L7 9 L11 5.5",
2542
+ fill: "none",
2543
+ stroke: "currentColor",
2544
+ strokeWidth: "1.6",
2545
+ strokeLinecap: "round",
2546
+ strokeLinejoin: "round"
2547
+ }
2548
+ )
2549
+ }
2908
2550
  );
2909
2551
  }
2910
- function EngramSection({ api, t }) {
2911
- const [overview, setOverview] = (0, import_react7.useState)(null);
2912
- const [memories, setMemories] = (0, import_react7.useState)([]);
2913
- const [tasks, setTasks] = (0, import_react7.useState)([]);
2914
- const [links, setLinks] = (0, import_react7.useState)([]);
2915
- const [nodes, setNodes] = (0, import_react7.useState)([]);
2916
- const [detail, setDetail] = (0, import_react7.useState)(null);
2917
- const [detailNotice, setDetailNotice] = (0, import_react7.useState)(null);
2918
- const [error, setError] = (0, import_react7.useState)(null);
2919
- const [workspace, setWorkspace] = (0, import_react7.useState)("");
2920
- const [kind, setKind] = (0, import_react7.useState)("");
2921
- const [status, setStatus] = (0, import_react7.useState)("active");
2922
- const [q, setQ] = (0, import_react7.useState)("");
2923
- const [busy, setBusy] = (0, import_react7.useState)(false);
2924
- const [memPage, setMemPage] = (0, import_react7.useState)(0);
2925
- const [expandedRows, setExpandedRows] = (0, import_react7.useState)(/* @__PURE__ */ new Set());
2926
- const [view, setView] = (0, import_react7.useState)("mem");
2927
- const [esrGraph, setEsrGraph] = (0, import_react7.useState)(false);
2928
- const [gcDryRun, setGcDryRun] = (0, import_react7.useState)(true);
2929
- const [gcReport, setGcReport] = (0, import_react7.useState)(null);
2930
- const [gcRunning, setGcRunning] = (0, import_react7.useState)(false);
2931
- const [newTaskWs, setNewTaskWs] = (0, import_react7.useState)("");
2932
- const [newTaskName, setNewTaskName] = (0, import_react7.useState)("");
2933
- const [newTaskDesc, setNewTaskDesc] = (0, import_react7.useState)("");
2934
- const [newTaskBusy, setNewTaskBusy] = (0, import_react7.useState)(false);
2935
- const [closeFor, setCloseFor] = (0, import_react7.useState)(null);
2936
- const [closeArtifact, setCloseArtifact] = (0, import_react7.useState)("");
2937
- const [closeEval, setCloseEval] = (0, import_react7.useState)("");
2938
- const [closeRefs, setCloseRefs] = (0, import_react7.useState)("");
2939
- const [closeBusy, setCloseBusy] = (0, import_react7.useState)(false);
2940
- const [importDump, setImportDump] = (0, import_react7.useState)(null);
2941
- const [importName, setImportName] = (0, import_react7.useState)("");
2942
- const [importMode, setImportMode] = (0, import_react7.useState)("merge");
2943
- const [importDryRun, setImportDryRun] = (0, import_react7.useState)(true);
2944
- const [importReplaceOk, setImportReplaceOk] = (0, import_react7.useState)(false);
2945
- const [importBusy, setImportBusy] = (0, import_react7.useState)(false);
2946
- const [importReport, setImportReport] = (0, import_react7.useState)(null);
2947
- const [dataError, setDataError] = (0, import_react7.useState)(null);
2948
- const refresh = (0, import_react7.useCallback)(async () => {
2949
- setBusy(true);
2552
+ function EngramConfigCard({ scope, defaultOpen }) {
2553
+ const [snap, setSnap] = (0, import_react6.useState)(null);
2554
+ const [draft, setDraft] = (0, import_react6.useState)({});
2555
+ const [saving, setSaving] = (0, import_react6.useState)(false);
2556
+ const [error, setError] = (0, import_react6.useState)(null);
2557
+ const [open, setOpen] = (0, import_react6.useState)(defaultOpen ?? false);
2558
+ const [advancedOpen, setAdvancedOpen] = (0, import_react6.useState)(false);
2559
+ (0, import_react6.useEffect)(() => {
2560
+ void scope.load();
2561
+ const off = scope.subscribe(() => {
2562
+ const next = scope.getSnapshot();
2563
+ setSnap(next);
2564
+ if (next.status === "unavailable") {
2565
+ console.warn("[dsh-engram] settings scope unavailable \u2014 Plugins \u914D\u7F6E\u5361\u5DF2\u9690\u85CF:", next.reason ?? "no reason served");
2566
+ }
2567
+ const value2 = next.value ?? next.base;
2568
+ if (value2) setDraft((prev) => ({ ...value2, ...prev }));
2569
+ });
2570
+ setSnap(scope.getSnapshot());
2571
+ return off;
2572
+ }, [scope]);
2573
+ const setField = (0, import_react6.useCallback)((key, value2) => {
2574
+ setDraft((prev) => ({ ...prev, [key]: value2 }));
2575
+ }, []);
2576
+ const effective = snap?.value ?? snap?.base ?? {};
2577
+ const anyDirty = FIELDS.some((field) => {
2578
+ const staged = draft[field.key];
2579
+ return staged !== void 0 && staged !== effective[field.key];
2580
+ });
2581
+ const save = (0, import_react6.useCallback)(async () => {
2582
+ setSaving(true);
2950
2583
  setError(null);
2951
2584
  try {
2952
- const ov = await api.overview();
2953
- setOverview(ov);
2954
- const wsList = workspace ? [workspace] : Object.keys(ov.workspaces);
2955
- const [mem, taskGroups2, linkGroups2, nodeGroups2] = await Promise.all([
2956
- api.memories({
2957
- workspace: workspace || void 0,
2958
- q: q || void 0,
2959
- kind: kind || void 0,
2960
- status: status === "all" ? void 0 : status
2961
- }),
2962
- Promise.all(wsList.map((w) => api.tasks(w, true))),
2963
- Promise.all(wsList.map((w) => api.links(w))),
2964
- Promise.all(wsList.map((w) => api.nodes(w)))
2965
- ]);
2966
- setMemories(mem.items);
2967
- setTasks(taskGroups2.flatMap((x) => x.items));
2968
- setLinks(linkGroups2.flatMap((x) => x.items));
2969
- setNodes(nodeGroups2.flatMap((x) => x.items));
2585
+ for (const field of FIELDS) {
2586
+ const value2 = draft[field.key];
2587
+ if (value2 === void 0) continue;
2588
+ if (value2 === effective[field.key]) continue;
2589
+ if (typeof value2 === "number" && Number.isNaN(value2)) continue;
2590
+ await scope.set(field.key, value2);
2591
+ }
2592
+ await scope.load();
2970
2593
  } catch (e) {
2971
- setError(e instanceof Error ? e.message : String(e));
2594
+ setError(String(e instanceof Error ? e.message : e));
2972
2595
  } finally {
2973
- setBusy(false);
2596
+ setSaving(false);
2974
2597
  }
2975
- }, [api, workspace, q, kind, status]);
2976
- (0, import_react7.useEffect)(() => {
2977
- void refresh();
2978
- const id = setInterval(() => void refresh(), POLL_MS);
2979
- return () => clearInterval(id);
2980
- }, [refresh]);
2981
- const workspaces = (0, import_react7.useMemo)(() => overview ? Object.keys(overview.workspaces) : [], [overview]);
2982
- const kindsPresent = (0, import_react7.useMemo)(() => overview ? Object.keys(overview.kinds) : [], [overview]);
2983
- const previewDefault = (0, import_react7.useMemo)(() => {
2984
- if (workspace !== "") return workspace;
2985
- if (!overview) return "";
2986
- let best = "";
2987
- let bestN = -1;
2988
- for (const ws of Object.keys(overview.workspaces)) {
2989
- const c = overview.workspaces[ws] ?? {};
2990
- const n = (c.memories ?? 0) + (c.tasks ?? 0) + (c.links ?? 0) + (c.nodes ?? 0);
2991
- if (n > bestN) {
2992
- bestN = n;
2993
- best = ws;
2994
- }
2598
+ }, [draft, scope, effective]);
2599
+ const discard = (0, import_react6.useCallback)(() => {
2600
+ setError(null);
2601
+ const value2 = snap?.value ?? snap?.base ?? {};
2602
+ setDraft({ ...value2 });
2603
+ }, [snap]);
2604
+ const resetField = (0, import_react6.useCallback)(async (key) => {
2605
+ setError(null);
2606
+ try {
2607
+ await scope.unset(key);
2608
+ await scope.load();
2609
+ setDraft((prev) => ({ ...prev, [key]: void 0 }));
2610
+ } catch (e) {
2611
+ setError(String(e instanceof Error ? e.message : e));
2995
2612
  }
2996
- return best;
2997
- }, [workspace, overview]);
2998
- const nameOf = (0, import_react7.useMemo)(() => {
2999
- const map = /* @__PURE__ */ new Map();
3000
- for (const n of nodes) map.set(n.id, n.name);
3001
- for (const t2 of tasks) map.set(t2.id, t2.name);
3002
- return (id) => map.get(id) ?? id;
3003
- }, [nodes, tasks]);
3004
- const supersedeIndex = (0, import_react7.useMemo)(() => {
3005
- const replacedBy = /* @__PURE__ */ new Map();
3006
- const contradictedBy = /* @__PURE__ */ new Map();
3007
- for (const m of memories) {
3008
- if (m.status !== "active") continue;
3009
- if (m.supersedes) {
3010
- const list = replacedBy.get(m.supersedes) ?? [];
3011
- list.push(m);
3012
- replacedBy.set(m.supersedes, list);
3013
- }
3014
- if (m.contradicts) {
3015
- const list = contradictedBy.get(m.contradicts) ?? [];
3016
- list.push(m);
3017
- contradictedBy.set(m.contradicts, list);
3018
- }
3019
- }
3020
- return { replacedBy, contradictedBy };
3021
- }, [memories]);
3022
- (0, import_react7.useEffect)(() => {
3023
- if (!newTaskWs && workspaces.length > 0 && !workspace) setNewTaskWs(workspaces[0]);
3024
- }, [newTaskWs, workspaces, workspace]);
3025
- const cfg = overview?.config ?? null;
3026
- const act = async (fn) => {
3027
- try {
3028
- await fn();
3029
- await refresh();
3030
- } catch (e) {
3031
- setError(e instanceof Error ? e.message : String(e));
3032
- }
3033
- };
3034
- const toggleExpand = (id) => {
3035
- setExpandedRows((prev) => {
3036
- const next = new Set(prev);
3037
- if (next.has(id)) next.delete(id);
3038
- else next.add(id);
3039
- return next;
3040
- });
3041
- };
3042
- const runGc = async () => {
3043
- setGcRunning(true);
3044
- setError(null);
3045
- try {
3046
- const { report } = await api.gc(workspace || void 0, gcDryRun);
3047
- setGcReport(report);
3048
- if (!gcDryRun) await refresh();
3049
- } catch (e) {
3050
- setError(e instanceof Error ? e.message : String(e));
3051
- } finally {
3052
- setGcRunning(false);
3053
- }
3054
- };
3055
- const downloadExport = async () => {
3056
- setDataError(null);
3057
- try {
3058
- const dump = await api.exportData(workspace || void 0);
3059
- const blob = new Blob([JSON.stringify(dump, null, 2)], { type: "application/json" });
3060
- const url = URL.createObjectURL(blob);
3061
- const a = document.createElement("a");
3062
- const safe = (workspace || "all").replace(/[^a-zA-Z0-9._-]+/g, "_");
3063
- a.href = url;
3064
- a.download = `dsh-engram-export-${safe}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.json`;
3065
- a.click();
3066
- URL.revokeObjectURL(url);
3067
- } catch (e) {
3068
- setDataError(e instanceof Error ? e.message : String(e));
3069
- }
3070
- };
3071
- const pickImportFile = async (file) => {
3072
- setDataError(null);
3073
- setImportReport(null);
3074
- setImportDump(null);
3075
- setImportName("");
3076
- if (!file) return;
3077
- try {
3078
- const text = await file.text();
3079
- setImportDump(JSON.parse(text));
3080
- setImportName(file.name);
3081
- } catch (e) {
3082
- setDataError(`\u65E0\u6CD5\u89E3\u6790 ${file.name}\uFF1A${e instanceof Error ? e.message : String(e)}`);
3083
- }
3084
- };
3085
- const runImport = async () => {
3086
- if (importDump === null) return;
3087
- if (importMode === "replace" && !importReplaceOk) {
3088
- setDataError("\u66FF\u6362\u6A21\u5F0F\u4F1A\u5148\u6E05\u7A7A\u76EE\u6807\u5DE5\u4F5C\u533A \u2014 \u8BF7\u5148\u52FE\u9009\u786E\u8BA4");
3089
- return;
3090
- }
3091
- setImportBusy(true);
3092
- setDataError(null);
3093
- try {
3094
- const report = await api.importData({
3095
- payload: importDump,
3096
- mode: importMode,
3097
- dryRun: importDryRun,
3098
- workspace: workspace || void 0,
3099
- confirm: importMode === "replace" ? "restore" : void 0
3100
- });
3101
- setImportReport(report);
3102
- if (!report.dryRun) await refresh();
3103
- } catch (e) {
3104
- setDataError(e instanceof Error ? e.message : String(e));
3105
- } finally {
3106
- setImportBusy(false);
3107
- }
3108
- };
3109
- const groupedRows = workspace === "" ? groupByWorkspace(memories) : [[workspace, memories]];
3110
- const taskGroups = workspace === "" ? groupByWorkspace(tasks) : [[workspace, tasks]];
3111
- const linkGroups = workspace === "" ? groupByWorkspace(links) : [[workspace, links]];
3112
- const nodeGroups = workspace === "" ? groupByWorkspace(nodes) : [[workspace, nodes]];
3113
- const flatRows = [];
3114
- for (const [ws, items] of groupedRows) {
3115
- if (workspace === "") flatRows.push({ kind: "head", ws, count: items.length });
3116
- for (const m of items) flatRows.push({ kind: "row", m });
3117
- }
3118
- const memPageCount = Math.max(1, Math.ceil(flatRows.length / MEM_PAGE_SIZE));
3119
- const memPageSafe = Math.min(memPage, memPageCount - 1);
3120
- const memPageRows = flatRows.slice(memPageSafe * MEM_PAGE_SIZE, (memPageSafe + 1) * MEM_PAGE_SIZE);
3121
- (0, import_react7.useEffect)(() => {
3122
- setMemPage(0);
3123
- }, [workspace, status, kind, q]);
3124
- (0, import_react7.useEffect)(() => {
3125
- if (memPage >= memPageCount) setMemPage(Math.max(0, memPageCount - 1));
3126
- }, [memPage, memPageCount]);
3127
- const createNewTask = async () => {
3128
- const ws = newTaskWs || workspace || workspaces[0] || "";
3129
- if (!ws || newTaskName.trim() === "") return;
3130
- setNewTaskBusy(true);
3131
- setError(null);
3132
- try {
3133
- await api.createTask(ws, newTaskName.trim(), newTaskDesc);
3134
- setNewTaskName("");
3135
- setNewTaskDesc("");
3136
- await refresh();
3137
- } catch (e) {
3138
- setError(e instanceof Error ? e.message : String(e));
3139
- } finally {
3140
- setNewTaskBusy(false);
3141
- }
3142
- };
3143
- const submitClose = async (task) => {
3144
- setCloseBusy(true);
3145
- setError(null);
3146
- try {
3147
- const refs = closeRefs.split(/[,\s]+/).map((x) => x.trim()).filter(Boolean);
3148
- const out = await api.closeTask(task.workspace, task.id, {
3149
- artifact: closeArtifact,
3150
- evaluation: closeEval,
3151
- memoryRefs: refs
3152
- });
3153
- if (out.state === "active") {
3154
- setError(`\u8BC1\u636E\u4ECD\u6709\u7F3A\u53E3\uFF1A${(out.gaps ?? []).join(", ")}${out.artifactReason ? `\uFF08${out.artifactReason}\uFF09` : ""} \u2014 \u4EFB\u52A1\u4FDD\u6301 ACTIVE\uFF0C\u8865\u9F50\u540E\u518D\u63D0\u4EA4`);
3155
- }
3156
- setCloseFor(null);
3157
- setCloseArtifact("");
3158
- setCloseEval("");
3159
- setCloseRefs("");
3160
- await refresh();
3161
- } catch (e) {
3162
- setError(e instanceof Error ? e.message : String(e));
3163
- } finally {
3164
- setCloseBusy(false);
3165
- }
3166
- };
3167
- const openMemory = (id) => {
3168
- const m = memories.find((mem) => mem.id === id);
3169
- if (m) {
3170
- setDetail({ kind: "memory", memory: m });
3171
- setDetailNotice(null);
3172
- } else {
3173
- setDetailNotice(`\u8BB0\u5FC6 ${id} \u4E0D\u5728\u5F53\u524D\u52A0\u8F7D\u5217\u8868 \u2014 \u5207\u6362\u5230\u300C\u8BB0\u5FC6\u300D\u9875\u52A0\u8F7D\u540E\u53EF\u7528`);
3174
- }
3175
- };
3176
- const openTask = (id) => {
3177
- setDetail({ kind: "task", id });
3178
- setDetailNotice(null);
3179
- };
3180
- const openNode = (id) => {
3181
- setDetail({ kind: "node", id });
3182
- setDetailNotice(null);
3183
- };
3184
- const indexCost = workspace && overview ? overview.indexes[workspace] : null;
3185
- const gc = overview?.gc ?? null;
3186
- const wsCounts = workspace && overview ? overview.workspaces[workspace] ?? null : null;
2613
+ }, [scope]);
2614
+ const value = { ...snap?.base ?? {}, ...draft };
2615
+ const writable = snap?.writable !== false && snap?.status !== "unavailable";
2616
+ const available = snap?.status === "ready";
3187
2617
  const { vars } = useEngramTheme();
3188
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...s2.root, ...vars }, children: [
3189
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { style: s2.h1, children: "Engram \u8BB0\u5FC6" }),
3190
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: s2.sub, children: "\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6 \xB7 \u96F6 LLM \u81EA\u52A8\u6355\u83B7 \xB7 \u7B26\u53F7\u7D22\u5F15\u6E10\u8FDB\u62AB\u9732 \u2014 \u6570\u636E\u6E90 ~/.dsh/storages/dsh_engram.json" }),
3191
- error && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.error, children: [
3192
- t("error"),
3193
- ": ",
3194
- error
3195
- ] }),
3196
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.tabBar, children: [
3197
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: view === "mem" ? s2.tabActive : s2.tab, onClick: () => setView("mem"), children: "\u8BB0\u5FC6" }),
3198
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: view === "esr" ? s2.tabActive : s2.tab, onClick: () => setView("esr"), children: "ESR\uFF08\u4EFB\u52A1 \xB7 \u8282\u70B9 \xB7 \u5173\u7CFB\uFF09" }),
3199
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: view === "preview" ? s2.tabActive : s2.tab, onClick: () => setView("preview"), children: "\u6CE8\u5165\u9884\u89C8" }),
3200
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: view === "telemetry" ? s2.tabActive : s2.tab, onClick: () => setView("telemetry"), children: "\u9065\u6D4B" })
2618
+ if (!available) return null;
2619
+ const renderField = (field) => {
2620
+ const raw = value[field.key];
2621
+ const overridden = snap?.user !== void 0 && field.key in snap.user;
2622
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.row, children: [
2623
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
2624
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.label, children: field.label }),
2625
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.hint, children: field.hint })
2626
+ ] }),
2627
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2628
+ field.kind === "bool" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2629
+ "input",
2630
+ {
2631
+ type: "checkbox",
2632
+ checked: raw === true,
2633
+ disabled: !writable,
2634
+ onChange: (e) => setField(field.key, e.target.checked)
2635
+ }
2636
+ ) : field.kind === "select" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2637
+ "select",
2638
+ {
2639
+ style: { ...s2.input, width: field.width ?? 160 },
2640
+ value: raw === void 0 ? "" : String(raw),
2641
+ disabled: !writable,
2642
+ onChange: (e) => setField(field.key, e.target.value),
2643
+ children: (field.options ?? []).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("option", { value: opt, children: opt }, opt))
2644
+ }
2645
+ ) : field.kind === "text" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2646
+ "input",
2647
+ {
2648
+ type: "text",
2649
+ style: { ...s2.input, width: field.width ?? 180 },
2650
+ value: Array.isArray(raw) ? raw.join(", ") : raw === void 0 ? "" : String(raw),
2651
+ disabled: !writable,
2652
+ placeholder: "host.domain, \u53E6\u4E00\u57DF\u540D\u2026",
2653
+ onChange: (e) => {
2654
+ const tokens = e.target.value.split(/[,\s]+/).map((x) => x.trim()).filter(Boolean);
2655
+ setField(field.key, tokens);
2656
+ }
2657
+ }
2658
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2659
+ "input",
2660
+ {
2661
+ type: "number",
2662
+ style: s2.input,
2663
+ step: field.step ?? 1,
2664
+ min: field.min,
2665
+ max: field.max,
2666
+ value: raw === void 0 ? "" : String(raw),
2667
+ disabled: !writable,
2668
+ onChange: (e) => setField(field.key, e.target.value === "" ? void 0 : Number(e.target.value))
2669
+ }
2670
+ ),
2671
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2672
+ "button",
2673
+ {
2674
+ type: "button",
2675
+ style: overridden ? { ...s2.reset, color: "var(--dsw-alias-brand-primary, #2563eb)" } : { ...s2.reset, cursor: "default", opacity: 0.55 },
2676
+ title: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4",
2677
+ disabled: !writable || !overridden,
2678
+ onClick: () => void resetField(field.key),
2679
+ children: "\u91CD\u7F6E"
2680
+ }
2681
+ )
2682
+ ] })
2683
+ ] }, field.key);
2684
+ };
2685
+ const renderGroup = (group) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.groupPanel, children: [
2686
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.groupHead, children: [
2687
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.groupTitle, children: group.title }),
2688
+ group.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.groupDesc, children: group.description })
3201
2689
  ] }),
3202
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...s2.row, marginTop: 2 }, children: [
3203
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u5DE5\u4F5C\u533A\uFF1A" }),
3204
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { style: s2.input, value: workspace, onChange: (e) => setWorkspace(e.target.value), children: [
3205
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "\u5168\u90E8\u5DE5\u4F5C\u533A" }),
3206
- workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("option", { value: ws, children: [
3207
- ws,
3208
- "\uFF08",
3209
- overview?.workspaces[ws]?.memories ?? 0,
3210
- " \u6761\uFF09"
3211
- ] }, ws))
2690
+ group.fields.map(renderField)
2691
+ ] }, group.id);
2692
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: vars, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { ...s2.card, ...open ? s2.cardOpen : void 0 }, children: [
2693
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2694
+ "button",
2695
+ {
2696
+ type: "button",
2697
+ style: s2.header,
2698
+ "aria-expanded": open,
2699
+ "aria-label": `${open ? "\u6536\u8D77" : "\u5C55\u5F00"} dsh-engram \u8BBE\u7F6E`,
2700
+ onClick: () => setOpen((prev) => !prev),
2701
+ children: [
2702
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: s2.headText, children: [
2703
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: s2.name, children: "dsh-engram" }),
2704
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: s2.description, children: CARD_DESCRIPTION })
2705
+ ] }),
2706
+ anyDirty && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: s2.pending, children: "\u672A\u4FDD\u5B58" }),
2707
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Chevron, { open })
2708
+ ]
2709
+ }
2710
+ ),
2711
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.body, children: [
2712
+ !writable && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: s2.readOnly, children: "\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF08\u5BBF\u4E3B\u672A\u6388\u6743\u5199\u5165\u6216\u9700\u91CD\u542F\u5E94\u7528\uFF09\u3002" }),
2713
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.groupPanel, children: [
2714
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.groupHead, children: [
2715
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.groupTitle, children: "\u5E38\u7528" }),
2716
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.groupDesc, children: "\u6BCF\u5929\u4F1A\u78B0\u5230\u7684\u65CB\u94AE \xB7 \u5176\u4F59\u6536\u5728\u4E0B\u65B9\u9AD8\u7EA7\u8BBE\u7F6E" })
2717
+ ] }),
2718
+ COMMON_FIELDS.map(renderField)
2719
+ ] }),
2720
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2721
+ "button",
2722
+ {
2723
+ type: "button",
2724
+ style: { ...s2.advHead, ...advancedOpen ? { borderColor: "var(--dsw-alias-label-dimmed, #9ca3af)" } : void 0 },
2725
+ "aria-expanded": advancedOpen,
2726
+ onClick: () => setAdvancedOpen((v) => !v),
2727
+ children: [
2728
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: s2.advHeadLabel, children: [
2729
+ "\u9AD8\u7EA7\u8BBE\u7F6E\uFF08",
2730
+ ADVANCED_GROUPS.reduce((n, g) => n + g.fields.length, 0),
2731
+ " \u9879\uFF09"
2732
+ ] }),
2733
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Chevron, { open: advancedOpen })
2734
+ ]
2735
+ }
2736
+ ),
2737
+ advancedOpen && ADVANCED_GROUPS.map(renderGroup),
2738
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.note, children: "\u8BBE\u7F6E\u5BF9\u65B0\u5EFA\u4F1A\u8BDD\u5373\u65F6\u751F\u6548\uFF1B\u5DF2\u51BB\u7ED3\u7684 [ENGRAM] \u5757\u4FDD\u6301\u524D\u7F00\u7A33\u5B9A\u3002" }),
2739
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: s2.failed, children: error }),
2740
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: s2.footer, children: [
2741
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2742
+ "button",
2743
+ {
2744
+ type: "button",
2745
+ style: { ...s2.discard, ...!anyDirty || saving ? s2.disabled : void 0 },
2746
+ disabled: !anyDirty || saving,
2747
+ onClick: discard,
2748
+ children: "\u653E\u5F03\u4FEE\u6539"
2749
+ }
2750
+ ),
2751
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2752
+ "button",
2753
+ {
2754
+ type: "button",
2755
+ style: { ...s2.save, ...!anyDirty || saving ? s2.disabled : void 0 },
2756
+ disabled: !anyDirty || saving,
2757
+ onClick: () => void save(),
2758
+ children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
2759
+ }
2760
+ )
3212
2761
  ] })
2762
+ ] })
2763
+ ] }) });
2764
+ }
2765
+
2766
+ // client/src/EngramDetail.tsx
2767
+ var import_react7 = require("react");
2768
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2769
+ var KIND_COLORS = {
2770
+ preference: "#b45309",
2771
+ decision: "#2563eb",
2772
+ error: "#dc2626",
2773
+ procedure: "#7c3aed",
2774
+ fact: "#059669",
2775
+ insight: "#d97706",
2776
+ handoff: "#0891b2",
2777
+ task: "#4f46e5"
2778
+ };
2779
+ var KIND_LABEL = {
2780
+ preference: "\u504F\u597D",
2781
+ decision: "\u51B3\u5B9A",
2782
+ error: "\u9519\u8BEF",
2783
+ procedure: "\u6D41\u7A0B",
2784
+ fact: "\u4E8B\u5B9E",
2785
+ insight: "\u6D1E\u5BDF",
2786
+ handoff: "\u4EA4\u63A5",
2787
+ task: "\u4EFB\u52A1"
2788
+ };
2789
+ var REL_COLORS = {
2790
+ depends_on: "#d97706",
2791
+ implements: "#2563eb",
2792
+ refines: "#7c3aed",
2793
+ contradicts: "#dc2626",
2794
+ tracks: "#059669"
2795
+ };
2796
+ function fmtDate(ts) {
2797
+ if (!ts) return "\u2013";
2798
+ const d = new Date(ts);
2799
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
2800
+ const dd = String(d.getDate()).padStart(2, "0");
2801
+ return `${d.getFullYear()}-${mm}-${dd} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
2802
+ }
2803
+ function daysLeft(ts) {
2804
+ if (ts === null || ts === void 0) return "\u221E";
2805
+ const days = Math.ceil((ts - Date.now()) / 864e5);
2806
+ return days > 0 ? `${days}d` : "\u5DF2\u8FC7\u671F";
2807
+ }
2808
+ function gapsOf(t) {
2809
+ const gaps = [];
2810
+ if (!t.artifact) gaps.push("artifact");
2811
+ if (!t.evaluation) gaps.push("evaluation");
2812
+ if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
2813
+ return gaps;
2814
+ }
2815
+ function EngramDetail({ target, api, tasks, nodes, links, memories = [], onClose, onNavigateMemory, onChanged }) {
2816
+ const { dark, vars } = useEngramTheme();
2817
+ const [gArt, setGArt] = (0, import_react7.useState)("");
2818
+ const [gEval, setGEval] = (0, import_react7.useState)("");
2819
+ const [gRefs, setGRefs] = (0, import_react7.useState)("");
2820
+ const [busy, setBusy] = (0, import_react7.useState)(false);
2821
+ const [closing, setClosing] = (0, import_react7.useState)(false);
2822
+ const [notice, setNotice] = (0, import_react7.useState)(null);
2823
+ const task = target.kind === "task" ? tasks.find((t) => t.id === target.id) ?? null : null;
2824
+ const node = target.kind === "node" ? nodes.find((n) => n.id === target.id) ?? null : null;
2825
+ const link = target.kind === "link" ? target.link : null;
2826
+ const nameOf = (0, import_react7.useMemo)(() => {
2827
+ const nodeName = (id) => nodes.find((n) => n.id === id)?.name;
2828
+ const taskName = (id) => tasks.find((t) => t.id === id)?.name;
2829
+ return (id) => nodeName(id) ?? taskName(id) ?? id;
2830
+ }, [nodes, tasks]);
2831
+ const incident = (0, import_react7.useMemo)(() => {
2832
+ if (target.kind !== "node" || !node) return [];
2833
+ return links.filter((l) => l.source === node.id || l.target === node.id).map((l) => ({ l, outgoing: l.source === node.id }));
2834
+ }, [links, node, target.kind]);
2835
+ const memoryLinks = (0, import_react7.useMemo)(() => {
2836
+ if (target.kind !== "memory") return null;
2837
+ const m = target.memory;
2838
+ return {
2839
+ m,
2840
+ replacedBy: memories.filter((x) => x.status === "active" && x.supersedes === m.id),
2841
+ contradictedBy: memories.filter((x) => x.status === "active" && x.contradicts === m.id)
2842
+ };
2843
+ }, [target, memories]);
2844
+ const hb2 = {
2845
+ root: {
2846
+ ...vars,
2847
+ border: "1px solid var(--dsh-color-border, #d1d5db)",
2848
+ borderRadius: 12,
2849
+ background: dark ? "rgba(10,14,22,.55)" : "var(--dsh-color-surface, #ffffff)",
2850
+ boxShadow: "0 1px 2px rgba(15,23,42,.05), 0 8px 24px rgba(15,23,42,.07)",
2851
+ overflow: "hidden",
2852
+ display: "flex",
2853
+ flexDirection: "column",
2854
+ maxHeight: "72vh"
2855
+ },
2856
+ head: {
2857
+ display: "flex",
2858
+ alignItems: "center",
2859
+ gap: 8,
2860
+ padding: "10px 12px",
2861
+ borderBottom: "1px solid var(--dsh-color-border, #e5e7eb)",
2862
+ background: dark ? "rgba(15,23,42,.6)" : "rgba(15,23,42,.04)"
2863
+ },
2864
+ title: { fontWeight: 700, fontSize: 13, flex: "1 1 auto", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
2865
+ close: { border: "none", background: "none", cursor: "pointer", fontSize: 13, color: "var(--dsh-color-muted, #6b7280)", padding: "2px 6px", borderRadius: 6 },
2866
+ body: { padding: "10px 12px", overflowY: "auto", display: "flex", flexDirection: "column", gap: 8, fontSize: 12.5 },
2867
+ kv: { display: "flex", flexDirection: "column", gap: 3 },
2868
+ k: { fontSize: 10.5, fontWeight: 700, color: "var(--dsh-color-muted-weak, #9ca3af)", textTransform: "uppercase", letterSpacing: 0.4 },
2869
+ v: { color: "var(--dsh-color-muted-strong, #374151)", wordBreak: "break-word", lineHeight: 1.5 },
2870
+ mono: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 },
2871
+ badge: { fontSize: 10.5, fontWeight: 700, padding: "2px 8px", borderRadius: 999, color: "#fff" },
2872
+ tag: { fontSize: 11, padding: "1px 8px", borderRadius: 999, background: "var(--dsh-color-hover-bg, #f3f4f6)", color: "var(--dsh-color-muted, #6b7280)", border: "1px solid var(--dsh-color-border, #e5e7eb)" },
2873
+ chip: { fontSize: 11, padding: "2px 9px", borderRadius: 999, background: "rgba(37,99,235,.1)", color: "#2563eb", border: "1px solid rgba(37,99,235,.25)", cursor: "pointer" },
2874
+ chipSup: { fontSize: 11, padding: "2px 9px", borderRadius: 999, background: "rgba(124,58,237,.1)", color: "#7c3aed", border: "1px solid rgba(124,58,237,.25)", cursor: "pointer" },
2875
+ chipDanger: { fontSize: 11, padding: "2px 9px", borderRadius: 999, background: "rgba(220,38,38,.1)", color: "#dc2626", border: "1px solid rgba(220,38,38,.25)", cursor: "pointer" },
2876
+ input: { fontSize: 12, padding: "5px 8px", border: "1px solid var(--dsh-color-border, #d1d5db)", borderRadius: 8, background: "var(--dsh-color-surface, #ffffff)", color: "var(--dsh-color-muted-strong, #374151)", width: "100%", boxSizing: "border-box" },
2877
+ btn: { fontSize: 11.5, fontWeight: 600, padding: "5px 10px", borderRadius: 8, cursor: "pointer", border: "1px solid var(--dsh-color-border, #d1d5db)", background: "transparent", color: "var(--dsh-color-muted-strong, #374151)" },
2878
+ btnPrimary: { fontSize: 11.5, fontWeight: 700, padding: "5px 12px", borderRadius: 8, cursor: "pointer", border: "none", background: "#2563eb", color: "#fff" },
2879
+ relRow: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap", fontSize: 12 },
2880
+ relType: { border: "1px dashed var(--dsh-color-border, #cbd5e1)", borderRadius: 999, padding: "1px 7px", fontSize: 11, color: "var(--dsh-color-muted, #6b7280)" },
2881
+ dot: { width: 8, height: 8, borderRadius: 999, display: "inline-block" },
2882
+ section: { marginTop: 2 }
2883
+ };
2884
+ async function submitClose() {
2885
+ if (!task) return;
2886
+ setBusy(true);
2887
+ setNotice(null);
2888
+ try {
2889
+ const refs = gRefs.split(/[,,\s]+/).map((s4) => s4.trim()).filter(Boolean);
2890
+ const res = await api.closeTask(task.workspace, task.id, { artifact: gArt.trim() || void 0, evaluation: gEval.trim() || void 0, memoryRefs: refs.length ? refs : void 0 });
2891
+ setNotice(res.state === "stable" ? `\u5DF2\u95ED\u73AF \u2192 STABLE \u2713` : `\u4ECD\u6709\u7F3A\u53E3\uFF1A${(res.gaps ?? []).join(", ") || "\u2014"}${res.artifactReason ? `\uFF08${res.artifactReason}\uFF09` : ""}`);
2892
+ onChanged?.();
2893
+ } catch (e) {
2894
+ setNotice(`\u5173\u95ED\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}`);
2895
+ } finally {
2896
+ setBusy(false);
2897
+ }
2898
+ }
2899
+ const kindBadge = (kind, label) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...hb2.badge, background: KIND_COLORS[kind] ?? "#6b7280" }, children: label });
2900
+ const headerTitle = target.kind === "task" ? task?.name ?? target.id : target.kind === "memory" ? target.memory.text.slice(0, 40) : target.kind === "node" ? node?.name ?? target.id : link ? nameOf(link.source) : "";
2901
+ const headerKind = target.kind === "task" ? task?.state === "stable" ? { k: "task", label: "STABLE" } : task ? { k: "task", label: task.state === "draft" ? "DRAFT" : "\u4EFB\u52A1" } : { k: "task", label: "\u4EFB\u52A1" } : target.kind === "memory" ? { k: target.memory.kind, label: KIND_LABEL[target.memory.kind] ?? target.memory.kind } : target.kind === "node" ? { k: "node", label: node?.kind || "\u8282\u70B9" } : { k: "link", label: "\u5173\u7CFB" };
2902
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.root, children: [
2903
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.head, children: [
2904
+ kindBadge(headerKind.k, headerKind.label),
2905
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.title, title: headerTitle, children: headerTitle }),
2906
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: hb2.close, onClick: onClose, "aria-label": "\u5173\u95ED\u8BE6\u60C5", children: "\u2715" })
3213
2907
  ] }),
3214
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 12, alignItems: "flex-start" }, children: [
3215
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { flex: "1 1 auto", minWidth: 0 }, children: [
3216
- view === "mem" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3217
- overview && (() => {
3218
- const memNum = wsCounts ? wsCounts.memories : overview.totals.memories;
3219
- const taskNum = wsCounts ? wsCounts.tasks : overview.totals.tasks;
3220
- const linkNum = wsCounts ? wsCounts.links : overview.totals.links;
3221
- const ledgerBucket = overview.ledger ? workspace && overview.ledger.byWorkspace[workspace] || overview.ledger.totals : null;
3222
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.stats, children: [
3223
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(memNum), label: wsCounts ? "\u8BB0\u5FC6 (active)" : "\u8BB0\u5FC6 (active, \u5168\u5C40)" }),
3224
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(taskNum), label: wsCounts ? "\u4EFB\u52A1 (active)" : "\u4EFB\u52A1 (active, \u5168\u5C40)" }),
3225
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(linkNum), label: wsCounts ? "\u5173\u7CFB" : "\u5173\u7CFB (\u5168\u5C40)" }),
3226
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(wsCounts ? wsCounts.nodes ?? 0 : overview.totals.nodes ?? 0), label: wsCounts ? "\u8282\u70B9" : "\u8282\u70B9 (\u5168\u5C40)" }),
3227
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(workspaces.length), label: "\u5DE5\u4F5C\u533A" }),
3228
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(overview.captures.total), label: "\u81EA\u52A8\u6355\u83B7" }),
3229
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StatCard, { num: String(overview.captures.preference ?? 0), label: "\u504F\u597D\u6355\u83B7" }),
3230
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3231
- StatCard,
3232
- {
3233
- num: indexCost ? `~${indexCost.tokens}` : "\u2013",
3234
- label: "[ENGRAM] \u7D22\u5F15 token / \u5DE5\u4F5C\u533A"
3235
- }
3236
- ),
3237
- gc && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3238
- StatCard,
3239
- {
3240
- num: String(gc.archivedMemories + gc.archivedTasks),
3241
- label: `GC \u5DF2\u5F52\u6863 \xB7 \u94FE\u63A5-${gc.removedLinks}`
3242
- }
3243
- ),
3244
- ledgerBucket && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3245
- StatCard,
3246
- {
3247
- num: signedTokens(ledgerBucket.netTokens),
3248
- label: `\u4E0A\u4E0B\u6587\u51C0 token \xB7 \u8BF7\u6C42 ${ledgerBucket.requests}`
3249
- }
3250
- )
3251
- ] });
3252
- })(),
3253
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.subPanel, children: [
3254
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
3255
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u8BB0\u5FC6 GC\uFF08pi-esr \u7EA6\u675F\uFF09" }),
3256
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: { fontSize: 12, display: "inline-flex", gap: 4, alignItems: "center" }, children: [
3257
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: gcDryRun, onChange: (e) => setGcDryRun(e.target.checked) }),
3258
- "\u4EC5\u9884\u89C8\uFF08dry run\uFF09"
3259
- ] }),
3260
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btnPrimary, onClick: () => void runGc(), disabled: gcRunning || !workspace, children: gcRunning ? "\u2026" : "\u8FD0\u884C GC" }),
3261
- gc && gc.lastRun > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: s2.mono, children: [
3262
- "\u4E0A\u6B21 ",
3263
- fmtDate2(gc.lastRun)
3264
- ] })
3265
- ] }),
3266
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)", marginTop: 4 }, children: "\u5DE5\u4F5C\u96C6\uFF08active \u4EFB\u52A1\u5F15\u7528 / \u4EFB\u52A1\u8BB0\u5FC6 / \u5DF2\u5165\u7D22\u5F15\u547D\u4E2D\uFF09\u6C38\u4E0D\u9A71\u9010\uFF1BTTL \u8FC7\u671F\u5F52\u6863\u3001\u8D85\u5BB9\u91CF\u6DD8\u6C70\u3001stable \u4EFB\u52A1\u8D85\u7A97\u5F52\u6863\u3001\u60AC\u7A7A\u94FE\u63A5\u6E05\u7406\u3002\u53EA\u5F52\u6863\u4E0D\u786C\u5220\u2014\u2014\u6761\u76EE id \u4FDD\u6301\u53EF\u91CD\u53D6\u3002" }),
3267
- gcReport && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 8, fontSize: 12.5 }, children: [
3268
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
3269
- gcReport.dryRun ? "dry-run \u9884\u89C8\uFF1A" : "\u5DF2\u6267\u884C\uFF1A",
3270
- " ",
3271
- "\u5F52\u6863\u8BB0\u5FC6 ",
3272
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: gcReport.archivedMemories.length }),
3273
- " \xB7 \u5F52\u6863\u4EFB\u52A1 ",
3274
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: gcReport.archivedTasks.length }),
3275
- " \xB7 \u6E05\u7406\u94FE\u63A5 ",
3276
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: gcReport.removedLinks.length }),
3277
- " \xB7 \u4FDD\u62A4 ",
3278
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: gcReport.protectedMemories })
3279
- ] }),
3280
- gcReport.archivedMemories.slice(0, 5).map((e) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.mono, children: [
3281
- "- ",
3282
- e.id.slice(0, 8),
3283
- " ",
3284
- e.reason,
3285
- ": ",
3286
- e.text
3287
- ] }, e.id)),
3288
- gcReport.archivedTasks.slice(0, 3).map((t2) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.mono, children: [
3289
- "- ",
3290
- t2.id.slice(0, 6),
3291
- " ",
3292
- t2.reason,
3293
- ": ",
3294
- t2.name
3295
- ] }, t2.id)),
3296
- gcReport.removedLinks.slice(0, 3).map((l) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.mono, children: [
3297
- "- link ",
3298
- (l.source ?? "?").slice(0, 8),
3299
- " --",
3300
- l.relation ?? "",
3301
- "--> ",
3302
- (l.target ?? "?").slice(0, 8)
3303
- ] }, l.id))
2908
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.body, children: [
2909
+ target.kind === "task" && !task && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.v, children: "\u4EFB\u52A1\u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u5DF2\u88AB\u5173\u95ED/\u5F52\u6863\uFF09\u3002" }),
2910
+ target.kind === "task" && task && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2911
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
2912
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EvidenceRing, { artifact: Boolean(task.artifact), evaluation: Boolean(task.evaluation), refs: (task.memoryRefs?.length ?? 0) > 0, size: 40 }),
2913
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2914
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 12, fontWeight: 700 }, children: task.name }),
2915
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.mono, children: [
2916
+ task.id,
2917
+ " \xB7 ",
2918
+ task.workspace
3304
2919
  ] })
3305
- ] }),
3306
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.subPanel, children: [
3307
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
3308
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u6570\u636E\u5907\u4EFD\uFF08export / import\uFF09" }),
3309
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, title: "\u4E0B\u8F7D\u5F53\u524D\u5DE5\u4F5C\u533A\uFF08\u6216\u5168\u90E8\uFF09\u7684 dsh-engram/export v1 \u8F6C\u50A8", onClick: () => void downloadExport(), children: "\u5BFC\u51FA JSON" }),
3310
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: { ...s2.btn, display: "inline-flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
3311
- "\u9009\u62E9\u5BFC\u5165\u6587\u4EF6\u2026",
3312
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3313
- "input",
3314
- {
3315
- type: "file",
3316
- accept: "application/json,.json",
3317
- style: { display: "none" },
3318
- onChange: (e) => {
3319
- void pickImportFile(e.target.files?.[0] ?? null);
3320
- e.currentTarget.value = "";
3321
- }
3322
- }
3323
- )
3324
- ] }),
3325
- importName && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: s2.mono, children: importName })
3326
- ] }),
3327
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)", marginTop: 4 }, children: [
3328
- "\u5BFC\u51FA\u5373\u53EF\u79FB\u690D\u5907\u4EFD\uFF08\u8BB0\u5FC6/\u4EFB\u52A1/\u5173\u7CFB/\u8282\u70B9\uFF0C\u542B\u5F52\u6863\uFF09\uFF1B",
3329
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: "merge" }),
3330
- " \u53EA\u8865\u7F3A\u5931\uFF08\u5E42\u7B49\uFF09\uFF0C",
3331
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("b", { children: "replace" }),
3332
- " \u5148\u6E05\u7A7A\u76EE\u6807\u5DE5\u4F5C\u533A\u518D\u6062\u590D\u3002\u8BB0\u5FC6\u6B63\u6587\u4E0E\u6240\u6709\u5199\u5165\u8DEF\u5F84\u4E00\u6837\u8FC7\u8131\u654F\u3002"
2920
+ ] })
2921
+ ] }),
2922
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
2923
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u72B6\u6001" }),
2924
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 5, alignItems: "center", flexWrap: "wrap", marginTop: 3 }, children: [
2925
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...hb2.badge, background: task.state === "stable" ? "#059669" : task.state === "draft" ? "#6b7280" : gapsOf(task).length === 0 ? "#2563eb" : "#d97706" }, children: task.state === "stable" ? "STABLE" : task.state === "draft" ? "DRAFT" : gapsOf(task).length === 0 ? "READY" : "ACTIVE" }),
2926
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.tag, children: [
2927
+ "\u521B\u5EFA ",
2928
+ fmtDate(task.createdAt)
3333
2929
  ] }),
3334
- importDump !== null && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 8, display: "flex", flexDirection: "column", gap: 6 }, children: [
3335
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", fontSize: 12.5 }, children: [
3336
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: { display: "inline-flex", gap: 4, alignItems: "center" }, children: [
3337
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: importDryRun, onChange: (e) => setImportDryRun(e.target.checked) }),
3338
- "\u4EC5\u9884\u89C8\uFF08dry run\uFF09"
3339
- ] }),
3340
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
3341
- "select",
3342
- {
3343
- style: s2.input,
3344
- value: importMode,
3345
- onChange: (e) => {
3346
- const mode = e.target.value;
3347
- setImportMode(mode);
3348
- if (mode !== "replace") setImportReplaceOk(false);
3349
- },
3350
- children: [
3351
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "merge", children: "\u5408\u5E76\uFF08merge\uFF0C\u5E42\u7B49\uFF09" }),
3352
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "replace", children: "\u66FF\u6362\uFF08replace\uFF0C\u5148\u6E05\u7A7A\u5DE5\u4F5C\u533A\uFF09" })
3353
- ]
3354
- }
3355
- ),
3356
- importMode === "replace" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: { display: "inline-flex", gap: 4, alignItems: "center", color: "#b45309" }, children: [
3357
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: importReplaceOk, onChange: (e) => setImportReplaceOk(e.target.checked) }),
3358
- "\u6211\u786E\u8BA4\u66FF\u6362",
3359
- workspace ? `\uFF08${workspace}\uFF09` : "\uFF08\u9700\u5148\u9009\u5DE5\u4F5C\u533A\uFF09"
3360
- ] }),
3361
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3362
- "button",
3363
- {
3364
- style: s2.btnPrimary,
3365
- disabled: importBusy || importMode === "replace" && (!importReplaceOk || !workspace),
3366
- onClick: () => void runImport(),
3367
- children: importBusy ? "\u2026" : importDryRun ? "\u9884\u89C8\u5BFC\u5165" : "\u6267\u884C\u5BFC\u5165"
3368
- }
3369
- )
3370
- ] }),
3371
- importReport && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: 12.5 }, children: [
3372
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
3373
- importReport.dryRun ? "dry-run \u9884\u89C8\uFF1A" : "\u5DF2\u5BFC\u5165\uFF1A",
3374
- " ",
3375
- Object.entries(importReport.written).map(([k, v]) => `${k} ${v}`).join(" \xB7 ") || "\u2014",
3376
- importReport.skipped.length > 0 ? ` \xB7 \u8DF3\u8FC7 ${importReport.skipped.length}` : ""
3377
- ] }),
3378
- importReport.skipped.slice(0, 5).map((x, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.mono, children: [
3379
- "- ",
3380
- x.table,
3381
- " ",
3382
- x.id ? x.id.slice(0, 8) : "?",
3383
- ": ",
3384
- x.reason
3385
- ] }, `${x.table}-${x.id ?? i}`))
3386
- ] }),
3387
- dataError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 12, color: "#dc2626", fontWeight: 600 }, children: dataError })
2930
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.tag, children: [
2931
+ "\u66F4\u65B0 ",
2932
+ fmtDate(task.updatedAt)
3388
2933
  ] })
3389
- ] }),
3390
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.row, children: [
3391
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { style: s2.input, value: kind, onChange: (e) => setKind(e.target.value), children: [
3392
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "\u5168\u90E8\u7C7B\u578B" }),
3393
- kindsPresent.map((k) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: k, children: KIND_LABEL2[k] ?? k }, k))
3394
- ] }),
3395
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { style: s2.input, value: status, onChange: (e) => setStatus(e.target.value), children: [
3396
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "active", children: "\u4EC5\u6D3B\u52A8" }),
3397
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "all", children: "\u5168\u90E8\u72B6\u6001" }),
3398
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "archived", children: "\u5DF2\u5F52\u6863" })
3399
- ] }),
3400
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3401
- "input",
3402
- {
3403
- style: { ...s2.input, width: 180 },
3404
- placeholder: "\u641C\u7D22\u8BB0\u5FC6\u2026",
3405
- value: q,
3406
- onChange: (e) => setQ(e.target.value),
3407
- onKeyDown: (e) => {
3408
- if (e.key === "Enter") void refresh();
3409
- }
3410
- }
3411
- ),
3412
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btnPrimary, onClick: () => void refresh(), disabled: busy, children: busy ? "\u2026" : t("refresh") })
3413
- ] }),
3414
- cfg && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.row, children: [
3415
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: s2.tag, children: [
3416
- "autoCapture ",
3417
- cfg.autoCapture ? "on" : "off"
3418
- ] }),
3419
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: s2.tag, children: [
3420
- "sessionSearch ",
3421
- cfg.sessionSearch ? "on" : "off"
3422
- ] }),
3423
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: s2.tag, children: [
3424
- "TTL ",
3425
- cfg.expireDays,
3426
- "d"
3427
- ] }),
3428
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: s2.tag, children: [
3429
- "index ",
3430
- cfg.indexMaxLines,
3431
- " \u884C / ",
3432
- cfg.indexMaxChars,
3433
- " \u5B57\u7B26"
3434
- ] }),
3435
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: s2.tag, children: [
3436
- "promote \u2265",
3437
- cfg.promoteHits,
3438
- " hits"
2934
+ ] })
2935
+ ] }),
2936
+ task.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
2937
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u63CF\u8FF0" }),
2938
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { ...hb2.v, marginTop: 3 }, children: task.description })
2939
+ ] }),
2940
+ task.state === "active" && gapsOf(task).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
2941
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u8BC1\u636E\u7F3A\u53E3" }),
2942
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: gapsOf(task).map((g) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.tag, children: [
2943
+ g,
2944
+ " \u2717"
2945
+ ] }, g)) })
2946
+ ] }),
2947
+ task.artifact && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
2948
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "artifact" }),
2949
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...hb2.mono, marginTop: 3, color: "#059669" }, children: [
2950
+ "\u2713 ",
2951
+ task.artifact
2952
+ ] })
2953
+ ] }),
2954
+ task.evaluation && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
2955
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "evaluation" }),
2956
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 3, color: "#059669" }, children: [
2957
+ "\u2713 ",
2958
+ task.evaluation
2959
+ ] })
2960
+ ] }),
2961
+ task.memoryRefs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
2962
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u8BB0\u5FC6\u5F15\u7528" }),
2963
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: task.memoryRefs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.chip, title: `\u6253\u5F00\u8BB0\u5FC6 ${r}`, onClick: () => onNavigateMemory(r), children: [
2964
+ "#",
2965
+ r.slice(0, 8)
2966
+ ] }, r)) })
2967
+ ] }),
2968
+ task.state === "active" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 4, padding: 8, border: "1px solid var(--dsh-color-border, #d1d5db)", borderRadius: 10, background: dark ? "rgba(15,23,42,.35)" : "rgba(15,23,42,.03)" }, children: [
2969
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-color-muted, #6b7280)", marginBottom: 6 }, children: "\u8865\u9F50\u8BC1\u636E \u2192 \u5173\u95ED\uFF08\u4E09\u9879\u5168\u9F50\u8F6C STABLE\uFF09" }),
2970
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
2971
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: hb2.input, placeholder: "artifact \u4EA7\u7269\uFF08\u6587\u4EF6/PR/\u8DEF\uFF09", value: gArt, onChange: (e) => setGArt(e.target.value) }),
2972
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: hb2.input, placeholder: "evaluation \u8BC4\u4F30\uFF08\u6D4B\u8BD5/\u8BC4\u5BA1/\u5206\u6570\uFF09", value: gEval, onChange: (e) => setGEval(e.target.value) }),
2973
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: hb2.input, placeholder: "memory_refs\uFF08#id, \u9017\u53F7\u5206\u9694\uFF09", value: gRefs, onChange: (e) => setGRefs(e.target.value) }),
2974
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 6 }, children: [
2975
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: hb2.btnPrimary, disabled: busy, onClick: () => setClosing(true), children: busy ? "\u2026" : "\u6309\u8BC1\u636E\u95ED\u73AF" }),
2976
+ closing && !busy && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2977
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)", alignSelf: "center" }, children: "\u786E\u8BA4\u63D0\u4EA4\uFF1F" }),
2978
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: hb2.btn, onClick: () => {
2979
+ void submitClose();
2980
+ setClosing(false);
2981
+ }, children: "\u786E\u8BA4" }),
2982
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: hb2.btn, onClick: () => setClosing(false), children: "\u53D6\u6D88" })
2983
+ ] })
3439
2984
  ] })
2985
+ ] })
2986
+ ] }),
2987
+ task.state === "stable" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 11.5, color: "#059669", fontWeight: 700 }, children: "\u5DF2\u95ED\u73AF \u2713 \u2014 \u5168\u90E8\u8BC1\u636E\u95E8\u5DF2\u6EE1\u8DB3\uFF0C\u53EF\u4EE5\u4ECE\u770B\u677F/\u6E05\u5355\u79FB\u9664" }),
2988
+ notice && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 11.5, fontWeight: 600, color: "#d97706" }, children: notice })
2989
+ ] }),
2990
+ target.kind === "memory" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2991
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: 6 }, children: [
2992
+ kindBadge(target.memory.kind, KIND_LABEL[target.memory.kind] ?? target.memory.kind),
2993
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.tag, children: [
2994
+ "signal ",
2995
+ target.memory.signal.toFixed(2)
3440
2996
  ] }),
3441
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { style: s2.table, children: [
3442
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("colgroup", { children: [
3443
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("col", { style: { width: 58 } }),
3444
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("col", {}),
3445
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("col", { style: { width: 72 } })
3446
- ] }),
3447
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { children: [
3448
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: s2.th, children: "\u7C7B\u578B" }),
3449
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: s2.th, children: "\u5185\u5BB9" }),
3450
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: s2.th })
3451
- ] }) }),
3452
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tbody", { children: [
3453
- flatRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { colSpan: 3, style: s2.empty, children: "\u6682\u65E0\u8BB0\u5FC6 \u2014 \u4F7F\u7528 engram_store \u663E\u5F0F\u8BB0\u5F55\uFF0C\u6216\u8BA9\u81EA\u52A8\u6355\u83B7\u5DE5\u4F5C\uFF08git \u63D0\u4EA4 / \u5173\u952E\u6587\u4EF6\u7F16\u8F91 / \u5DE5\u5177\u9519\u8BEF\uFF09" }) }),
3454
- memPageRows.map(
3455
- (r) => r.kind === "head" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { colSpan: 3, style: s2.wsHead, children: [
3456
- r.ws,
3457
- " \xB7 ",
3458
- r.count,
3459
- " \u6761"
3460
- ] }) }, `h-${r.ws}`) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { children: [
3461
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: { ...s2.td, whiteSpace: "nowrap" }, children: [
3462
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...s2.badge, background: KIND_COLORS2[r.m.kind] ?? "#6b7280" }, children: KIND_LABEL2[r.m.kind] ?? r.m.kind }),
3463
- r.m.status === "archived" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...s2.tag, color: "#b45309", background: "#fef3c7" }, children: "archived" }),
3464
- supersedeIndex.replacedBy.has(r.m.id) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...s2.tag, color: "#b45309", background: "#fef3c7" }, title: `\u5DF2\u88AB ${(supersedeIndex.replacedBy.get(r.m.id) ?? []).map((x) => `#${x.id.slice(0, 8)}`).join(", ")} \u66FF\u4EE3\uFF08\u65E7\u884C\u4FDD\u7559\u3001\u53EC\u56DE\u964D\u7EA7\uFF09`, children: "\u5DF2\u66FF\u6362" })
3465
- ] }),
3466
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: { ...s2.td, minWidth: 0 }, children: [
3467
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { title: r.m.text, style: expandedRows.has(r.m.id) ? s2.expanded : s2.clamp3, children: r.m.text }),
3468
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", marginTop: 2, flexWrap: "wrap" }, children: [
3469
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...s2.mono, flex: "1 1 160px" }, title: `${fmtDate2(r.m.createdAt)} \xB7 ${r.m.id} \xB7 ${r.m.entity ?? ""} \xB7 signal ${r.m.signal.toFixed(2)} \xB7 hits ${r.m.hits} \xB7 TTL ${daysLeft2(r.m.expiresAt)}`, children: [
3470
- fmtDate2(r.m.createdAt),
3471
- " \xB7 ",
3472
- r.m.id.slice(0, 8),
3473
- r.m.entity ? ` \xB7 ${r.m.entity}` : "",
3474
- " \xB7 ",
3475
- r.m.signal.toFixed(2),
3476
- " \xB7 hits ",
3477
- r.m.hits,
3478
- " \xB7 ",
3479
- daysLeft2(r.m.expiresAt)
3480
- ] }),
3481
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.linkBtn, onClick: () => toggleExpand(r.m.id), children: expandedRows.has(r.m.id) ? "\u6536\u8D77" : "\u5C55\u5F00\u5168\u6587" }),
3482
- r.m.supersedes && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("button", { style: s2.linkBtn, title: `\u6253\u5F00\u88AB\u66FF\u4EE3\u7684\u65E7\u8BB0\u5FC6 ${r.m.supersedes}`, onClick: () => openMemory(r.m.supersedes), children: [
3483
- "\u66FF\u4EE3 #",
3484
- r.m.supersedes.slice(0, 8)
3485
- ] })
3486
- ] }),
3487
- r.m.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { marginTop: 3, display: "flex", flexWrap: "wrap", gap: 4 }, children: r.m.tags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: s2.tag, children: tag }, tag)) })
3488
- ] }),
3489
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: s2.td, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-start" }, children: [
3490
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.linkBtn, title: "\u5728\u4FA7\u680F\u6253\u5F00\u8BE6\u60C5", onClick: () => {
3491
- setDetail({ kind: "memory", memory: r.m });
3492
- setDetailNotice(null);
3493
- }, children: "\u8BE6\u60C5" }),
3494
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, title: "\u5F52\u6863\uFF08TTL/\u8F6F\u5220\uFF0C\u53EF\u6062\u590D\u4E0D\u8F7D\u5165\u7D22\u5F15\uFF09", onClick: () => void act(() => api.archive(r.m.id, r.m.workspace)), children: "\u5F52\u6863" }),
3495
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, title: "\u6C38\u4E45\u5220\u9664", onClick: () => {
3496
- if (window.confirm(`\u5220\u9664\u8FD9\u6761\u8BB0\u5FC6?
3497
- ${r.m.text.slice(0, 60)}`)) void act(() => api.remove(r.m.id, r.m.workspace));
3498
- }, children: "\u5220\u9664" })
3499
- ] }) })
3500
- ] }, r.m.id)
3501
- )
3502
- ] })
2997
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.tag, children: [
2998
+ "hits ",
2999
+ target.memory.hits
3503
3000
  ] }),
3504
- flatRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.pageBar, children: [
3505
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, disabled: memPageSafe === 0, onClick: () => setMemPage(memPageSafe - 1), children: "\u2039 \u4E0A\u4E00\u9875" }),
3506
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3507
- "\u7B2C ",
3508
- memPageSafe + 1,
3509
- " / ",
3510
- memPageCount,
3511
- " \u9875 \xB7 \u5171 ",
3512
- flatRows.length,
3513
- " \u6761"
3001
+ target.memory.status === "archived" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: hb2.tag, children: "archived" })
3002
+ ] }),
3003
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3004
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u5185\u5BB9" }),
3005
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { ...hb2.v, whiteSpace: "pre-wrap", background: dark ? "rgba(15,23,42,.5)" : "rgba(15,23,42,.05)", borderRadius: 8, padding: 8, marginTop: 3 }, children: target.memory.text })
3006
+ ] }),
3007
+ target.memory.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3008
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u6807\u7B7E" }),
3009
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: target.memory.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.tag, children: [
3010
+ "#",
3011
+ t
3012
+ ] }, t)) })
3013
+ ] }),
3014
+ memoryLinks && (memoryLinks.m.supersedes || memoryLinks.m.contradicts || memoryLinks.replacedBy.length > 0 || memoryLinks.contradictedBy.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3015
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u66FF\u6362 / \u51B2\u7A81" }),
3016
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 5, flexWrap: "wrap", marginTop: 3 }, children: [
3017
+ memoryLinks.m.supersedes && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.chipSup, title: `\u672C\u6761\u66FF\u4EE3 ${memoryLinks.m.supersedes}\uFF08\u65E7\u884C\u4FDD\u7559\u3001\u53EC\u56DE\u964D\u7EA7\uFF09`, onClick: () => onNavigateMemory(memoryLinks.m.supersedes), children: [
3018
+ "\u66FF\u4EE3 #",
3019
+ memoryLinks.m.supersedes.slice(0, 8)
3020
+ ] }),
3021
+ memoryLinks.m.contradicts && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.chipDanger, title: `\u672C\u6761\u4E0E ${memoryLinks.m.contradicts} \u51B2\u7A81\uFF08\u4E24\u6761\u90FD\u4FDD\u7559\uFF09`, onClick: () => onNavigateMemory(memoryLinks.m.contradicts), children: [
3022
+ "\u51B2\u7A81 #",
3023
+ memoryLinks.m.contradicts.slice(0, 8)
3514
3024
  ] }),
3515
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, disabled: memPageSafe >= memPageCount - 1, onClick: () => setMemPage(memPageSafe + 1), children: "\u4E0B\u4E00\u9875 \u203A" })
3025
+ memoryLinks.replacedBy.map((x) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.chipSup, title: `\u5DF2\u88AB ${x.text.slice(0, 40)}\u2026 \u66FF\u4EE3`, onClick: () => onNavigateMemory(x.id), children: [
3026
+ "\u2190 \u88AB #",
3027
+ x.id.slice(0, 8),
3028
+ " \u66FF\u4EE3"
3029
+ ] }, x.id)),
3030
+ memoryLinks.contradictedBy.map((x) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: hb2.chipDanger, title: `${x.text.slice(0, 40)}\u2026 \u4E0E\u672C\u6761\u51B2\u7A81`, onClick: () => onNavigateMemory(x.id), children: [
3031
+ "\u21C4 #",
3032
+ x.id.slice(0, 8),
3033
+ " \u51B2\u7A81"
3034
+ ] }, x.id))
3516
3035
  ] })
3517
3036
  ] }),
3518
- view === "esr" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3519
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 6 }, children: [
3520
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)" }, children: "\u5DE5\u5177\u8C03\u7528\u4E0E\u547D\u4E2D\u7EDF\u8BA1\u89C1\u300C\u9065\u6D4B\u300Dtab\uFF1B[ESR] \u6CE8\u5165\u5757\u7684 escalate \u63D0\u9192\u4F9D\u636E\u771F\u5B9E\u6570\u636E\u52A8\u6001\u51FA\u73B0\u3002" }),
3521
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { marginLeft: "auto", display: "inline-flex", gap: 6 }, children: [
3522
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", style: esrGraph ? s2.tab : s2.tabActive, onClick: () => setEsrGraph(false), children: "\u5217\u8868" }),
3523
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", style: esrGraph ? s2.tabActive : s2.tab, onClick: () => setEsrGraph(true), children: "\u56FE\u8C31" })
3524
- ] })
3525
- ] }),
3526
- esrGraph ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.subPanel, children: [
3527
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }, children: [
3528
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u5173\u7CFB\u56FE\u8C31\uFF08esr_link \u529B\u5BFC\u5411\u56FE\uFF09" }),
3529
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: 11.5, fontWeight: 400, color: "var(--dsh-color-muted, #6b7280)" }, children: [
3530
- workspace === "" ? "\u5168\u90E8\u5DE5\u4F5C\u533A" : `\u5DE5\u4F5C\u533A\uFF1A${workspace}`,
3531
- " \xB7 \u5B9E\u4F53\u4E3A\u5706\u5F62\u8282\u70B9\uFF0C\u4EFB\u52A1\u4E3A\u52FE\u9009\u5FBD\u6807\uFF1B\u70B9\u9009\u8282\u70B9\u67E5\u770B\u5173\u7CFB\u660E\u7EC6"
3532
- ] })
3037
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3038
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u5143\u6570\u636E" }),
3039
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 2, marginTop: 3, fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "var(--dsh-color-muted, #6b7280)" }, children: [
3040
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3041
+ "id ",
3042
+ target.memory.id
3533
3043
  ] }),
3534
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3535
- EngramGraph,
3536
- {
3537
- workspace,
3538
- entities: nodes,
3539
- tasks,
3540
- links,
3541
- nameOf
3542
- }
3543
- )
3544
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3545
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.panelTitle, children: [
3546
- "ESR \u4EFB\u52A1\uFF08\u8BC1\u636E\u95ED\u73AF\uFF09",
3547
- workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsh-color-muted, #9ca3af)" }, children: "\xB7 \u5168\u90E8\u5DE5\u4F5C\u533A" })
3044
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3045
+ "ws ",
3046
+ target.memory.workspace
3548
3047
  ] }),
3549
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.subPanel, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
3550
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("select", { style: s2.input, value: newTaskWs, onChange: (e) => setNewTaskWs(e.target.value), children: [
3551
- workspaces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "(no workspaces)" }),
3552
- workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: ws, children: ws }, ws))
3553
- ] }),
3554
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: { ...s2.input, width: 170 }, placeholder: "\u4EFB\u52A1\u540D\u2026", value: newTaskName, onChange: (e) => setNewTaskName(e.target.value), onKeyDown: (e) => {
3555
- if (e.key === "Enter") void createNewTask();
3556
- } }),
3557
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: { ...s2.input, width: 240 }, placeholder: "\u8981\u4EA7\u51FA / \u6EE1\u8DB3\u4EC0\u4E48\uFF08\u53EF\u9009\uFF09", value: newTaskDesc, onChange: (e) => setNewTaskDesc(e.target.value) }),
3558
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btnPrimary, disabled: newTaskBusy || newTaskName.trim() === "", onClick: () => void createNewTask(), children: newTaskBusy ? "\u2026" : "\u65B0\u5EFA\u4EFB\u52A1" })
3559
- ] }) }),
3560
- tasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.empty, children: "\u6682\u65E0\u4EFB\u52A1 \u2014 \u7528\u4E0A\u65B9\u300C\u65B0\u5EFA\u4EFB\u52A1\u300D\u6216 esr_task \u5DE5\u5177\u521B\u5EFA" }),
3561
- taskGroups.map(([ws, items]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.Fragment, { children: [
3562
- workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.groupLabel, children: [
3563
- ws,
3564
- " \xB7 ",
3565
- items.length,
3566
- " \u4E2A\u4EFB\u52A1"
3567
- ] }),
3568
- items.map((task) => {
3569
- const gaps = taskGaps(task);
3570
- const isStable = task.state === "stable";
3571
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.subPanel, children: [
3572
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontWeight: 600, fontSize: 13 }, children: [
3573
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: s2.mono, children: task.id.slice(0, 6) }),
3574
- " ",
3575
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3576
- EvidenceRing,
3577
- {
3578
- artifact: Boolean(task.artifact),
3579
- evaluation: Boolean(task.evaluation),
3580
- refs: (task.memoryRefs?.length ?? 0) > 0,
3581
- size: 22
3582
- }
3583
- ),
3584
- " ",
3585
- task.name,
3586
- " ",
3587
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...s2.badge, background: isStable ? "#059669" : gaps.length === 0 ? "#2563eb" : "#d97706" }, children: isStable ? "STABLE" : gaps.length === 0 ? "READY" : "ACTIVE" }),
3588
- !isStable && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, onClick: () => setCloseFor(closeFor === task.id ? null : task.id), children: closeFor === task.id ? "\u6536\u8D77" : "\u586B\u5199\u8BC1\u636E\u5173\u95ED\u2026" }),
3589
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.linkBtn, title: "\u5728\u4FA7\u680F\u6253\u5F00\u8BE6\u60C5", onClick: () => openTask(task.id), children: "\u8BE6\u60C5" })
3590
- ] }),
3591
- !isStable && gaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: 12, color: "var(--dsh-color-muted, #6b7280)", marginTop: 4 }, children: [
3592
- "\u7F3A\u53E3\uFF1A",
3593
- gaps.join(", "),
3594
- " \u2014 \u63D0\u4F9B artifact / evaluation / memory_ref \u540E\u8F6C\u4E3A STABLE"
3595
- ] }),
3596
- task.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-color-muted-strong, #4b5563)", marginTop: 4 }, children: task.description }),
3597
- task.memoryRefs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: 12, marginTop: 4 }, children: [
3598
- "\u8BB0\u5FC6\u5F15\u7528\uFF1A",
3599
- task.memoryRefs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: s2.tag, children: r.slice(0, 8) }, r))
3600
- ] }),
3601
- closeFor === task.id && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 8, padding: 8, border: "1px solid var(--dsh-color-border, #e5e7eb)", borderRadius: 8 }, children: [
3602
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-color-muted, #6b7280)", marginBottom: 6 }, children: "\u63D0\u4F9B\u8BC1\u636E\u540E\u5173\u95ED\uFF08\u4E09\u9879\u5168\u9F50\u624D\u8F6C STABLE\uFF09" }),
3603
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center" }, children: [
3604
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: { ...s2.input, width: 150 }, placeholder: "artifact \u8DEF\u5F84/URL", value: closeArtifact, onChange: (e) => setCloseArtifact(e.target.value) }),
3605
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: { ...s2.input, width: 150 }, placeholder: "evaluation \u9A8C\u8BC1\u8BC1\u636E", value: closeEval, onChange: (e) => setCloseEval(e.target.value) }),
3606
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { style: { ...s2.input, width: 150 }, placeholder: "memory_refs \u9017\u53F7\u5206\u9694", value: closeRefs, onChange: (e) => setCloseRefs(e.target.value) }),
3607
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btnPrimary, disabled: closeBusy, onClick: () => void submitClose(task), children: closeBusy ? "\u2026" : "\u63D0\u4EA4\u5173\u95ED" }),
3608
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { style: s2.btn, onClick: () => setCloseFor(null), children: "\u53D6\u6D88" })
3609
- ] })
3610
- ] })
3611
- ] }, task.id);
3612
- })
3613
- ] }, ws)),
3614
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.panelTitle, children: [
3615
- "\u8282\u70B9\u4E0E\u5173\u7CFB\uFF08esr_node / esr_link\uFF09",
3616
- workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsh-color-muted, #9ca3af)" }, children: "\xB7 \u5168\u90E8\u5DE5\u4F5C\u533A" })
3048
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3049
+ "entity ",
3050
+ target.memory.entity ?? "\u2014"
3617
3051
  ] }),
3618
- nodes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.empty, children: "\u6682\u65E0\u8282\u70B9 \u2014 \u6A21\u578B\u4F1A\u4E3A\u53CD\u590D\u51FA\u73B0\u7684\u9886\u57DF\u5BF9\u8C61\u4E3B\u52A8\u767B\u8BB0\uFF08esr_node\uFF09\uFF0C\u6B64\u5904\u4E5F\u53EF\u67E5\u770B\u5173\u7CFB" }),
3619
- nodeGroups.map(([ws, items]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.Fragment, { children: [
3620
- workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.groupLabel, children: [
3621
- ws,
3622
- " \xB7 ",
3623
- items.length,
3624
- " \u4E2A\u8282\u70B9"
3625
- ] }),
3626
- items.map((n) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: 12.5, padding: "2px 0", cursor: "pointer", borderRadius: 6 }, title: "\u5728\u4FA7\u680F\u6253\u5F00\u8282\u70B9\u8BE6\u60C5", onClick: () => openNode(n.id), children: [
3627
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mono", style: { ...s2.mono, color: "#4338ca" }, children: n.id.slice(0, 24) }),
3628
- " ",
3629
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontWeight: 600 }, children: n.name }),
3630
- n.kind && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...s2.tag, color: "#4338ca", background: "#eef2ff" }, children: n.kind }),
3631
- n.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { color: "var(--dsh-color-muted, #6b7280)" }, children: [
3632
- " \u2014 ",
3633
- n.description.slice(0, 48)
3634
- ] })
3635
- ] }, n.id))
3636
- ] }, ws)),
3637
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-color-muted, #6b7280)", marginTop: 6 }, children: "\u5173\u7CFB\uFF1A" }),
3638
- links.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.empty, children: "\u6682\u65E0\u5173\u7CFB \u2014 esr_link \u521B\u5EFA" }),
3639
- linkGroups.map(([ws, items]) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.Fragment, { children: [
3640
- workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.groupLabel, children: [
3641
- ws,
3642
- " \xB7 ",
3643
- items.length,
3644
- " \u6761\u5173\u7CFB"
3645
- ] }),
3646
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: items.map((l) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...s2.relRow, cursor: "pointer" }, title: "\u5728\u4FA7\u680F\u6253\u5F00\u5173\u7CFB\u8BE6\u60C5", onClick: () => {
3647
- setDetail({ kind: "link", link: l });
3648
- setDetailNotice(null);
3649
- }, children: [
3650
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mono", style: s2.nodePill, title: l.source, children: nameOf(l.source) }),
3651
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 3, fontSize: 11, color: "var(--dsh-color-muted, #6b7280)" }, children: [
3652
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { border: "1px dashed var(--dsh-color-border, #cbd5e1)", borderRadius: 999, padding: "1px 7px" }, children: l.relation }),
3653
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": true, children: "\u2192" })
3654
- ] }),
3655
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mono", style: s2.nodePill, title: l.target, children: nameOf(l.target) }),
3656
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: 11, color: "var(--dsh-color-muted-weak, #9ca3af)" }, children: [
3657
- "\xB7 ",
3658
- fmtDate2(l.createdAt)
3659
- ] })
3660
- ] }, l.id)) })
3661
- ] }, ws))
3052
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3053
+ "session ",
3054
+ target.memory.sessionId,
3055
+ " \xB7 seq ",
3056
+ target.memory.seq
3057
+ ] }),
3058
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3059
+ "created ",
3060
+ fmtDate(target.memory.createdAt),
3061
+ " \xB7 updated ",
3062
+ fmtDate(target.memory.updatedAt)
3063
+ ] }),
3064
+ target.memory.source && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3065
+ "source ",
3066
+ target.memory.source
3067
+ ] }),
3068
+ target.memory.conditions && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3069
+ "conditions ",
3070
+ target.memory.conditions
3071
+ ] }),
3072
+ target.memory.filePath && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3073
+ "file ",
3074
+ target.memory.filePath
3075
+ ] }),
3076
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3077
+ "TTL ",
3078
+ daysLeft(target.memory.expiresAt)
3079
+ ] })
3662
3080
  ] })
3663
- ] }),
3664
- view === "preview" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.subPanel, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3665
- EngramPreview,
3666
- {
3667
- api,
3668
- workspace,
3669
- defaultWorkspace: previewDefault,
3670
- workspaces
3671
- }
3672
- ) }),
3673
- view === "telemetry" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.subPanel, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(EngramTelemetry, { api, workspace }) })
3081
+ ] })
3674
3082
  ] }),
3675
- detail && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { flex: "0 0 320px", maxWidth: "38%", position: "sticky", top: 8 }, children: [
3676
- detailNotice && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: 11.5, padding: "6px 10px", marginBottom: 6, borderRadius: 8, background: "rgba(245,158,11,.14)", color: "#b45309", border: "1px solid rgba(245,158,11,.35)" }, children: detailNotice }),
3677
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
3678
- EngramDetail,
3679
- {
3680
- target: detail,
3681
- api,
3682
- tasks,
3683
- nodes,
3684
- links,
3685
- memories,
3686
- onClose: () => setDetail(null),
3687
- onNavigateMemory: openMemory,
3688
- onChanged: () => void refresh()
3689
- }
3690
- )
3691
- ] })
3692
- ] })
3693
- ] });
3694
- }
3695
-
3696
- // client/src/EngramConfigCard.tsx
3697
- var import_react8 = require("react");
3698
- var import_jsx_runtime8 = require("react/jsx-runtime");
3699
- var COMMON_FIELDS = [
3700
- { key: "autoCapture", label: "\u81EA\u52A8\u6355\u83B7", hint: "\u96F6 LLM \u4ECE\u5DE5\u5177\u7ED3\u679C\u63D0\u53D6\u8BB0\u5FC6\uFF08git/\u5173\u952E\u6587\u4EF6/\u9519\u8BEF\uFF09", kind: "bool" },
3701
- { key: "autoCapturePreferences", label: "\u504F\u597D\u6355\u83B7", hint: "\u7528\u6237\u7EA0\u6B63/\u504F\u597D\u5B66\u4E60\uFF1A\u68C0\u6D4B\u6D88\u606F\u91CC\u7684\u300C\u4E0D\u8981\u7528X/\u6539\u7528Y/prefer/never use\u300D\u53E5\u5F0F\uFF0C\u81EA\u52A8\u6C89\u6DC0\u4E3A preference \u8BB0\u5FC6\u5E76\u7F6E\u9876 [ENGRAM]\uFF08correx/PAHF \u5F0F\uFF0C\u96F6 LLM\uFF09", kind: "bool" },
3702
- { key: "boundaryPrune", label: "\u8FB9\u754C\u526A\u679D", hint: "ResultPack L1\uFF1Atodo \u5B8C\u6210/goal \u7EC8\u6001\u8FB9\u754C\u4E3B\u52A8\u526A\u6389\u8D85\u9608\u503C\u65E7\u5DE5\u5177\u7ED3\u679C\uFF08DSH \u786E\u5B9A\u6027 surface-replace\u3001\u507F\u8FD8\u95E8\u3001\u96F6 LLM\uFF1B\u9ED8\u8BA4\u5F00\uFF0C\u5173=opt-out\uFF09", kind: "bool" },
3703
- { key: "boundaryPruneAdaptive", label: "\u526A\u679D\u81EA\u9002\u5E94", hint: "\u8D26\u672C\u5426\u51B3\uFF1A\u8BE5 workspace \u771F\u5B9E\u526A\u679D\u540E\u51C0\u6536\u76CA\u4E3A\u8D1F\uFF08\u22653 \u6B21\uFF09\u65F6\u81EA\u52A8\u505C\u6B62\u7EE7\u7EED\u526A\uFF1B\u8BC1\u636E\u53EA\u80FD\u5173\u3001\u4E0D\u4F1A\u81EA\u884C\u5F00", kind: "bool" },
3704
- { key: "boundaryPruneThresholdChars", label: "\u526A\u679D\u9608\u503C(\u5B57\u7B26)", hint: "\u8986\u76D6\u5BBF\u4E3B 8192\uFF1A\u8BBE 2048 \u53EF\u66F4\u65E9\u5F00\u526A\uFF0C\u5E76\u914D L2 \u5F52\u6863\u627E\u56DE\u88AB\u526A\u4E2D\u6BB5\uFF1B\u7559\u7A7A=\u5BBF\u4E3B\u9ED8\u8BA4\u3002\u5EFA\u8BAE\u540C\u65F6\u628A\u4FDD\u7559\u8FB9\u7F18\u8C03\u5C0F\uFF08\u5982 1024/256\uFF09\u4EE5\u83B7\u5F97\u6700\u5927\u8282\u7701", kind: "num", min: 256, max: 16384, step: 256 },
3705
- { key: "resultPack", label: "\u7ED3\u679C\u5F52\u6863\u53EC\u56DE", hint: "ResultPack L2\uFF1A\u526A\u6389\u7684\u4E2D\u6BB5\u8131\u654F\u5F52\u6863\uFF0C\u8282\u70B9\u5C3E\u90E8\u9644 handle\uFF0C\u6A21\u578B\u53EF\u7528 result_recall(callId) \u6309\u9875\u53EC\u56DE\uFF08\u5B8C\u6574 ObservationPack\uFF1B\u9ED8\u8BA4\u5F00\uFF0C\u4FDD\u8BC1\u9ED8\u8BA4\u526A\u679D\u65E0\u635F\uFF1B\u9700\u4E0E\u8FB9\u754C\u526A\u679D\u4E00\u8D77\u5F00\uFF09", kind: "bool" },
3706
- { key: "autoRecallOnStart", label: "\u4F1A\u8BDD\u542F\u52A8\u81EA\u52A8\u53EC\u56DE", hint: "\u65B0\u4F1A\u8BDD\u6309\u9996\u6761\u6D88\u606F\u81EA\u52A8\u628A\u6700\u76F8\u5173\u8BB0\u5FC6\u6CE8\u5165 [RECALL] \u5757\uFF08\u65E0\u9700\u6A21\u578B\u60F3\u8D77\u8C03 engram_recall\uFF09\uFF1B\u5173\u6389=\u56DE\u5230\u7EAF\u62C9\u53D6", kind: "bool" },
3707
- { key: "jitEnabled", label: "JIT \u63A8\u9001", hint: "\u9519\u8BEF\u590D\u53D1 / \u6587\u4EF6\u7126\u70B9 / goal \u5B8C\u6210\u65F6\uFF0C\u628A\u5DF2\u6709\u8BB0\u5FC6\u538B\u6210\u4E00\u884C\u63A8\u8FDB\u4E0A\u4E0B\u6587\uFF08append-only\u3001\u6BCF\u4F1A\u8BDD\u22643 \u884C\u3001\u65E0\u547D\u4E2D\u6C89\u9ED8\uFF1B\u6BCF\u6B21\u63A8\u9001\u6309\u524D\u7F00\u91CD\u5199\u8BA1\u5165\u8D26\u672C\uFF09", kind: "bool" },
3708
- { key: "episodeEnabled", label: "\u4F1A\u8BDD\u7EAA\u8981", hint: "\u4F1A\u8BDD\u7ED3\u675F\u65F6\u7528\u96F6 LLM \u89C4\u5219\u751F\u6210\u4E00\u6761 episode\uFF08\u6587\u4EF6/\u547D\u4EE4/\u5931\u8D25\u2192\u4FEE\u590D/\u51B3\u7B56/\u672A\u95ED\u73AF\uFF09\uFF0C\u4E0D\u8FDB\u7D22\u5F15\uFF0C\u4F9B\u6062\u590D\u7B80\u62A5\u4F7F\u7528\uFF1B\u6BCF workspace \u4FDD\u7559\u6700\u8FD1 20 \u6761", kind: "bool" },
3709
- { key: "resumeBriefEnabled", label: "\u6062\u590D\u7B80\u62A5", hint: "\u65B0\u4F1A\u8BDD\u542F\u52A8\u65F6\u6CE8\u5165\u6700\u8FD1 7 \u5929\u7684\u6700\u65B0 episode\uFF08\u2264240 \u5B57\u7B26 + engram_detail \u6307\u9488\uFF09\uFF0C\u9694\u5929\u7EE7\u7EED\u4E0D\u7528\u91CD\u65B0\u4EA4\u4EE3\u80CC\u666F", kind: "bool" },
3710
- { key: "entityCardsEnabled", label: "\u5B9E\u4F53\u5361", hint: "[ENGRAM] \u7D22\u5F15\u4E3A\u6D3B\u8DC3\u5B9E\u4F53\uFF08\u6709\u951A\u5B9A\u8BB0\u5FC6\u6216\u672A\u95ED\u73AF\u4EFB\u52A1\uFF09\u6E32\u67D3\u4E00\u884C\u300C\u5F53\u524D\u771F\u76F8 + \u6253\u5F00\u4EFB\u52A1 + engram_detail(entity=\u2026) \u6307\u9488\u300D\uFF0C\u6700\u591A 2 \u5F20\uFF0C\u66FF\u4EE3\u8FD9\u4E9B\u5B9E\u4F53\u7684\u5E73\u94FA\u8BB0\u5FC6\u884C", kind: "bool" },
3711
- { key: "maxRecallPerSession", label: "\u6BCF\u4F1A\u8BDD\u53BB\u91CD\u4E0A\u9650", hint: "recall \u6BCF\u4F1A\u8BDD\u6700\u591A\u4FDD\u7559 N \u6761\uFF08\u8DE8\u4F1A\u8BDD\u53BB\u91CD\uFF09", kind: "num", min: 1, max: 10 },
3712
- { key: "expireDays", label: "TTL\uFF08\u5929\uFF09", hint: "0 = \u4E0D\u8FC7\u671F", kind: "num", min: 0, max: 3650 },
3713
- { key: "indexMaxChars", label: "\u7D22\u5F15\u5B57\u7B26\u4E0A\u9650", hint: "[ENGRAM] \u5757 token \u9884\u7B97", kind: "num", min: 0, max: 4e3 }
3714
- ];
3715
- var ADVANCED_GROUPS = [
3716
- {
3717
- id: "capture-adv",
3718
- title: "\u6355\u83B7\u4E0E\u68C0\u7D22\uFF08\u9AD8\u7EA7\uFF09",
3719
- description: "\u5176\u4F59\u6355\u83B7/\u641C\u7D22\u65CB\u94AE",
3720
- fields: [
3721
- { key: "autoCapturePerSession", label: "\u6BCF\u4F1A\u8BDD\u6355\u83B7\u4E0A\u9650", hint: "\u5355\u4F1A\u8BDD\u81EA\u52A8\u6355\u83B7\u6761\u6570\u4E0A\u9650", kind: "num", min: 0, max: 1e3 },
3722
- { key: "boundaryPruneHeadChars", label: "\u526A\u679D\u4FDD\u7559\u5934\u90E8(\u5B57\u7B26)", hint: "\u9608\u503C\u8986\u76D6\u751F\u6548\u65F6\u7ED3\u679C\u5F00\u5934\u4FDD\u7559\u957F\u5EA6\uFF08\u9ED8\u8BA4 4096\uFF09\uFF1B\u8C03\u5C0F\uFF08\u5982 1024\uFF09\u914D\u5408 T2048 \u66F4\u7701", kind: "num", min: 0, max: 16384, step: 128 },
3723
- { key: "boundaryPruneTailChars", label: "\u526A\u679D\u4FDD\u7559\u5C3E\u90E8(\u5B57\u7B26)", hint: "\u9608\u503C\u8986\u76D6\u751F\u6548\u65F6\u7ED3\u679C\u7ED3\u5C3E\u4FDD\u7559\u957F\u5EA6\uFF08\u9ED8\u8BA4 1024\uFF09\uFF1B\u8C03\u5C0F\uFF08\u5982 256\uFF09\u914D\u5408 T2048 \u66F4\u7701", kind: "num", min: 0, max: 16384, step: 128 },
3724
- { key: "boundaryPruneIdleCadence", label: "\u7A7A\u95F2\u6B65\u6570\u8FB9\u754C", hint: "\u6700\u591A\u6BCF N \u8BF7\u6C42\u89E6\u53D1\u4E00\u6B21\u526A\u679D\uFF0C\u8986\u76D6\u65E0 todo/goal \u7684\u4F1A\u8BDD\uFF08\u7EA6 62% \u4F1A\u8BDD\uFF09\uFF1B\u4ECD\u53D7\u507F\u8FD8\u95E8\u7EA6\u675F\uFF1B\u7559\u7A7A=\u4EC5\u4E8B\u4EF6\u8FB9\u754C", kind: "num", min: 10, max: 1e3, step: 10 },
3725
- { key: "autoRecallLimit", label: "\u81EA\u52A8\u53EC\u56DE\u6761\u6570", hint: "[RECALL] \u5757\u6700\u591A\u6CE8\u5165\u7684\u8BB0\u5FC6\u6761\u6570\uFF08\u5C11\u800C\u51C6\uFF09", kind: "num", min: 1, max: 8 },
3726
- { key: "sessionSearch", label: "\u4F1A\u8BDD\u5386\u53F2\u641C\u7D22", hint: "engram_recall \u652F\u6301\u8DE8\u4F1A\u8BDD FTS \u515C\u5E95", kind: "bool" },
3727
- { key: "recallScope", label: "\u53EC\u56DE\u8303\u56F4", hint: "workspace=\u4E25\u683C\u5F53\u524D\u5DE5\u4F5C\u533A\u9694\u79BB\uFF08\u9ED8\u8BA4\uFF0Ctoken \u7EAA\u5F8B\uFF09\uFF1Bglobal=\u672C\u5DE5\u4F5C\u533A\u547D\u4E2D\u5F31/\u7A7A\u65F6\u4ECE\u5176\u5B83\u5DE5\u4F5C\u533A\u6C60\u53EC\u56DE\u5E76\u6807 [W:<workspace>] \u6765\u6E90\uFF08\u4F1A\u6253\u7834\u9694\u79BB\uFF09", kind: "select", options: ["workspace", "global"] }
3728
- ]
3729
- },
3730
- {
3731
- id: "index-adv",
3732
- title: "\u7D22\u5F15\uFF08\u9AD8\u7EA7\uFF09",
3733
- description: "\u884C\u6570\u3001\u5165\u7D22\u5F15\u4FE1\u53F7\u4E0E\u664B\u5347\u89C4\u5219",
3734
- fields: [
3735
- { key: "indexMaxLines", label: "\u7D22\u5F15\u6700\u5927\u884C\u6570", hint: "[ENGRAM] \u5757\u6700\u591A\u663E\u793A\u7684\u6761\u76EE\u884C\u6570", kind: "num", min: 0, max: 50 },
3736
- { key: "minIndexSignal", label: "\u5165\u7D22\u5F15\u4FE1\u53F7\u9608\u503C", hint: "signal \u2265 \u6B64\u503C\u7684\u81EA\u52A8\u6355\u83B7\u624D\u8FDB\u7D22\u5F15", kind: "num", min: 0, max: 1, step: 0.05 },
3737
- { key: "promoteHits", label: "\u664B\u5347\u547D\u4E2D\u6570", hint: "hit \u6570\u8FBE\u6B64\u503C\u7684\u4F4E\u4FE1\u53F7\u6761\u76EE\u8FDB\u7D22\u5F15", kind: "num", min: 0, max: 20 }
3738
- ]
3739
- },
3740
- {
3741
- id: "retention-adv",
3742
- title: "\u751F\u547D\u5468\u671F\u4E0E GC\uFF08\u9AD8\u7EA7\uFF09",
3743
- description: "\u5BB9\u91CF\u4E0A\u9650\u4E0E\u5B9A\u671F\u56DE\u6536\uFF08\u81EA\u52A8 GC = \u66FF\u4EE3 DSH \u81EA\u52A8 compact\uFF09",
3744
- fields: [
3745
- { key: "maxMemoriesPerWorkspace", label: "\u5DE5\u4F5C\u533A\u8BB0\u5FC6\u4E0A\u9650", hint: "\u5355\u5DE5\u4F5C\u533A\u5BB9\u91CF\u4E0A\u9650\uFF08GC \u5F52\u6863\u8D85\u9650\u6761\u76EE\uFF09", kind: "num", min: 0, max: 1e4 },
3746
- { key: "gcEnabled", label: "\u8BB0\u5FC6 GC", hint: "\u5B9A\u65F6\u56DE\u6536\uFF08\u8FC7\u671F/\u8D85\u5BB9\u91CF/stable \u8D85\u7A97/\u60AC\u7A7A\u94FE\u63A5\uFF09", kind: "bool" },
3747
- { key: "gcNarrative", label: "Context GC \u53D9\u4E8B\u515C\u5E95", hint: "\u81EA\u52A8 compact \u65F6\u65E0\u951A\u8F6E\u6B21\u7528 LLM \u6458\u8981\uFF1B\u5173\u6389=\u7EAF\u673A\u68B0\uFF08\u96F6 LLM\uFF09", kind: "bool" },
3748
- { key: "gcStableRetentionDays", label: "stable \u4EFB\u52A1\u4FDD\u7559\uFF08\u5929\uFF09", hint: "\u8D85\u7A97\u540E\u7531 GC \u5F52\u6863\u3001\u79BB\u5F00 [ESR] \u8868\u9762", kind: "num", min: 0, max: 3650 },
3749
- { key: "verifyArtifact", label: "\u6821\u9A8C artifact \u5B58\u5728", hint: "esr_close \u7684 artifact \u987B\u5728\u5DE5\u4F5C\u533A\u78C1\u76D8\u4E0A\u771F\u5B9E\u5B58\u5728\uFF08URL \u8C41\u514D\uFF09\uFF1Bforce:true/\u5173\u6389\u53EF\u8DF3\u8FC7", kind: "bool" },
3750
- { key: "autoSinkTodosOnEnd", label: "\u4F1A\u8BDD\u7ED3\u675F\u81EA\u52A8\u6C89\u6DC0 todo", hint: "\u4F1A\u8BDD\u7ED3\u675F\u65F6\u4ECD pending \u7684 todo \u81EA\u52A8\u8F6C\u6210 ESR draft\uFF08\u6309\u540D\u53BB\u91CD\u3001\u53D7\u4EFB\u52A1\u4E0A\u9650\u7EA6\u675F\uFF09\uFF1B\u5173\u6389=\u968F\u4F1A\u8BDD\u5F03\u53BB", kind: "bool" },
3751
- { key: "autoWebCompaction", label: "web \u9762\u81EA\u52A8\u542F\u7528 Context GC", hint: "\u542F\u52A8\u65F6\u628A\u5168\u90E8 stock \u5E03\u5C40\u7684 agent \u9884\u8BBE\uFF08shipped + \u81EA\u5B9A\u4E49\u6839\uFF09\u7684 compaction \u7EC4\u81EA\u52A8\u6362\u6210 dsh-engram/compaction\uFF1B\u5173\u6389=\u4E0D\u518D\u81EA\u52A8\u63A5\u7BA1\uFF08\u5DF2\u63A5\u7BA1\u7684\u9700 web-compaction:revert \u8FD8\u539F\uFF09", kind: "bool" }
3752
- ]
3753
- },
3754
- {
3755
- id: "semantics-adv",
3756
- title: "\u8BB0\u5FC6\u8BED\u4E49\uFF08\u9AD8\u7EA7\uFF09",
3757
- description: "supersedes / contradict \u7684\u542F\u53D1\u5F0F",
3758
- fields: [
3759
- { key: "autoSupersede", label: "\u81EA\u52A8 supersedes", hint: "\u5B9E\u4F53\u951A\u5B9A + \u66FF\u6362\u5F0F\u66F4\u65B0\uFF08\u6539\u7528/\u4E0D\u518D/no longer\u2026\uFF09\u81EA\u52A8\u628A\u65E7\u8BB0\u5FC6\u6807\u4E3A superseded\uFF1B\u65E7\u884C\u4FDD\u7559\u3001\u53EC\u56DE\u964D\u7EA7\u3002\u9ED8\u8BA4\u5173\uFF0C\u663E\u5F0F supersedes \u6C38\u8FDC\u4F18\u5148", kind: "bool" }
3760
- ]
3761
- },
3762
- {
3763
- id: "security",
3764
- title: "\u5B89\u5168",
3765
- description: "\u8BBF\u95EE\u56F4\u680F\u4E0E\u96A7\u9053\u6388\u6743",
3766
- fields: [
3767
- { key: "trustedHosts", label: "\u53D7\u4FE1\u96A7\u9053\u57DF\u540D", hint: "\u5141\u8BB8\u7ECF\u96A7\u9053\u8BBF\u95EE\u8BB0\u5FC6\u67E5\u770B\u5668\u7684\u57DF\u540D\uFF0C\u9017\u53F7\u5206\u9694\uFF1B\u7559\u7A7A = \u4EC5\u672C\u673A\u3002\u6539\u540E\u9700\u91CD\u542F dsh web \u751F\u6548", kind: "text", width: 260 }
3768
- ]
3769
- }
3770
- ];
3771
- var FIELDS = [...COMMON_FIELDS, ...ADVANCED_GROUPS.flatMap((group) => group.fields)];
3772
- var CARD_DESCRIPTION = "\u63A7\u5236 engram \u8BB0\u5FC6\u7684\u6355\u83B7\u3001\u7D22\u5F15\u3001\u4FDD\u7559\u4E0E\u96A7\u9053\u8BBF\u95EE";
3083
+ target.kind === "node" && !node && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.v, children: "\u8282\u70B9\u4E0D\u5B58\u5728\u3002" }),
3084
+ target.kind === "node" && node && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3085
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3086
+ kindBadge("node", node.kind || "\u8282\u70B9"),
3087
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: node.name })
3088
+ ] }),
3089
+ node.description && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3090
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u63CF\u8FF0" }),
3091
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { ...hb2.v, marginTop: 3 }, children: node.description })
3092
+ ] }),
3093
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3094
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u5143\u6570\u636E" }),
3095
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 3, fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "var(--dsh-color-muted, #6b7280)", display: "flex", flexDirection: "column", gap: 2 }, children: [
3096
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3097
+ node.id,
3098
+ " \xB7 ",
3099
+ node.workspace
3100
+ ] }),
3101
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3102
+ "created ",
3103
+ fmtDate(node.createdAt)
3104
+ ] })
3105
+ ] })
3106
+ ] }),
3107
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3108
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.k, children: [
3109
+ "\u5173\u8054\u5173\u7CFB\uFF08",
3110
+ incident.length,
3111
+ "\uFF09"
3112
+ ] }),
3113
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 5, marginTop: 3 }, children: [
3114
+ incident.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.v, children: "\u6682\u65E0\u5173\u7CFB \u2014 \u7528 esr_link \u5173\u8054" }),
3115
+ incident.map(({ l, outgoing }) => {
3116
+ const other = outgoing ? l.target : l.source;
3117
+ const color = REL_COLORS[l.relation] ?? "#6b7280";
3118
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.relRow, children: [
3119
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...hb2.dot, background: color } }),
3120
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "mono", style: { fontSize: 11 }, children: [
3121
+ outgoing ? "\u2192" : "\u2190",
3122
+ " ",
3123
+ nameOf(other)
3124
+ ] }),
3125
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: hb2.relType, children: l.relation }),
3126
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontSize: 11, color: "var(--dsh-color-muted-weak, #9ca3af)" }, children: [
3127
+ Math.round(l.confidence * 100),
3128
+ "%"
3129
+ ] }),
3130
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 11, color: "var(--dsh-color-muted-weak, #9ca3af)" }, children: fmtDate(l.createdAt) })
3131
+ ] }, l.id);
3132
+ })
3133
+ ] })
3134
+ ] })
3135
+ ] }),
3136
+ target.kind === "link" && link && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
3137
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.relRow, children: [
3138
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mono", style: { fontSize: 12, fontWeight: 600 }, children: nameOf(link.source) }),
3139
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { ...hb2.relType, color: REL_COLORS[link.relation] ?? "var(--dsh-color-muted, #6b7280)", borderColor: (REL_COLORS[link.relation] ?? "#cbd5e1") + "66" }, children: link.relation }),
3140
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "aria-hidden": true, children: "\u2192" }),
3141
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "mono", style: { fontSize: 12, fontWeight: 600 }, children: nameOf(link.target) })
3142
+ ] }),
3143
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: hb2.section, children: [
3144
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: hb2.k, children: "\u5143\u6570\u636E" }),
3145
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { marginTop: 3, fontSize: 11.5, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", color: "var(--dsh-color-muted, #6b7280)", display: "flex", flexDirection: "column", gap: 2 }, children: [
3146
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3147
+ "id ",
3148
+ link.id,
3149
+ " \xB7 ",
3150
+ link.workspace
3151
+ ] }),
3152
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3153
+ "confidence ",
3154
+ Math.round(link.confidence * 100),
3155
+ "%"
3156
+ ] }),
3157
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3158
+ "created ",
3159
+ fmtDate(link.createdAt)
3160
+ ] }),
3161
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
3162
+ link.source,
3163
+ " \u2192 ",
3164
+ link.target
3165
+ ] })
3166
+ ] })
3167
+ ] })
3168
+ ] })
3169
+ ] })
3170
+ ] });
3171
+ }
3172
+
3173
+ // client/src/EngramSection.tsx
3174
+ var import_jsx_runtime8 = require("react/jsx-runtime");
3175
+ function signedTokens(n) {
3176
+ const sign = n > 0 ? "+" : n < 0 ? "\u2212" : "";
3177
+ const abs = Math.abs(n);
3178
+ return abs >= 1e3 ? `${sign}${(abs / 1e3).toFixed(1)}k` : `${sign}${abs}`;
3179
+ }
3180
+ var KIND_COLORS2 = {
3181
+ preference: "#b45309",
3182
+ episode: "#0e7490",
3183
+ decision: "#2563eb",
3184
+ error: "#dc2626",
3185
+ procedure: "#7c3aed",
3186
+ fact: "#059669",
3187
+ insight: "#d97706",
3188
+ handoff: "#0891b2",
3189
+ task: "#4f46e5"
3190
+ };
3191
+ var KIND_LABEL2 = {
3192
+ preference: "\u504F\u597D",
3193
+ decision: "\u51B3\u5B9A",
3194
+ error: "\u9519\u8BEF",
3195
+ procedure: "\u6D41\u7A0B",
3196
+ fact: "\u4E8B\u5B9E",
3197
+ insight: "\u6D1E\u5BDF",
3198
+ handoff: "\u4EA4\u63A5",
3199
+ task: "\u4EFB\u52A1"
3200
+ };
3773
3201
  var s3 = {
3202
+ root: { padding: "2px 4px 40px" },
3203
+ h1: { fontSize: 20, fontWeight: 700, margin: "0 0 4px" },
3204
+ sub: { color: "var(--dsh-color-muted, #6b7280)", fontSize: 12, margin: "0 0 16px" },
3205
+ stats: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))", gap: 8, marginBottom: 16 },
3774
3206
  card: {
3775
- listStyle: "none",
3776
- border: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
3207
+ border: "1px solid var(--dsh-color-border, #e5e7eb)",
3777
3208
  borderRadius: 12,
3778
- background: "var(--dsw-alias-bg-layer-3, #ffffff)",
3779
- transition: "border-color .16s, background .16s"
3780
- },
3781
- cardOpen: {
3782
- background: "var(--dsw-alias-bg-layer-2, #ffffff)",
3783
- borderColor: "var(--dsw-alias-label-dimmed, #9ca3af)"
3784
- },
3785
- header: {
3786
- width: "100%",
3787
- appearance: "none",
3788
- border: 0,
3789
- background: "none",
3790
- font: "inherit",
3791
- color: "inherit",
3792
- textAlign: "left",
3793
- cursor: "pointer",
3794
- display: "flex",
3795
- alignItems: "center",
3796
- gap: 12,
3797
- padding: "14px 16px",
3798
- borderRadius: 12
3799
- },
3800
- headText: {
3801
- flex: 1,
3802
- minWidth: 0,
3803
- display: "flex",
3804
- flexDirection: "column",
3805
- gap: 4
3806
- },
3807
- name: { fontSize: 15, fontWeight: 600, lineHeight: 1.4, color: "var(--dsw-alias-label-primary, #111827)" },
3808
- description: { fontSize: 13, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary, #6b7280)" },
3809
- pending: {
3810
- flex: "none",
3811
- borderRadius: 999,
3812
- padding: "1px 8px",
3813
- fontSize: 11,
3814
- lineHeight: "17px",
3815
- fontWeight: 500,
3816
- whiteSpace: "nowrap",
3817
- background: "var(--dsw-alias-bg-module-platform, #f3f4f6)",
3818
- color: "var(--dsw-alias-label-secondary, #4b5563)"
3819
- },
3820
- body: {
3821
- borderTop: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
3822
- margin: "0 16px",
3823
- paddingBottom: 8
3824
- },
3825
- readOnly: { margin: "12px 0 0", fontSize: 12, lineHeight: 1.5, color: "var(--dsw-alias-label-tertiary, #6b7280)" },
3826
- groupPanel: {
3827
- marginTop: 10,
3828
- border: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
3829
- borderRadius: 10,
3830
- padding: "8px 12px 2px",
3831
- background: "var(--dsw-alias-bg-layer-3, #ffffff)"
3832
- },
3833
- groupHead: { display: "flex", alignItems: "baseline", gap: 8, marginBottom: 2 },
3834
- groupTitle: { fontSize: 13, fontWeight: 700, letterSpacing: 0.2, color: "var(--dsw-alias-label-primary, #111827)" },
3835
- groupDesc: { fontSize: 11, color: "var(--dsw-alias-label-tertiary, #6b7280)" },
3836
- row: {
3837
- display: "flex",
3838
- alignItems: "center",
3839
- justifyContent: "space-between",
3840
- gap: 12,
3841
- padding: "8px 0",
3842
- borderBottom: "1px solid var(--dsw-alias-border-l1, #f3f4f6)"
3209
+ padding: "10px 12px",
3210
+ background: "var(--dsh-color-surface, #ffffff)",
3211
+ boxShadow: "0 1px 2px rgba(15,23,42,.04), 0 2px 8px rgba(15,23,42,.03)"
3843
3212
  },
3844
- label: { fontSize: 13, fontWeight: 600, color: "var(--dsw-alias-label-primary, #111827)" },
3845
- hint: { fontSize: 11.5, lineHeight: 1.4, color: "var(--dsw-alias-label-tertiary, #6b7280)", marginTop: 2 },
3213
+ cardNum: { fontSize: 22, fontWeight: 700, lineHeight: 1.2 },
3214
+ cardLabel: { color: "var(--dsh-color-muted, #6b7280)", fontSize: 11, marginTop: 2 },
3215
+ row: { display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: 12 },
3846
3216
  input: {
3847
- width: 90,
3848
- border: "1px solid var(--dsw-alias-border-l2, #d1d5db)",
3217
+ border: "1px solid var(--dsh-color-border, #d1d5db)",
3849
3218
  borderRadius: 6,
3850
- padding: "4px 7px",
3851
- fontSize: 12.5,
3852
- background: "var(--dsw-alias-bg-layer-3, #fff)",
3219
+ padding: "5px 8px",
3220
+ fontSize: 13,
3221
+ background: "var(--dsh-color-surface, #fff)",
3853
3222
  color: "inherit"
3854
3223
  },
3855
- footer: {
3856
- display: "flex",
3857
- alignItems: "center",
3858
- justifyContent: "flex-end",
3859
- gap: 8,
3860
- padding: "12px 0 4px",
3861
- borderTop: "1px solid var(--dsw-alias-border-l2, #e5e7eb)"
3862
- },
3863
- reset: {
3864
- appearance: "none",
3865
- border: 0,
3866
- background: "none",
3867
- font: "inherit",
3224
+ btn: {
3225
+ border: "1px solid var(--dsh-color-border, #d1d5db)",
3226
+ borderRadius: 8,
3227
+ padding: "5px 11px",
3868
3228
  fontSize: 12,
3869
3229
  cursor: "pointer",
3870
- color: "var(--dsw-alias-label-tertiary, #6b7280)"
3230
+ background: "var(--dsh-color-surface, #fff)",
3231
+ transition: "background .15s ease, border-color .15s ease"
3871
3232
  },
3872
- discard: {
3873
- appearance: "none",
3874
- border: "1px solid var(--dsw-alias-border-l2, #d1d5db)",
3233
+ btnPrimary: {
3234
+ border: "1px solid transparent",
3875
3235
  borderRadius: 8,
3876
- padding: "5px 14px",
3877
- font: "inherit",
3878
- fontSize: 13,
3879
- lineHeight: 1.5,
3236
+ padding: "5px 11px",
3237
+ fontSize: 12,
3880
3238
  cursor: "pointer",
3239
+ color: "#fff",
3240
+ background: "linear-gradient(135deg, #6366f1 0%, #7c3aed 100%)",
3241
+ boxShadow: "0 1px 3px rgba(99,102,241,.35)",
3242
+ transition: "opacity .15s ease"
3243
+ },
3244
+ table: { width: "100%", borderCollapse: "collapse", fontSize: 12.5, tableLayout: "fixed" },
3245
+ clamp3: { display: "-webkit-box", WebkitLineClamp: 3, WebkitBoxOrient: "vertical", overflow: "hidden", maxHeight: 60, minHeight: 18, lineHeight: 1.5, wordBreak: "break-word", whiteSpace: "normal" },
3246
+ expanded: { whiteSpace: "pre-wrap", wordBreak: "break-word", lineHeight: 1.5 },
3247
+ linkBtn: { border: "none", background: "none", padding: 0, fontSize: 11.5, cursor: "pointer", color: "var(--dsh-color-primary, #2563eb)", textDecoration: "underline" },
3248
+ pageBar: { display: "flex", alignItems: "center", gap: 8, marginTop: 8, fontSize: 12.5, color: "var(--dsh-color-muted, #6b7280)", flexWrap: "wrap" },
3249
+ tabBar: {
3250
+ display: "inline-flex",
3251
+ gap: 4,
3252
+ padding: 3,
3253
+ background: "var(--dsh-color-hover-bg, #f3f4f6)",
3254
+ borderRadius: 999,
3255
+ marginBottom: 14
3256
+ },
3257
+ tab: {
3258
+ border: "none",
3881
3259
  background: "none",
3882
- color: "var(--dsw-alias-label-secondary, #4b5563)"
3260
+ padding: "6px 16px",
3261
+ fontSize: 13,
3262
+ cursor: "pointer",
3263
+ color: "var(--dsh-color-muted, #6b7280)",
3264
+ borderRadius: 999,
3265
+ fontWeight: 600,
3266
+ margin: 0,
3267
+ transition: "background .16s ease, color .16s ease"
3883
3268
  },
3884
- save: {
3885
- appearance: "none",
3886
- border: "1px solid transparent",
3887
- borderRadius: 8,
3888
- padding: "5px 14px",
3889
- font: "inherit",
3269
+ tabActive: {
3270
+ border: "none",
3271
+ background: "var(--dsh-color-surface, #fff)",
3272
+ color: "var(--dsh-color-primary, #2563eb)",
3273
+ padding: "6px 16px",
3890
3274
  fontSize: 13,
3891
- lineHeight: 1.5,
3892
3275
  cursor: "pointer",
3893
- background: "var(--dsw-alias-label-primary, #111827)",
3894
- color: "var(--dsw-alias-bg-layer-3, #ffffff)"
3276
+ borderRadius: 999,
3277
+ fontWeight: 700,
3278
+ margin: 0,
3279
+ boxShadow: "0 1px 3px rgba(15,23,42,.10)"
3280
+ },
3281
+ th: {
3282
+ textAlign: "left",
3283
+ padding: "6px 8px",
3284
+ borderBottom: "1px solid var(--dsh-color-border, #e5e7eb)",
3285
+ color: "var(--dsh-color-muted, #6b7280)",
3286
+ fontWeight: 600,
3287
+ whiteSpace: "nowrap"
3288
+ },
3289
+ td: { padding: "6px 8px", borderBottom: "1px solid var(--dsh-color-border, #f3f4f6)", verticalAlign: "top" },
3290
+ wsHead: {
3291
+ padding: "8px 8px 4px",
3292
+ fontSize: 12.5,
3293
+ fontWeight: 600,
3294
+ color: "var(--dsh-color-muted, #4b5563)",
3295
+ background: "var(--dsh-color-hover-bg, #f3f4f6)",
3296
+ borderBottom: "1px solid var(--dsh-color-border, #e5e7eb)"
3297
+ },
3298
+ badge: { display: "inline-block", borderRadius: 5, padding: "1px 6px", fontSize: 11, color: "#fff", whiteSpace: "nowrap" },
3299
+ tag: {
3300
+ display: "inline-block",
3301
+ borderRadius: 4,
3302
+ padding: "0 5px",
3303
+ fontSize: 11,
3304
+ marginRight: 4,
3305
+ background: "var(--dsh-color-hover-bg, #f3f4f6)"
3895
3306
  },
3896
- disabled: { opacity: 0.4, cursor: "default" },
3897
- failed: { flex: 1, minWidth: 0, margin: 0, fontSize: 12, lineHeight: 1.5, color: "#dc2626" },
3898
- note: { fontSize: 11.5, color: "var(--dsw-alias-label-tertiary, #9ca3af)", marginTop: 8 },
3899
- advHead: {
3307
+ panelTitle: { fontSize: 14, fontWeight: 700, margin: "18px 0 6px" },
3308
+ groupLabel: { fontSize: 12.5, fontWeight: 600, color: "var(--dsh-color-muted, #4b5563)", margin: "8px 0 4px" },
3309
+ mono: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11.5, color: "var(--dsh-color-muted, #4b5563)" },
3310
+ empty: { color: "var(--dsh-color-muted, #9ca3af)", fontSize: 12.5, padding: "14px 4px" },
3311
+ error: { color: "#dc2626", fontSize: 12.5, marginBottom: 10 },
3312
+ subPanel: {
3313
+ border: "1px solid var(--dsh-color-border, #e5e7eb)",
3314
+ borderRadius: 12,
3315
+ padding: "10px 12px",
3316
+ marginBottom: 10,
3317
+ background: "var(--dsh-color-surface, #fff)",
3318
+ boxShadow: "0 1px 2px rgba(15,23,42,.03)"
3319
+ },
3320
+ relRow: {
3900
3321
  display: "flex",
3901
3322
  alignItems: "center",
3902
- justifyContent: "space-between",
3903
- gap: 8,
3904
- width: "100%",
3905
- appearance: "none",
3906
- background: "none",
3907
- border: "1px solid var(--dsw-alias-border-l2, #e5e7eb)",
3323
+ gap: 6,
3324
+ flexWrap: "wrap",
3325
+ padding: "5px 8px",
3908
3326
  borderRadius: 10,
3909
- padding: "8px 12px",
3910
- marginTop: 10,
3911
- font: "inherit",
3912
- cursor: "pointer",
3913
- color: "var(--dsw-alias-label-secondary, #4b5563)",
3914
- textAlign: "left"
3327
+ border: "1px solid var(--dsh-color-border, #f3f4f6)",
3328
+ background: "var(--dsh-color-hover-bg, #f8fafc)"
3915
3329
  },
3916
- advHeadLabel: { fontSize: 12.5, fontWeight: 600, letterSpacing: 0.2, color: "var(--dsw-alias-label-secondary, #4b5563)" }
3330
+ nodePill: {
3331
+ display: "inline-flex",
3332
+ alignItems: "center",
3333
+ maxWidth: 220,
3334
+ overflow: "hidden",
3335
+ textOverflow: "ellipsis",
3336
+ whiteSpace: "nowrap",
3337
+ borderRadius: 8,
3338
+ padding: "2px 9px",
3339
+ fontSize: 12,
3340
+ fontWeight: 600,
3341
+ background: "rgba(99,102,241,.12)",
3342
+ color: "#4338ca"
3343
+ }
3917
3344
  };
3918
- function Chevron({ open }) {
3919
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3920
- "svg",
3921
- {
3922
- width: "14",
3923
- height: "14",
3924
- viewBox: "0 0 14 14",
3925
- "aria-hidden": "true",
3926
- style: {
3927
- flex: "none",
3928
- color: "var(--dsw-alias-label-tertiary, #6b7280)",
3929
- transition: "transform .16s",
3930
- transform: open ? "rotate(180deg)" : void 0
3931
- },
3932
- children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3933
- "path",
3934
- {
3935
- d: "M3 5.5 L7 9 L11 5.5",
3936
- fill: "none",
3937
- stroke: "currentColor",
3938
- strokeWidth: "1.6",
3939
- strokeLinecap: "round",
3940
- strokeLinejoin: "round"
3941
- }
3942
- )
3943
- }
3345
+ function fmtDate2(ts) {
3346
+ const d = new Date(ts);
3347
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
3348
+ const dd = String(d.getDate()).padStart(2, "0");
3349
+ return `${mm}-${dd} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
3350
+ }
3351
+ var MEM_PAGE_SIZE = 20;
3352
+ function daysLeft2(ts) {
3353
+ if (ts === null || ts === void 0) return "\u221E";
3354
+ const days = Math.ceil((ts - Date.now()) / 864e5);
3355
+ return days > 0 ? `${days}d` : "expired";
3356
+ }
3357
+ function StatCard({ num, label }) {
3358
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.card, children: [
3359
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.cardNum, children: num }),
3360
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.cardLabel, children: label })
3361
+ ] });
3362
+ }
3363
+ function groupByWorkspace(items) {
3364
+ const groups = /* @__PURE__ */ new Map();
3365
+ for (const m of items) {
3366
+ const list = groups.get(m.workspace) ?? [];
3367
+ list.push(m);
3368
+ groups.set(m.workspace, list);
3369
+ }
3370
+ return [...groups.entries()].sort(
3371
+ (a, b) => b[1].length - a[1].length || a[0].localeCompare(b[0])
3944
3372
  );
3945
3373
  }
3946
- function EngramConfigCard({ scope }) {
3947
- const [snap, setSnap] = (0, import_react8.useState)(null);
3948
- const [draft, setDraft] = (0, import_react8.useState)({});
3949
- const [saving, setSaving] = (0, import_react8.useState)(false);
3374
+ function EngramSection({ api, t, scope }) {
3375
+ const [overview, setOverview] = (0, import_react8.useState)(null);
3376
+ const [memories, setMemories] = (0, import_react8.useState)([]);
3377
+ const [tasks, setTasks] = (0, import_react8.useState)([]);
3378
+ const [links, setLinks] = (0, import_react8.useState)([]);
3379
+ const [nodes, setNodes] = (0, import_react8.useState)([]);
3380
+ const [detail, setDetail] = (0, import_react8.useState)(null);
3381
+ const [detailNotice, setDetailNotice] = (0, import_react8.useState)(null);
3950
3382
  const [error, setError] = (0, import_react8.useState)(null);
3951
- const [open, setOpen] = (0, import_react8.useState)(false);
3952
- const [advancedOpen, setAdvancedOpen] = (0, import_react8.useState)(false);
3383
+ const [workspace, setWorkspace] = (0, import_react8.useState)("");
3384
+ const [kind, setKind] = (0, import_react8.useState)("");
3385
+ const [status, setStatus] = (0, import_react8.useState)("active");
3386
+ const [q, setQ] = (0, import_react8.useState)("");
3387
+ const [busy, setBusy] = (0, import_react8.useState)(false);
3388
+ const [memPage, setMemPage] = (0, import_react8.useState)(0);
3389
+ const [expandedRows, setExpandedRows] = (0, import_react8.useState)(/* @__PURE__ */ new Set());
3390
+ const [view, setView] = (0, import_react8.useState)("mem");
3391
+ const [esrGraph, setEsrGraph] = (0, import_react8.useState)(false);
3392
+ const [gcDryRun, setGcDryRun] = (0, import_react8.useState)(true);
3393
+ const [gcReport, setGcReport] = (0, import_react8.useState)(null);
3394
+ const [gcRunning, setGcRunning] = (0, import_react8.useState)(false);
3395
+ const [newTaskWs, setNewTaskWs] = (0, import_react8.useState)("");
3396
+ const [newTaskName, setNewTaskName] = (0, import_react8.useState)("");
3397
+ const [newTaskDesc, setNewTaskDesc] = (0, import_react8.useState)("");
3398
+ const [newTaskBusy, setNewTaskBusy] = (0, import_react8.useState)(false);
3399
+ const [closeFor, setCloseFor] = (0, import_react8.useState)(null);
3400
+ const [closeArtifact, setCloseArtifact] = (0, import_react8.useState)("");
3401
+ const [closeEval, setCloseEval] = (0, import_react8.useState)("");
3402
+ const [closeRefs, setCloseRefs] = (0, import_react8.useState)("");
3403
+ const [closeBusy, setCloseBusy] = (0, import_react8.useState)(false);
3404
+ const [importDump, setImportDump] = (0, import_react8.useState)(null);
3405
+ const [importName, setImportName] = (0, import_react8.useState)("");
3406
+ const [importMode, setImportMode] = (0, import_react8.useState)("merge");
3407
+ const [importDryRun, setImportDryRun] = (0, import_react8.useState)(true);
3408
+ const [importReplaceOk, setImportReplaceOk] = (0, import_react8.useState)(false);
3409
+ const [importBusy, setImportBusy] = (0, import_react8.useState)(false);
3410
+ const [importReport, setImportReport] = (0, import_react8.useState)(null);
3411
+ const [dataError, setDataError] = (0, import_react8.useState)(null);
3412
+ const refresh = (0, import_react8.useCallback)(async () => {
3413
+ setBusy(true);
3414
+ setError(null);
3415
+ try {
3416
+ const ov = await api.overview();
3417
+ setOverview(ov);
3418
+ const wsList = workspace ? [workspace] : Object.keys(ov.workspaces);
3419
+ const [mem, taskGroups2, linkGroups2, nodeGroups2] = await Promise.all([
3420
+ api.memories({
3421
+ workspace: workspace || void 0,
3422
+ q: q || void 0,
3423
+ kind: kind || void 0,
3424
+ status: status === "all" ? void 0 : status
3425
+ }),
3426
+ Promise.all(wsList.map((w) => api.tasks(w, true))),
3427
+ Promise.all(wsList.map((w) => api.links(w))),
3428
+ Promise.all(wsList.map((w) => api.nodes(w)))
3429
+ ]);
3430
+ setMemories(mem.items);
3431
+ setTasks(taskGroups2.flatMap((x) => x.items));
3432
+ setLinks(linkGroups2.flatMap((x) => x.items));
3433
+ setNodes(nodeGroups2.flatMap((x) => x.items));
3434
+ } catch (e) {
3435
+ setError(e instanceof Error ? e.message : String(e));
3436
+ } finally {
3437
+ setBusy(false);
3438
+ }
3439
+ }, [api, workspace, q, kind, status]);
3440
+ (0, import_react8.useEffect)(() => {
3441
+ void refresh();
3442
+ const id = setInterval(() => void refresh(), POLL_MS);
3443
+ return () => clearInterval(id);
3444
+ }, [refresh]);
3445
+ const workspaces = (0, import_react8.useMemo)(() => overview ? Object.keys(overview.workspaces) : [], [overview]);
3446
+ const kindsPresent = (0, import_react8.useMemo)(() => overview ? Object.keys(overview.kinds) : [], [overview]);
3447
+ const previewDefault = (0, import_react8.useMemo)(() => {
3448
+ if (workspace !== "") return workspace;
3449
+ if (!overview) return "";
3450
+ let best = "";
3451
+ let bestN = -1;
3452
+ for (const ws of Object.keys(overview.workspaces)) {
3453
+ const c = overview.workspaces[ws] ?? {};
3454
+ const n = (c.memories ?? 0) + (c.tasks ?? 0) + (c.links ?? 0) + (c.nodes ?? 0);
3455
+ if (n > bestN) {
3456
+ bestN = n;
3457
+ best = ws;
3458
+ }
3459
+ }
3460
+ return best;
3461
+ }, [workspace, overview]);
3462
+ const nameOf = (0, import_react8.useMemo)(() => {
3463
+ const map = /* @__PURE__ */ new Map();
3464
+ for (const n of nodes) map.set(n.id, n.name);
3465
+ for (const t2 of tasks) map.set(t2.id, t2.name);
3466
+ return (id) => map.get(id) ?? id;
3467
+ }, [nodes, tasks]);
3468
+ const supersedeIndex = (0, import_react8.useMemo)(() => {
3469
+ const replacedBy = /* @__PURE__ */ new Map();
3470
+ const contradictedBy = /* @__PURE__ */ new Map();
3471
+ for (const m of memories) {
3472
+ if (m.status !== "active") continue;
3473
+ if (m.supersedes) {
3474
+ const list = replacedBy.get(m.supersedes) ?? [];
3475
+ list.push(m);
3476
+ replacedBy.set(m.supersedes, list);
3477
+ }
3478
+ if (m.contradicts) {
3479
+ const list = contradictedBy.get(m.contradicts) ?? [];
3480
+ list.push(m);
3481
+ contradictedBy.set(m.contradicts, list);
3482
+ }
3483
+ }
3484
+ return { replacedBy, contradictedBy };
3485
+ }, [memories]);
3486
+ (0, import_react8.useEffect)(() => {
3487
+ if (!newTaskWs && workspaces.length > 0 && !workspace) setNewTaskWs(workspaces[0]);
3488
+ }, [newTaskWs, workspaces, workspace]);
3489
+ const cfg = overview?.config ?? null;
3490
+ const act = async (fn) => {
3491
+ try {
3492
+ await fn();
3493
+ await refresh();
3494
+ } catch (e) {
3495
+ setError(e instanceof Error ? e.message : String(e));
3496
+ }
3497
+ };
3498
+ const toggleExpand = (id) => {
3499
+ setExpandedRows((prev) => {
3500
+ const next = new Set(prev);
3501
+ if (next.has(id)) next.delete(id);
3502
+ else next.add(id);
3503
+ return next;
3504
+ });
3505
+ };
3506
+ const runGc = async () => {
3507
+ setGcRunning(true);
3508
+ setError(null);
3509
+ try {
3510
+ const { report } = await api.gc(workspace || void 0, gcDryRun);
3511
+ setGcReport(report);
3512
+ if (!gcDryRun) await refresh();
3513
+ } catch (e) {
3514
+ setError(e instanceof Error ? e.message : String(e));
3515
+ } finally {
3516
+ setGcRunning(false);
3517
+ }
3518
+ };
3519
+ const downloadExport = async () => {
3520
+ setDataError(null);
3521
+ try {
3522
+ const dump = await api.exportData(workspace || void 0);
3523
+ const blob = new Blob([JSON.stringify(dump, null, 2)], { type: "application/json" });
3524
+ const url = URL.createObjectURL(blob);
3525
+ const a = document.createElement("a");
3526
+ const safe = (workspace || "all").replace(/[^a-zA-Z0-9._-]+/g, "_");
3527
+ a.href = url;
3528
+ a.download = `dsh-engram-export-${safe}-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.json`;
3529
+ a.click();
3530
+ URL.revokeObjectURL(url);
3531
+ } catch (e) {
3532
+ setDataError(e instanceof Error ? e.message : String(e));
3533
+ }
3534
+ };
3535
+ const pickImportFile = async (file) => {
3536
+ setDataError(null);
3537
+ setImportReport(null);
3538
+ setImportDump(null);
3539
+ setImportName("");
3540
+ if (!file) return;
3541
+ try {
3542
+ const text = await file.text();
3543
+ setImportDump(JSON.parse(text));
3544
+ setImportName(file.name);
3545
+ } catch (e) {
3546
+ setDataError(`\u65E0\u6CD5\u89E3\u6790 ${file.name}\uFF1A${e instanceof Error ? e.message : String(e)}`);
3547
+ }
3548
+ };
3549
+ const runImport = async () => {
3550
+ if (importDump === null) return;
3551
+ if (importMode === "replace" && !importReplaceOk) {
3552
+ setDataError("\u66FF\u6362\u6A21\u5F0F\u4F1A\u5148\u6E05\u7A7A\u76EE\u6807\u5DE5\u4F5C\u533A \u2014 \u8BF7\u5148\u52FE\u9009\u786E\u8BA4");
3553
+ return;
3554
+ }
3555
+ setImportBusy(true);
3556
+ setDataError(null);
3557
+ try {
3558
+ const report = await api.importData({
3559
+ payload: importDump,
3560
+ mode: importMode,
3561
+ dryRun: importDryRun,
3562
+ workspace: workspace || void 0,
3563
+ confirm: importMode === "replace" ? "restore" : void 0
3564
+ });
3565
+ setImportReport(report);
3566
+ if (!report.dryRun) await refresh();
3567
+ } catch (e) {
3568
+ setDataError(e instanceof Error ? e.message : String(e));
3569
+ } finally {
3570
+ setImportBusy(false);
3571
+ }
3572
+ };
3573
+ const groupedRows = workspace === "" ? groupByWorkspace(memories) : [[workspace, memories]];
3574
+ const taskGroups = workspace === "" ? groupByWorkspace(tasks) : [[workspace, tasks]];
3575
+ const linkGroups = workspace === "" ? groupByWorkspace(links) : [[workspace, links]];
3576
+ const nodeGroups = workspace === "" ? groupByWorkspace(nodes) : [[workspace, nodes]];
3577
+ const flatRows = [];
3578
+ for (const [ws, items] of groupedRows) {
3579
+ if (workspace === "") flatRows.push({ kind: "head", ws, count: items.length });
3580
+ for (const m of items) flatRows.push({ kind: "row", m });
3581
+ }
3582
+ const memPageCount = Math.max(1, Math.ceil(flatRows.length / MEM_PAGE_SIZE));
3583
+ const memPageSafe = Math.min(memPage, memPageCount - 1);
3584
+ const memPageRows = flatRows.slice(memPageSafe * MEM_PAGE_SIZE, (memPageSafe + 1) * MEM_PAGE_SIZE);
3953
3585
  (0, import_react8.useEffect)(() => {
3954
- void scope.load();
3955
- const off = scope.subscribe(() => {
3956
- const next = scope.getSnapshot();
3957
- setSnap(next);
3958
- if (next.status === "unavailable") {
3959
- console.warn("[dsh-engram] settings scope unavailable \u2014 Plugins \u914D\u7F6E\u5361\u5DF2\u9690\u85CF:", next.reason ?? "no reason served");
3960
- }
3961
- const value2 = next.value ?? next.base;
3962
- if (value2) setDraft((prev) => ({ ...value2, ...prev }));
3963
- });
3964
- setSnap(scope.getSnapshot());
3965
- return off;
3966
- }, [scope]);
3967
- const setField = (0, import_react8.useCallback)((key, value2) => {
3968
- setDraft((prev) => ({ ...prev, [key]: value2 }));
3969
- }, []);
3970
- const effective = snap?.value ?? snap?.base ?? {};
3971
- const anyDirty = FIELDS.some((field) => {
3972
- const staged = draft[field.key];
3973
- return staged !== void 0 && staged !== effective[field.key];
3974
- });
3975
- const save = (0, import_react8.useCallback)(async () => {
3976
- setSaving(true);
3586
+ setMemPage(0);
3587
+ }, [workspace, status, kind, q]);
3588
+ (0, import_react8.useEffect)(() => {
3589
+ if (memPage >= memPageCount) setMemPage(Math.max(0, memPageCount - 1));
3590
+ }, [memPage, memPageCount]);
3591
+ const createNewTask = async () => {
3592
+ const ws = newTaskWs || workspace || workspaces[0] || "";
3593
+ if (!ws || newTaskName.trim() === "") return;
3594
+ setNewTaskBusy(true);
3977
3595
  setError(null);
3978
3596
  try {
3979
- for (const field of FIELDS) {
3980
- const value2 = draft[field.key];
3981
- if (value2 === void 0) continue;
3982
- if (value2 === effective[field.key]) continue;
3983
- if (typeof value2 === "number" && Number.isNaN(value2)) continue;
3984
- await scope.set(field.key, value2);
3985
- }
3986
- await scope.load();
3597
+ await api.createTask(ws, newTaskName.trim(), newTaskDesc);
3598
+ setNewTaskName("");
3599
+ setNewTaskDesc("");
3600
+ await refresh();
3987
3601
  } catch (e) {
3988
- setError(String(e instanceof Error ? e.message : e));
3602
+ setError(e instanceof Error ? e.message : String(e));
3989
3603
  } finally {
3990
- setSaving(false);
3604
+ setNewTaskBusy(false);
3991
3605
  }
3992
- }, [draft, scope, effective]);
3993
- const discard = (0, import_react8.useCallback)(() => {
3994
- setError(null);
3995
- const value2 = snap?.value ?? snap?.base ?? {};
3996
- setDraft({ ...value2 });
3997
- }, [snap]);
3998
- const resetField = (0, import_react8.useCallback)(async (key) => {
3606
+ };
3607
+ const submitClose = async (task) => {
3608
+ setCloseBusy(true);
3999
3609
  setError(null);
4000
3610
  try {
4001
- await scope.unset(key);
4002
- await scope.load();
4003
- setDraft((prev) => ({ ...prev, [key]: void 0 }));
3611
+ const refs = closeRefs.split(/[,\s]+/).map((x) => x.trim()).filter(Boolean);
3612
+ const out = await api.closeTask(task.workspace, task.id, {
3613
+ artifact: closeArtifact,
3614
+ evaluation: closeEval,
3615
+ memoryRefs: refs
3616
+ });
3617
+ if (out.state === "active") {
3618
+ setError(`\u8BC1\u636E\u4ECD\u6709\u7F3A\u53E3\uFF1A${(out.gaps ?? []).join(", ")}${out.artifactReason ? `\uFF08${out.artifactReason}\uFF09` : ""} \u2014 \u4EFB\u52A1\u4FDD\u6301 ACTIVE\uFF0C\u8865\u9F50\u540E\u518D\u63D0\u4EA4`);
3619
+ }
3620
+ setCloseFor(null);
3621
+ setCloseArtifact("");
3622
+ setCloseEval("");
3623
+ setCloseRefs("");
3624
+ await refresh();
4004
3625
  } catch (e) {
4005
- setError(String(e instanceof Error ? e.message : e));
3626
+ setError(e instanceof Error ? e.message : String(e));
3627
+ } finally {
3628
+ setCloseBusy(false);
4006
3629
  }
4007
- }, [scope]);
4008
- const value = { ...snap?.base ?? {}, ...draft };
4009
- const writable = snap?.writable !== false && snap?.status !== "unavailable";
4010
- const available = snap?.status === "ready";
3630
+ };
3631
+ const openMemory = (id) => {
3632
+ const m = memories.find((mem) => mem.id === id);
3633
+ if (m) {
3634
+ setDetail({ kind: "memory", memory: m });
3635
+ setDetailNotice(null);
3636
+ } else {
3637
+ setDetailNotice(`\u8BB0\u5FC6 ${id} \u4E0D\u5728\u5F53\u524D\u52A0\u8F7D\u5217\u8868 \u2014 \u5207\u6362\u5230\u300C\u8BB0\u5FC6\u300D\u9875\u52A0\u8F7D\u540E\u53EF\u7528`);
3638
+ }
3639
+ };
3640
+ const openTask = (id) => {
3641
+ setDetail({ kind: "task", id });
3642
+ setDetailNotice(null);
3643
+ };
3644
+ const openNode = (id) => {
3645
+ setDetail({ kind: "node", id });
3646
+ setDetailNotice(null);
3647
+ };
3648
+ const indexCost = workspace && overview ? overview.indexes[workspace] : null;
3649
+ const gc = overview?.gc ?? null;
3650
+ const wsCounts = workspace && overview ? overview.workspaces[workspace] ?? null : null;
4011
3651
  const { vars } = useEngramTheme();
4012
- if (!available) return null;
4013
- const renderField = (field) => {
4014
- const raw = value[field.key];
4015
- const overridden = snap?.user !== void 0 && field.key in snap.user;
4016
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.row, children: [
4017
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
4018
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.label, children: field.label }),
4019
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.hint, children: field.hint })
4020
- ] }),
4021
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4022
- field.kind === "bool" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4023
- "input",
4024
- {
4025
- type: "checkbox",
4026
- checked: raw === true,
4027
- disabled: !writable,
4028
- onChange: (e) => setField(field.key, e.target.checked)
4029
- }
4030
- ) : field.kind === "select" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4031
- "select",
4032
- {
4033
- style: { ...s3.input, width: field.width ?? 160 },
4034
- value: raw === void 0 ? "" : String(raw),
4035
- disabled: !writable,
4036
- onChange: (e) => setField(field.key, e.target.value),
4037
- children: (field.options ?? []).map((opt) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: opt, children: opt }, opt))
4038
- }
4039
- ) : field.kind === "text" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4040
- "input",
4041
- {
4042
- type: "text",
4043
- style: { ...s3.input, width: field.width ?? 180 },
4044
- value: Array.isArray(raw) ? raw.join(", ") : raw === void 0 ? "" : String(raw),
4045
- disabled: !writable,
4046
- placeholder: "host.domain, \u53E6\u4E00\u57DF\u540D\u2026",
4047
- onChange: (e) => {
4048
- const tokens = e.target.value.split(/[,\s]+/).map((x) => x.trim()).filter(Boolean);
4049
- setField(field.key, tokens);
4050
- }
4051
- }
4052
- ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4053
- "input",
4054
- {
4055
- type: "number",
4056
- style: s3.input,
4057
- step: field.step ?? 1,
4058
- min: field.min,
4059
- max: field.max,
4060
- value: raw === void 0 ? "" : String(raw),
4061
- disabled: !writable,
4062
- onChange: (e) => setField(field.key, e.target.value === "" ? void 0 : Number(e.target.value))
4063
- }
4064
- ),
4065
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4066
- "button",
4067
- {
4068
- type: "button",
4069
- style: overridden ? { ...s3.reset, color: "var(--dsw-alias-brand-primary, #2563eb)" } : { ...s3.reset, cursor: "default", opacity: 0.55 },
4070
- title: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4",
4071
- disabled: !writable || !overridden,
4072
- onClick: () => void resetField(field.key),
4073
- children: "\u91CD\u7F6E"
4074
- }
4075
- )
3652
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...s3.root, ...vars }, children: [
3653
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h1", { style: s3.h1, children: "Engram \u8BB0\u5FC6" }),
3654
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: s3.sub, children: "\u8DE8\u4F1A\u8BDD\u8BB0\u5FC6 \xB7 \u96F6 LLM \u81EA\u52A8\u6355\u83B7 \xB7 \u7B26\u53F7\u7D22\u5F15\u6E10\u8FDB\u62AB\u9732 \u2014 \u6570\u636E\u6E90 ~/.dsh/storages/dsh_engram.json" }),
3655
+ error && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.error, children: [
3656
+ t("error"),
3657
+ ": ",
3658
+ error
3659
+ ] }),
3660
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.tabBar, children: [
3661
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: view === "mem" ? s3.tabActive : s3.tab, onClick: () => setView("mem"), children: "\u8BB0\u5FC6" }),
3662
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: view === "esr" ? s3.tabActive : s3.tab, onClick: () => setView("esr"), children: "ESR\uFF08\u4EFB\u52A1 \xB7 \u8282\u70B9 \xB7 \u5173\u7CFB\uFF09" }),
3663
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: view === "preview" ? s3.tabActive : s3.tab, onClick: () => setView("preview"), children: "\u6CE8\u5165\u9884\u89C8" }),
3664
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: view === "telemetry" ? s3.tabActive : s3.tab, onClick: () => setView("telemetry"), children: "\u9065\u6D4B" }),
3665
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: view === "config" ? s3.tabActive : s3.tab, onClick: () => setView("config"), children: "\u8BBE\u7F6E" })
3666
+ ] }),
3667
+ view !== "config" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...s3.row, marginTop: 2 }, children: [
3668
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u5DE5\u4F5C\u533A\uFF1A" }),
3669
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { style: s3.input, value: workspace, onChange: (e) => setWorkspace(e.target.value), children: [
3670
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "\u5168\u90E8\u5DE5\u4F5C\u533A" }),
3671
+ workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("option", { value: ws, children: [
3672
+ ws,
3673
+ "\uFF08",
3674
+ overview?.workspaces[ws]?.memories ?? 0,
3675
+ " \u6761\uFF09"
3676
+ ] }, ws))
4076
3677
  ] })
4077
- ] }, field.key);
4078
- };
4079
- const renderGroup = (group) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupPanel, children: [
4080
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupHead, children: [
4081
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.groupTitle, children: group.title }),
4082
- group.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.groupDesc, children: group.description })
4083
3678
  ] }),
4084
- group.fields.map(renderField)
4085
- ] }, group.id);
4086
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: vars, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...s3.card, ...open ? s3.cardOpen : void 0 }, children: [
4087
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4088
- "button",
4089
- {
4090
- type: "button",
4091
- style: s3.header,
4092
- "aria-expanded": open,
4093
- "aria-label": `${open ? "\u6536\u8D77" : "\u5C55\u5F00"} dsh-engram \u8BBE\u7F6E`,
4094
- onClick: () => setOpen((prev) => !prev),
4095
- children: [
4096
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.headText, children: [
4097
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.name, children: "dsh-engram" }),
4098
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.description, children: CARD_DESCRIPTION })
3679
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: 12, alignItems: "flex-start" }, children: [
3680
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: "1 1 auto", minWidth: 0 }, children: [
3681
+ view === "mem" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3682
+ overview && (() => {
3683
+ const memNum = wsCounts ? wsCounts.memories : overview.totals.memories;
3684
+ const taskNum = wsCounts ? wsCounts.tasks : overview.totals.tasks;
3685
+ const linkNum = wsCounts ? wsCounts.links : overview.totals.links;
3686
+ const ledgerBucket = overview.ledger ? workspace && overview.ledger.byWorkspace[workspace] || overview.ledger.totals : null;
3687
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.stats, children: [
3688
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(memNum), label: wsCounts ? "\u8BB0\u5FC6 (active)" : "\u8BB0\u5FC6 (active, \u5168\u5C40)" }),
3689
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(taskNum), label: wsCounts ? "\u4EFB\u52A1 (active)" : "\u4EFB\u52A1 (active, \u5168\u5C40)" }),
3690
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(linkNum), label: wsCounts ? "\u5173\u7CFB" : "\u5173\u7CFB (\u5168\u5C40)" }),
3691
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(wsCounts ? wsCounts.nodes ?? 0 : overview.totals.nodes ?? 0), label: wsCounts ? "\u8282\u70B9" : "\u8282\u70B9 (\u5168\u5C40)" }),
3692
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(workspaces.length), label: "\u5DE5\u4F5C\u533A" }),
3693
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(overview.captures.total), label: "\u81EA\u52A8\u6355\u83B7" }),
3694
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatCard, { num: String(overview.captures.preference ?? 0), label: "\u504F\u597D\u6355\u83B7" }),
3695
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3696
+ StatCard,
3697
+ {
3698
+ num: indexCost ? `~${indexCost.tokens}` : "\u2013",
3699
+ label: "[ENGRAM] \u7D22\u5F15 token / \u5DE5\u4F5C\u533A"
3700
+ }
3701
+ ),
3702
+ gc && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3703
+ StatCard,
3704
+ {
3705
+ num: String(gc.archivedMemories + gc.archivedTasks),
3706
+ label: `GC \u5DF2\u5F52\u6863 \xB7 \u94FE\u63A5-${gc.removedLinks}`
3707
+ }
3708
+ ),
3709
+ ledgerBucket && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3710
+ StatCard,
3711
+ {
3712
+ num: signedTokens(ledgerBucket.netTokens),
3713
+ label: `\u4E0A\u4E0B\u6587\u51C0 token \xB7 \u8BF7\u6C42 ${ledgerBucket.requests}`
3714
+ }
3715
+ )
3716
+ ] });
3717
+ })(),
3718
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.subPanel, children: [
3719
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
3720
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u8BB0\u5FC6 GC\uFF08pi-esr \u7EA6\u675F\uFF09" }),
3721
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { fontSize: 12, display: "inline-flex", gap: 4, alignItems: "center" }, children: [
3722
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: gcDryRun, onChange: (e) => setGcDryRun(e.target.checked) }),
3723
+ "\u4EC5\u9884\u89C8\uFF08dry run\uFF09"
3724
+ ] }),
3725
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btnPrimary, onClick: () => void runGc(), disabled: gcRunning || !workspace, children: gcRunning ? "\u2026" : "\u8FD0\u884C GC" }),
3726
+ gc && gc.lastRun > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.mono, children: [
3727
+ "\u4E0A\u6B21 ",
3728
+ fmtDate2(gc.lastRun)
3729
+ ] })
3730
+ ] }),
3731
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)", marginTop: 4 }, children: "\u5DE5\u4F5C\u96C6\uFF08active \u4EFB\u52A1\u5F15\u7528 / \u4EFB\u52A1\u8BB0\u5FC6 / \u5DF2\u5165\u7D22\u5F15\u547D\u4E2D\uFF09\u6C38\u4E0D\u9A71\u9010\uFF1BTTL \u8FC7\u671F\u5F52\u6863\u3001\u8D85\u5BB9\u91CF\u6DD8\u6C70\u3001stable \u4EFB\u52A1\u8D85\u7A97\u5F52\u6863\u3001\u60AC\u7A7A\u94FE\u63A5\u6E05\u7406\u3002\u53EA\u5F52\u6863\u4E0D\u786C\u5220\u2014\u2014\u6761\u76EE id \u4FDD\u6301\u53EF\u91CD\u53D6\u3002" }),
3732
+ gcReport && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 8, fontSize: 12.5 }, children: [
3733
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
3734
+ gcReport.dryRun ? "dry-run \u9884\u89C8\uFF1A" : "\u5DF2\u6267\u884C\uFF1A",
3735
+ " ",
3736
+ "\u5F52\u6863\u8BB0\u5FC6 ",
3737
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", { children: gcReport.archivedMemories.length }),
3738
+ " \xB7 \u5F52\u6863\u4EFB\u52A1 ",
3739
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", { children: gcReport.archivedTasks.length }),
3740
+ " \xB7 \u6E05\u7406\u94FE\u63A5 ",
3741
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", { children: gcReport.removedLinks.length }),
3742
+ " \xB7 \u4FDD\u62A4 ",
3743
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", { children: gcReport.protectedMemories })
3744
+ ] }),
3745
+ gcReport.archivedMemories.slice(0, 5).map((e) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.mono, children: [
3746
+ "- ",
3747
+ e.id.slice(0, 8),
3748
+ " ",
3749
+ e.reason,
3750
+ ": ",
3751
+ e.text
3752
+ ] }, e.id)),
3753
+ gcReport.archivedTasks.slice(0, 3).map((t2) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.mono, children: [
3754
+ "- ",
3755
+ t2.id.slice(0, 6),
3756
+ " ",
3757
+ t2.reason,
3758
+ ": ",
3759
+ t2.name
3760
+ ] }, t2.id)),
3761
+ gcReport.removedLinks.slice(0, 3).map((l) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.mono, children: [
3762
+ "- link ",
3763
+ (l.source ?? "?").slice(0, 8),
3764
+ " --",
3765
+ l.relation ?? "",
3766
+ "--> ",
3767
+ (l.target ?? "?").slice(0, 8)
3768
+ ] }, l.id))
3769
+ ] })
3770
+ ] }),
3771
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.subPanel, children: [
3772
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }, children: [
3773
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u6570\u636E\u5907\u4EFD\uFF08export / import\uFF09" }),
3774
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, title: "\u4E0B\u8F7D\u5F53\u524D\u5DE5\u4F5C\u533A\uFF08\u6216\u5168\u90E8\uFF09\u7684 dsh-engram/export v1 \u8F6C\u50A8", onClick: () => void downloadExport(), children: "\u5BFC\u51FA JSON" }),
3775
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { ...s3.btn, display: "inline-flex", alignItems: "center", gap: 6, cursor: "pointer" }, children: [
3776
+ "\u9009\u62E9\u5BFC\u5165\u6587\u4EF6\u2026",
3777
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3778
+ "input",
3779
+ {
3780
+ type: "file",
3781
+ accept: "application/json,.json",
3782
+ style: { display: "none" },
3783
+ onChange: (e) => {
3784
+ void pickImportFile(e.target.files?.[0] ?? null);
3785
+ e.currentTarget.value = "";
3786
+ }
3787
+ }
3788
+ )
3789
+ ] }),
3790
+ importName && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.mono, children: importName })
3791
+ ] }),
3792
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)", marginTop: 4 }, children: [
3793
+ "\u5BFC\u51FA\u5373\u53EF\u79FB\u690D\u5907\u4EFD\uFF08\u8BB0\u5FC6/\u4EFB\u52A1/\u5173\u7CFB/\u8282\u70B9\uFF0C\u542B\u5F52\u6863\uFF09\uFF1B",
3794
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", { children: "merge" }),
3795
+ " \u53EA\u8865\u7F3A\u5931\uFF08\u5E42\u7B49\uFF09\uFF0C",
3796
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("b", { children: "replace" }),
3797
+ " \u5148\u6E05\u7A7A\u76EE\u6807\u5DE5\u4F5C\u533A\u518D\u6062\u590D\u3002\u8BB0\u5FC6\u6B63\u6587\u4E0E\u6240\u6709\u5199\u5165\u8DEF\u5F84\u4E00\u6837\u8FC7\u8131\u654F\u3002"
3798
+ ] }),
3799
+ importDump !== null && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 8, display: "flex", flexDirection: "column", gap: 6 }, children: [
3800
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", fontSize: 12.5 }, children: [
3801
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", gap: 4, alignItems: "center" }, children: [
3802
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: importDryRun, onChange: (e) => setImportDryRun(e.target.checked) }),
3803
+ "\u4EC5\u9884\u89C8\uFF08dry run\uFF09"
3804
+ ] }),
3805
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3806
+ "select",
3807
+ {
3808
+ style: s3.input,
3809
+ value: importMode,
3810
+ onChange: (e) => {
3811
+ const mode = e.target.value;
3812
+ setImportMode(mode);
3813
+ if (mode !== "replace") setImportReplaceOk(false);
3814
+ },
3815
+ children: [
3816
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "merge", children: "\u5408\u5E76\uFF08merge\uFF0C\u5E42\u7B49\uFF09" }),
3817
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "replace", children: "\u66FF\u6362\uFF08replace\uFF0C\u5148\u6E05\u7A7A\u5DE5\u4F5C\u533A\uFF09" })
3818
+ ]
3819
+ }
3820
+ ),
3821
+ importMode === "replace" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { style: { display: "inline-flex", gap: 4, alignItems: "center", color: "#b45309" }, children: [
3822
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { type: "checkbox", checked: importReplaceOk, onChange: (e) => setImportReplaceOk(e.target.checked) }),
3823
+ "\u6211\u786E\u8BA4\u66FF\u6362",
3824
+ workspace ? `\uFF08${workspace}\uFF09` : "\uFF08\u9700\u5148\u9009\u5DE5\u4F5C\u533A\uFF09"
3825
+ ] }),
3826
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3827
+ "button",
3828
+ {
3829
+ style: s3.btnPrimary,
3830
+ disabled: importBusy || importMode === "replace" && (!importReplaceOk || !workspace),
3831
+ onClick: () => void runImport(),
3832
+ children: importBusy ? "\u2026" : importDryRun ? "\u9884\u89C8\u5BFC\u5165" : "\u6267\u884C\u5BFC\u5165"
3833
+ }
3834
+ )
3835
+ ] }),
3836
+ importReport && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontSize: 12.5 }, children: [
3837
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
3838
+ importReport.dryRun ? "dry-run \u9884\u89C8\uFF1A" : "\u5DF2\u5BFC\u5165\uFF1A",
3839
+ " ",
3840
+ Object.entries(importReport.written).map(([k, v]) => `${k} ${v}`).join(" \xB7 ") || "\u2014",
3841
+ importReport.skipped.length > 0 ? ` \xB7 \u8DF3\u8FC7 ${importReport.skipped.length}` : ""
3842
+ ] }),
3843
+ importReport.skipped.slice(0, 5).map((x, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.mono, children: [
3844
+ "- ",
3845
+ x.table,
3846
+ " ",
3847
+ x.id ? x.id.slice(0, 8) : "?",
3848
+ ": ",
3849
+ x.reason
3850
+ ] }, `${x.table}-${x.id ?? i}`))
3851
+ ] }),
3852
+ dataError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 12, color: "#dc2626", fontWeight: 600 }, children: dataError })
3853
+ ] })
4099
3854
  ] }),
4100
- anyDirty && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.pending, children: "\u672A\u4FDD\u5B58" }),
4101
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Chevron, { open })
4102
- ]
4103
- }
4104
- ),
4105
- open && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.body, children: [
4106
- !writable && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { style: s3.readOnly, children: "\u672C\u90E8\u7F72\u7684\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF08\u5BBF\u4E3B\u672A\u6388\u6743\u5199\u5165\u6216\u9700\u91CD\u542F\u5E94\u7528\uFF09\u3002" }),
4107
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupPanel, children: [
4108
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupHead, children: [
4109
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.groupTitle, children: "\u5E38\u7528" }),
4110
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.groupDesc, children: "\u6BCF\u5929\u4F1A\u78B0\u5230\u7684\u65CB\u94AE \xB7 \u5176\u4F59\u6536\u5728\u4E0B\u65B9\u9AD8\u7EA7\u8BBE\u7F6E" })
3855
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.row, children: [
3856
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { style: s3.input, value: kind, onChange: (e) => setKind(e.target.value), children: [
3857
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "\u5168\u90E8\u7C7B\u578B" }),
3858
+ kindsPresent.map((k) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: k, children: KIND_LABEL2[k] ?? k }, k))
3859
+ ] }),
3860
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { style: s3.input, value: status, onChange: (e) => setStatus(e.target.value), children: [
3861
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "active", children: "\u4EC5\u6D3B\u52A8" }),
3862
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "all", children: "\u5168\u90E8\u72B6\u6001" }),
3863
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "archived", children: "\u5DF2\u5F52\u6863" })
3864
+ ] }),
3865
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3866
+ "input",
3867
+ {
3868
+ style: { ...s3.input, width: 180 },
3869
+ placeholder: "\u641C\u7D22\u8BB0\u5FC6\u2026",
3870
+ value: q,
3871
+ onChange: (e) => setQ(e.target.value),
3872
+ onKeyDown: (e) => {
3873
+ if (e.key === "Enter") void refresh();
3874
+ }
3875
+ }
3876
+ ),
3877
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btnPrimary, onClick: () => void refresh(), disabled: busy, children: busy ? "\u2026" : t("refresh") })
3878
+ ] }),
3879
+ cfg && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.row, children: [
3880
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.tag, children: [
3881
+ "autoCapture ",
3882
+ cfg.autoCapture ? "on" : "off"
3883
+ ] }),
3884
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.tag, children: [
3885
+ "sessionSearch ",
3886
+ cfg.sessionSearch ? "on" : "off"
3887
+ ] }),
3888
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.tag, children: [
3889
+ "TTL ",
3890
+ cfg.expireDays,
3891
+ "d"
3892
+ ] }),
3893
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.tag, children: [
3894
+ "index ",
3895
+ cfg.indexMaxLines,
3896
+ " \u884C / ",
3897
+ cfg.indexMaxChars,
3898
+ " \u5B57\u7B26"
3899
+ ] }),
3900
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.tag, children: [
3901
+ "promote \u2265",
3902
+ cfg.promoteHits,
3903
+ " hits"
3904
+ ] })
3905
+ ] }),
3906
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("table", { style: s3.table, children: [
3907
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("colgroup", { children: [
3908
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("col", { style: { width: 58 } }),
3909
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("col", {}),
3910
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("col", { style: { width: 72 } })
3911
+ ] }),
3912
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
3913
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: s3.th, children: "\u7C7B\u578B" }),
3914
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: s3.th, children: "\u5185\u5BB9" }),
3915
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("th", { style: s3.th })
3916
+ ] }) }),
3917
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tbody", { children: [
3918
+ flatRows.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { colSpan: 3, style: s3.empty, children: "\u6682\u65E0\u8BB0\u5FC6 \u2014 \u4F7F\u7528 engram_store \u663E\u5F0F\u8BB0\u5F55\uFF0C\u6216\u8BA9\u81EA\u52A8\u6355\u83B7\u5DE5\u4F5C\uFF08git \u63D0\u4EA4 / \u5173\u952E\u6587\u4EF6\u7F16\u8F91 / \u5DE5\u5177\u9519\u8BEF\uFF09" }) }),
3919
+ memPageRows.map(
3920
+ (r) => r.kind === "head" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { colSpan: 3, style: s3.wsHead, children: [
3921
+ r.ws,
3922
+ " \xB7 ",
3923
+ r.count,
3924
+ " \u6761"
3925
+ ] }) }, `h-${r.ws}`) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("tr", { children: [
3926
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { style: { ...s3.td, whiteSpace: "nowrap" }, children: [
3927
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...s3.badge, background: KIND_COLORS2[r.m.kind] ?? "#6b7280" }, children: KIND_LABEL2[r.m.kind] ?? r.m.kind }),
3928
+ r.m.status === "archived" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...s3.tag, color: "#b45309", background: "#fef3c7" }, children: "archived" }),
3929
+ supersedeIndex.replacedBy.has(r.m.id) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...s3.tag, color: "#b45309", background: "#fef3c7" }, title: `\u5DF2\u88AB ${(supersedeIndex.replacedBy.get(r.m.id) ?? []).map((x) => `#${x.id.slice(0, 8)}`).join(", ")} \u66FF\u4EE3\uFF08\u65E7\u884C\u4FDD\u7559\u3001\u53EC\u56DE\u964D\u7EA7\uFF09`, children: "\u5DF2\u66FF\u6362" })
3930
+ ] }),
3931
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("td", { style: { ...s3.td, minWidth: 0 }, children: [
3932
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { title: r.m.text, style: expandedRows.has(r.m.id) ? s3.expanded : s3.clamp3, children: r.m.text }),
3933
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", marginTop: 2, flexWrap: "wrap" }, children: [
3934
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...s3.mono, flex: "1 1 160px" }, title: `${fmtDate2(r.m.createdAt)} \xB7 ${r.m.id} \xB7 ${r.m.entity ?? ""} \xB7 signal ${r.m.signal.toFixed(2)} \xB7 hits ${r.m.hits} \xB7 TTL ${daysLeft2(r.m.expiresAt)}`, children: [
3935
+ fmtDate2(r.m.createdAt),
3936
+ " \xB7 ",
3937
+ r.m.id.slice(0, 8),
3938
+ r.m.entity ? ` \xB7 ${r.m.entity}` : "",
3939
+ " \xB7 ",
3940
+ r.m.signal.toFixed(2),
3941
+ " \xB7 hits ",
3942
+ r.m.hits,
3943
+ " \xB7 ",
3944
+ daysLeft2(r.m.expiresAt)
3945
+ ] }),
3946
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.linkBtn, onClick: () => toggleExpand(r.m.id), children: expandedRows.has(r.m.id) ? "\u6536\u8D77" : "\u5C55\u5F00\u5168\u6587" }),
3947
+ r.m.supersedes && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { style: s3.linkBtn, title: `\u6253\u5F00\u88AB\u66FF\u4EE3\u7684\u65E7\u8BB0\u5FC6 ${r.m.supersedes}`, onClick: () => openMemory(r.m.supersedes), children: [
3948
+ "\u66FF\u4EE3 #",
3949
+ r.m.supersedes.slice(0, 8)
3950
+ ] })
3951
+ ] }),
3952
+ r.m.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { marginTop: 3, display: "flex", flexWrap: "wrap", gap: 4 }, children: r.m.tags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.tag, children: tag }, tag)) })
3953
+ ] }),
3954
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("td", { style: s3.td, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-start" }, children: [
3955
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.linkBtn, title: "\u5728\u4FA7\u680F\u6253\u5F00\u8BE6\u60C5", onClick: () => {
3956
+ setDetail({ kind: "memory", memory: r.m });
3957
+ setDetailNotice(null);
3958
+ }, children: "\u8BE6\u60C5" }),
3959
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, title: "\u5F52\u6863\uFF08TTL/\u8F6F\u5220\uFF0C\u53EF\u6062\u590D\u4E0D\u8F7D\u5165\u7D22\u5F15\uFF09", onClick: () => void act(() => api.archive(r.m.id, r.m.workspace)), children: "\u5F52\u6863" }),
3960
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, title: "\u6C38\u4E45\u5220\u9664", onClick: () => {
3961
+ if (window.confirm(`\u5220\u9664\u8FD9\u6761\u8BB0\u5FC6?
3962
+ ${r.m.text.slice(0, 60)}`)) void act(() => api.remove(r.m.id, r.m.workspace));
3963
+ }, children: "\u5220\u9664" })
3964
+ ] }) })
3965
+ ] }, r.m.id)
3966
+ )
3967
+ ] })
3968
+ ] }),
3969
+ flatRows.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.pageBar, children: [
3970
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, disabled: memPageSafe === 0, onClick: () => setMemPage(memPageSafe - 1), children: "\u2039 \u4E0A\u4E00\u9875" }),
3971
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
3972
+ "\u7B2C ",
3973
+ memPageSafe + 1,
3974
+ " / ",
3975
+ memPageCount,
3976
+ " \u9875 \xB7 \u5171 ",
3977
+ flatRows.length,
3978
+ " \u6761"
3979
+ ] }),
3980
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, disabled: memPageSafe >= memPageCount - 1, onClick: () => setMemPage(memPageSafe + 1), children: "\u4E0B\u4E00\u9875 \u203A" })
3981
+ ] })
4111
3982
  ] }),
4112
- COMMON_FIELDS.map(renderField)
4113
- ] }),
4114
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
4115
- "button",
4116
- {
4117
- type: "button",
4118
- style: { ...s3.advHead, ...advancedOpen ? { borderColor: "var(--dsw-alias-label-dimmed, #9ca3af)" } : void 0 },
4119
- "aria-expanded": advancedOpen,
4120
- onClick: () => setAdvancedOpen((v) => !v),
4121
- children: [
4122
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: s3.advHeadLabel, children: [
4123
- "\u9AD8\u7EA7\u8BBE\u7F6E\uFF08",
4124
- ADVANCED_GROUPS.reduce((n, g) => n + g.fields.length, 0),
4125
- " \u9879\uFF09"
3983
+ view === "esr" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3984
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", marginBottom: 6 }, children: [
3985
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: 11.5, color: "var(--dsh-color-muted, #6b7280)" }, children: "\u5DE5\u5177\u8C03\u7528\u4E0E\u547D\u4E2D\u7EDF\u8BA1\u89C1\u300C\u9065\u6D4B\u300Dtab\uFF1B[ESR] \u6CE8\u5165\u5757\u7684 escalate \u63D0\u9192\u4F9D\u636E\u771F\u5B9E\u6570\u636E\u52A8\u6001\u51FA\u73B0\u3002" }),
3986
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { marginLeft: "auto", display: "inline-flex", gap: 6 }, children: [
3987
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", style: esrGraph ? s3.tab : s3.tabActive, onClick: () => setEsrGraph(false), children: "\u5217\u8868" }),
3988
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", style: esrGraph ? s3.tabActive : s3.tab, onClick: () => setEsrGraph(true), children: "\u56FE\u8C31" })
3989
+ ] })
3990
+ ] }),
3991
+ esrGraph ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.subPanel, children: [
3992
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 4 }, children: [
3993
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontWeight: 700, fontSize: 13 }, children: "\u5173\u7CFB\u56FE\u8C31\uFF08esr_link \u529B\u5BFC\u5411\u56FE\uFF09" }),
3994
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { fontSize: 11.5, fontWeight: 400, color: "var(--dsh-color-muted, #6b7280)" }, children: [
3995
+ workspace === "" ? "\u5168\u90E8\u5DE5\u4F5C\u533A" : `\u5DE5\u4F5C\u533A\uFF1A${workspace}`,
3996
+ " \xB7 \u5B9E\u4F53\u4E3A\u5706\u5F62\u8282\u70B9\uFF0C\u4EFB\u52A1\u4E3A\u52FE\u9009\u5FBD\u6807\uFF1B\u70B9\u9009\u8282\u70B9\u67E5\u770B\u5173\u7CFB\u660E\u7EC6"
3997
+ ] })
4126
3998
  ] }),
4127
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Chevron, { open: advancedOpen })
4128
- ]
4129
- }
4130
- ),
4131
- advancedOpen && ADVANCED_GROUPS.map(renderGroup),
4132
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.note, children: "\u8BBE\u7F6E\u5BF9\u65B0\u5EFA\u4F1A\u8BDD\u5373\u65F6\u751F\u6548\uFF1B\u5DF2\u51BB\u7ED3\u7684 [ENGRAM] \u5757\u4FDD\u6301\u524D\u7F00\u7A33\u5B9A\u3002" }),
4133
- error && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.failed, children: error }),
4134
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.footer, children: [
4135
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4136
- "button",
3999
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4000
+ EngramGraph,
4001
+ {
4002
+ workspace,
4003
+ entities: nodes,
4004
+ tasks,
4005
+ links,
4006
+ nameOf
4007
+ }
4008
+ )
4009
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
4010
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.panelTitle, children: [
4011
+ "ESR \u4EFB\u52A1\uFF08\u8BC1\u636E\u95ED\u73AF\uFF09",
4012
+ workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsh-color-muted, #9ca3af)" }, children: "\xB7 \u5168\u90E8\u5DE5\u4F5C\u533A" })
4013
+ ] }),
4014
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.subPanel, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
4015
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("select", { style: s3.input, value: newTaskWs, onChange: (e) => setNewTaskWs(e.target.value), children: [
4016
+ workspaces.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", children: "(no workspaces)" }),
4017
+ workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: ws, children: ws }, ws))
4018
+ ] }),
4019
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { style: { ...s3.input, width: 170 }, placeholder: "\u4EFB\u52A1\u540D\u2026", value: newTaskName, onChange: (e) => setNewTaskName(e.target.value), onKeyDown: (e) => {
4020
+ if (e.key === "Enter") void createNewTask();
4021
+ } }),
4022
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { style: { ...s3.input, width: 240 }, placeholder: "\u8981\u4EA7\u51FA / \u6EE1\u8DB3\u4EC0\u4E48\uFF08\u53EF\u9009\uFF09", value: newTaskDesc, onChange: (e) => setNewTaskDesc(e.target.value) }),
4023
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btnPrimary, disabled: newTaskBusy || newTaskName.trim() === "", onClick: () => void createNewTask(), children: newTaskBusy ? "\u2026" : "\u65B0\u5EFA\u4EFB\u52A1" })
4024
+ ] }) }),
4025
+ tasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.empty, children: "\u6682\u65E0\u4EFB\u52A1 \u2014 \u7528\u4E0A\u65B9\u300C\u65B0\u5EFA\u4EFB\u52A1\u300D\u6216 esr_task \u5DE5\u5177\u521B\u5EFA" }),
4026
+ taskGroups.map(([ws, items]) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react8.Fragment, { children: [
4027
+ workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupLabel, children: [
4028
+ ws,
4029
+ " \xB7 ",
4030
+ items.length,
4031
+ " \u4E2A\u4EFB\u52A1"
4032
+ ] }),
4033
+ items.map((task) => {
4034
+ const gaps = taskGaps(task);
4035
+ const isStable = task.state === "stable";
4036
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.subPanel, children: [
4037
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontWeight: 600, fontSize: 13 }, children: [
4038
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.mono, children: task.id.slice(0, 6) }),
4039
+ " ",
4040
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4041
+ EvidenceRing,
4042
+ {
4043
+ artifact: Boolean(task.artifact),
4044
+ evaluation: Boolean(task.evaluation),
4045
+ refs: (task.memoryRefs?.length ?? 0) > 0,
4046
+ size: 22
4047
+ }
4048
+ ),
4049
+ " ",
4050
+ task.name,
4051
+ " ",
4052
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...s3.badge, background: isStable ? "#059669" : gaps.length === 0 ? "#2563eb" : "#d97706" }, children: isStable ? "STABLE" : gaps.length === 0 ? "READY" : "ACTIVE" }),
4053
+ !isStable && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, onClick: () => setCloseFor(closeFor === task.id ? null : task.id), children: closeFor === task.id ? "\u6536\u8D77" : "\u586B\u5199\u8BC1\u636E\u5173\u95ED\u2026" }),
4054
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.linkBtn, title: "\u5728\u4FA7\u680F\u6253\u5F00\u8BE6\u60C5", onClick: () => openTask(task.id), children: "\u8BE6\u60C5" })
4055
+ ] }),
4056
+ !isStable && gaps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontSize: 12, color: "var(--dsh-color-muted, #6b7280)", marginTop: 4 }, children: [
4057
+ "\u7F3A\u53E3\uFF1A",
4058
+ gaps.join(", "),
4059
+ " \u2014 \u63D0\u4F9B artifact / evaluation / memory_ref \u540E\u8F6C\u4E3A STABLE"
4060
+ ] }),
4061
+ task.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-color-muted-strong, #4b5563)", marginTop: 4 }, children: task.description }),
4062
+ task.memoryRefs.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontSize: 12, marginTop: 4 }, children: [
4063
+ "\u8BB0\u5FC6\u5F15\u7528\uFF1A",
4064
+ task.memoryRefs.map((r) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: s3.tag, children: r.slice(0, 8) }, r))
4065
+ ] }),
4066
+ closeFor === task.id && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { marginTop: 8, padding: 8, border: "1px solid var(--dsh-color-border, #e5e7eb)", borderRadius: 8 }, children: [
4067
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 11, color: "var(--dsh-color-muted, #6b7280)", marginBottom: 6 }, children: "\u63D0\u4F9B\u8BC1\u636E\u540E\u5173\u95ED\uFF08\u4E09\u9879\u5168\u9F50\u624D\u8F6C STABLE\uFF09" }),
4068
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { display: "flex", gap: 6, flexWrap: "wrap", alignItems: "center" }, children: [
4069
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { style: { ...s3.input, width: 150 }, placeholder: "artifact \u8DEF\u5F84/URL", value: closeArtifact, onChange: (e) => setCloseArtifact(e.target.value) }),
4070
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { style: { ...s3.input, width: 150 }, placeholder: "evaluation \u9A8C\u8BC1\u8BC1\u636E", value: closeEval, onChange: (e) => setCloseEval(e.target.value) }),
4071
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("input", { style: { ...s3.input, width: 150 }, placeholder: "memory_refs \u9017\u53F7\u5206\u9694", value: closeRefs, onChange: (e) => setCloseRefs(e.target.value) }),
4072
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btnPrimary, disabled: closeBusy, onClick: () => void submitClose(task), children: closeBusy ? "\u2026" : "\u63D0\u4EA4\u5173\u95ED" }),
4073
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { style: s3.btn, onClick: () => setCloseFor(null), children: "\u53D6\u6D88" })
4074
+ ] })
4075
+ ] })
4076
+ ] }, task.id);
4077
+ })
4078
+ ] }, ws)),
4079
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.panelTitle, children: [
4080
+ "\u8282\u70B9\u4E0E\u5173\u7CFB\uFF08esr_node / esr_link\uFF09",
4081
+ workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontSize: 12, fontWeight: 400, color: "var(--dsh-color-muted, #9ca3af)" }, children: "\xB7 \u5168\u90E8\u5DE5\u4F5C\u533A" })
4082
+ ] }),
4083
+ nodes.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.empty, children: "\u6682\u65E0\u8282\u70B9 \u2014 \u6A21\u578B\u4F1A\u4E3A\u53CD\u590D\u51FA\u73B0\u7684\u9886\u57DF\u5BF9\u8C61\u4E3B\u52A8\u767B\u8BB0\uFF08esr_node\uFF09\uFF0C\u6B64\u5904\u4E5F\u53EF\u67E5\u770B\u5173\u7CFB" }),
4084
+ nodeGroups.map(([ws, items]) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react8.Fragment, { children: [
4085
+ workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupLabel, children: [
4086
+ ws,
4087
+ " \xB7 ",
4088
+ items.length,
4089
+ " \u4E2A\u8282\u70B9"
4090
+ ] }),
4091
+ items.map((n) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { fontSize: 12.5, padding: "2px 0", cursor: "pointer", borderRadius: 6 }, title: "\u5728\u4FA7\u680F\u6253\u5F00\u8282\u70B9\u8BE6\u60C5", onClick: () => openNode(n.id), children: [
4092
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "mono", style: { ...s3.mono, color: "#4338ca" }, children: n.id.slice(0, 24) }),
4093
+ " ",
4094
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { fontWeight: 600 }, children: n.name }),
4095
+ n.kind && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { ...s3.tag, color: "#4338ca", background: "#eef2ff" }, children: n.kind }),
4096
+ n.description && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { color: "var(--dsh-color-muted, #6b7280)" }, children: [
4097
+ " \u2014 ",
4098
+ n.description.slice(0, 48)
4099
+ ] })
4100
+ ] }, n.id))
4101
+ ] }, ws)),
4102
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 12, color: "var(--dsh-color-muted, #6b7280)", marginTop: 6 }, children: "\u5173\u7CFB\uFF1A" }),
4103
+ links.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.empty, children: "\u6682\u65E0\u5173\u7CFB \u2014 esr_link \u521B\u5EFA" }),
4104
+ linkGroups.map(([ws, items]) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react8.Fragment, { children: [
4105
+ workspace === "" && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: s3.groupLabel, children: [
4106
+ ws,
4107
+ " \xB7 ",
4108
+ items.length,
4109
+ " \u6761\u5173\u7CFB"
4110
+ ] }),
4111
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, marginTop: 4 }, children: items.map((l) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { ...s3.relRow, cursor: "pointer" }, title: "\u5728\u4FA7\u680F\u6253\u5F00\u5173\u7CFB\u8BE6\u60C5", onClick: () => {
4112
+ setDetail({ kind: "link", link: l });
4113
+ setDetailNotice(null);
4114
+ }, children: [
4115
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "mono", style: s3.nodePill, title: l.source, children: nameOf(l.source) }),
4116
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { display: "inline-flex", alignItems: "center", gap: 3, fontSize: 11, color: "var(--dsh-color-muted, #6b7280)" }, children: [
4117
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: { border: "1px dashed var(--dsh-color-border, #cbd5e1)", borderRadius: 999, padding: "1px 7px" }, children: l.relation }),
4118
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { "aria-hidden": true, children: "\u2192" })
4119
+ ] }),
4120
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "mono", style: s3.nodePill, title: l.target, children: nameOf(l.target) }),
4121
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { style: { fontSize: 11, color: "var(--dsh-color-muted-weak, #9ca3af)" }, children: [
4122
+ "\xB7 ",
4123
+ fmtDate2(l.createdAt)
4124
+ ] })
4125
+ ] }, l.id)) })
4126
+ ] }, ws))
4127
+ ] })
4128
+ ] }),
4129
+ view === "preview" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.subPanel, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4130
+ EngramPreview,
4137
4131
  {
4138
- type: "button",
4139
- style: { ...s3.discard, ...!anyDirty || saving ? s3.disabled : void 0 },
4140
- disabled: !anyDirty || saving,
4141
- onClick: discard,
4142
- children: "\u653E\u5F03\u4FEE\u6539"
4132
+ api,
4133
+ workspace,
4134
+ defaultWorkspace: previewDefault,
4135
+ workspaces
4143
4136
  }
4144
- ),
4137
+ ) }),
4138
+ view === "telemetry" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.subPanel, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EngramTelemetry, { api, workspace }) }),
4139
+ view === "config" && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.subPanel, children: scope ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EngramConfigCard, { scope, defaultOpen: true }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: s3.error, children: "\u8BBE\u7F6E\u9762\u677F\u4E0D\u53EF\u7528\uFF1Ahost \u6CA1\u6709\u4E3A dsh-engram \u63D0\u4F9B\u8BBE\u7F6E\u547D\u540D\u7A7A\u95F4\uFF08\u65E7\u7248 DSH \u8D70\u300C\u63D2\u4EF6\u300D\u2192 \u914D\u7F6E tab\uFF09\u3002" }) })
4140
+ ] }),
4141
+ detail && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: "0 0 320px", maxWidth: "38%", position: "sticky", top: 8 }, children: [
4142
+ detailNotice && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: 11.5, padding: "6px 10px", marginBottom: 6, borderRadius: 8, background: "rgba(245,158,11,.14)", color: "#b45309", border: "1px solid rgba(245,158,11,.35)" }, children: detailNotice }),
4145
4143
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
4146
- "button",
4144
+ EngramDetail,
4147
4145
  {
4148
- type: "button",
4149
- style: { ...s3.save, ...!anyDirty || saving ? s3.disabled : void 0 },
4150
- disabled: !anyDirty || saving,
4151
- onClick: () => void save(),
4152
- children: saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
4146
+ target: detail,
4147
+ api,
4148
+ tasks,
4149
+ nodes,
4150
+ links,
4151
+ memories,
4152
+ onClose: () => setDetail(null),
4153
+ onNavigateMemory: openMemory,
4154
+ onChanged: () => void refresh()
4153
4155
  }
4154
4156
  )
4155
4157
  ] })
4156
4158
  ] })
4157
- ] }) });
4159
+ ] });
4158
4160
  }
4159
4161
 
4160
4162
  // client/src/scope.ts
@@ -5284,7 +5286,8 @@ function apply(ctx) {
5284
5286
  ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-engram: dictionaries");
5285
5287
  const api = new EngramApi();
5286
5288
  const t = ctx.locale.bind(NS);
5287
- const sectionInjected = () => ({ api, t });
5289
+ const scope = new EngramScopeImpl(api, "dsh-engram");
5290
+ const sectionInjected = () => ({ api, t, scope });
5288
5291
  try {
5289
5292
  ctx.slots.inject(
5290
5293
  "conversation.input.dock",
@@ -5320,7 +5323,6 @@ function apply(ctx) {
5320
5323
  } catch (error) {
5321
5324
  console.warn("[dsh-engram] settings.section registration failed:", error);
5322
5325
  }
5323
- const scope = new EngramScopeImpl(api, "dsh-engram");
5324
5326
  const cardInjected = () => ({ scope });
5325
5327
  try {
5326
5328
  ctx.slots.inject(