dsh-engram 0.7.2 → 0.8.6

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