dsh-engram 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -20
- package/README.zh.md +79 -13
- package/cordis.patch.yml +12 -0
- package/lib/api.js +24 -2
- package/lib/client.js +76 -90
- package/lib/compaction.js +85 -0
- package/lib/context-gc.js +626 -0
- package/lib/esrModel.mjs +49 -0
- package/lib/index-block.js +169 -39
- package/lib/index.js +78 -18
- package/lib/settings.js +4 -0
- package/lib/store.js +15 -0
- package/lib/tools.js +29 -0
- package/package.json +17 -4
package/lib/client.js
CHANGED
|
@@ -4,6 +4,7 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
"use strict";
|
|
7
8
|
var __defProp = Object.defineProperty;
|
|
8
9
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
10
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -217,6 +218,44 @@ function useEngramTheme() {
|
|
|
217
218
|
return { dark, vars };
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
// client/src/esrModel.ts
|
|
222
|
+
function taskGaps(t) {
|
|
223
|
+
const gaps = [];
|
|
224
|
+
if (!t.artifact) gaps.push("artifact");
|
|
225
|
+
if (!t.evaluation) gaps.push("evaluation");
|
|
226
|
+
if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
|
|
227
|
+
return gaps;
|
|
228
|
+
}
|
|
229
|
+
function shortId(id) {
|
|
230
|
+
return id.length > 12 ? `${id.slice(0, 6)}\u2026${id.slice(-4)}` : id;
|
|
231
|
+
}
|
|
232
|
+
function blockedBy(t, all) {
|
|
233
|
+
if (t.state === "stable") return 0;
|
|
234
|
+
const open = new Set(all.filter((x) => x.state !== "stable").map((x) => x.id));
|
|
235
|
+
return (t.deps ?? []).filter((d) => d.kind !== "relates-to" && open.has(d.id)).length;
|
|
236
|
+
}
|
|
237
|
+
function shortAgent(a) {
|
|
238
|
+
if (!a) return "";
|
|
239
|
+
const bare = a.split(/[@#/:\\]+/).pop();
|
|
240
|
+
return bare.replace(/^session[-_]/i, "").replace(/^agent[-_]/i, "").slice(0, 14);
|
|
241
|
+
}
|
|
242
|
+
function fmtDateShort(ts) {
|
|
243
|
+
return ts ? new Date(ts).toLocaleDateString("zh-CN", { month: "short", day: "numeric" }) : "\u2013";
|
|
244
|
+
}
|
|
245
|
+
function buildTasksMarkdown(tasks) {
|
|
246
|
+
const esc = (s4) => String(s4 ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
247
|
+
const rows = tasks.slice().sort((a, b) => a.state.localeCompare(b.state) || (a.createdAt ?? 0) - (b.createdAt ?? 0)).map((t) => {
|
|
248
|
+
const ev = `${t.artifact ? "artifact\u2713" : "artifact\u2717"} \xB7 ${t.evaluation ? "eval\u2713" : "eval\u2717"} \xB7 ${(t.memoryRefs?.length ?? 0) > 0 ? "ref\u2713" : "ref\u2717"}`;
|
|
249
|
+
return `| ${t.state} | ${esc(t.name)} | ${esc(t.workspace.replace(/^.*[\\/]/, ""))} | ${esc(taskGaps(t).join(", ") || "\u2014")} | ${ev} | ${fmtDateShort(t.createdAt ?? 0)} |`;
|
|
250
|
+
});
|
|
251
|
+
return `# ESR \u4EFB\u52A1\u5BFC\u51FA \xB7 ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
|
|
252
|
+
|
|
253
|
+
| \u72B6\u6001 | \u4EFB\u52A1 | \u5DE5\u4F5C\u533A | \u8BC1\u636E\u7F3A\u53E3 | \u8BC1\u636E | \u521B\u5EFA |
|
|
254
|
+
|---|---|---|---|---|---|
|
|
255
|
+
${rows.join("\n") || "(\u65E0\u4EFB\u52A1)"}`;
|
|
256
|
+
}
|
|
257
|
+
var POLL_MS = 2e4;
|
|
258
|
+
|
|
220
259
|
// client/src/EngramTaskDock.tsx
|
|
221
260
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
222
261
|
var EMPTY = { tasks: [], links: [], nodes: [], denied: false, error: null, loading: true };
|
|
@@ -233,13 +272,6 @@ function fmtD(ts) {
|
|
|
233
272
|
const p = (n) => String(n).padStart(2, "0");
|
|
234
273
|
return `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
235
274
|
}
|
|
236
|
-
var taskGaps = (t) => {
|
|
237
|
-
const gaps = [];
|
|
238
|
-
if (!t.artifact) gaps.push("artifact");
|
|
239
|
-
if (!t.evaluation) gaps.push("evaluation");
|
|
240
|
-
if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
|
|
241
|
-
return gaps;
|
|
242
|
-
};
|
|
243
275
|
var STATUS = {
|
|
244
276
|
gap: { bg: "var(--dsw-alias-state-warn-secondary, rgba(245,158,11,.14))", fg: "var(--dsw-alias-state-warn-label, #b45309)" },
|
|
245
277
|
ready: { bg: "rgba(59,130,246,.13)", fg: "var(--dsw-alias-label-primary-bluish, #1d4ed8)" },
|
|
@@ -329,6 +361,7 @@ function EngramTaskDock({ sessionId, useSessions, useWorkspaces, useProjection,
|
|
|
329
361
|
const [expandedId, setExpandedId] = (0, import_react2.useState)(null);
|
|
330
362
|
const [taskPage, setTaskPage] = (0, import_react2.useState)(0);
|
|
331
363
|
const [promoted, setPromoted] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
|
|
364
|
+
const [promoteNote, setPromoteNote] = (0, import_react2.useState)(null);
|
|
332
365
|
const [promoteBusy, setPromoteBusy] = (0, import_react2.useState)(false);
|
|
333
366
|
const [toolStats, setToolStats] = (0, import_react2.useState)(null);
|
|
334
367
|
const [closeArtifact, setCloseArtifact] = (0, import_react2.useState)("");
|
|
@@ -397,7 +430,7 @@ function EngramTaskDock({ sessionId, useSessions, useWorkspaces, useProjection,
|
|
|
397
430
|
(0, import_react2.useEffect)(() => {
|
|
398
431
|
if (!effWs || data.denied) return;
|
|
399
432
|
if (pollRef.current !== null) window.clearInterval(pollRef.current);
|
|
400
|
-
pollRef.current = window.setInterval(() => void load(),
|
|
433
|
+
pollRef.current = window.setInterval(() => void load(), POLL_MS);
|
|
401
434
|
return () => {
|
|
402
435
|
if (pollRef.current !== null) window.clearInterval(pollRef.current);
|
|
403
436
|
pollRef.current = null;
|
|
@@ -456,18 +489,26 @@ function EngramTaskDock({ sessionId, useSessions, useWorkspaces, useProjection,
|
|
|
456
489
|
const promoteAll = async () => {
|
|
457
490
|
if (!effWs || promoteBusy || toPromote.length === 0) return;
|
|
458
491
|
setPromoteBusy(true);
|
|
492
|
+
setPromoteNote(null);
|
|
459
493
|
const done = [];
|
|
494
|
+
const failed = [];
|
|
460
495
|
for (const it of toPromote) {
|
|
461
496
|
try {
|
|
462
497
|
await api.createTask(effWs, it.content, "\u6E90\u81EA\u4F1A\u8BDD\u8BA1\u5212\uFF08todo_write \u4E00\u952E\u6C89\u6DC0\uFF09");
|
|
463
498
|
done.push(it.content);
|
|
464
499
|
} catch {
|
|
500
|
+
failed.push(it.content);
|
|
465
501
|
}
|
|
466
502
|
}
|
|
467
503
|
if (done.length > 0) {
|
|
468
504
|
setPromoted((prev) => /* @__PURE__ */ new Set([...prev, ...done]));
|
|
469
505
|
void load();
|
|
470
506
|
}
|
|
507
|
+
if (failed.length > 0) {
|
|
508
|
+
setPromoteNote(
|
|
509
|
+
failed.length === done.length ? `\u6C89\u6DC0\u5931\u8D25\uFF1A${failed.length} \u9879\u672A\u80FD\u521B\u5EFA\uFF08\u5DE5\u4F5C\u533A\u5DF2\u6EE1\u6216\u670D\u52A1\u4E0D\u53EF\u8FBE\uFF09\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5` : `\u90E8\u5206\u5931\u8D25\uFF1A${failed.length}/${toPromote.length} \u9879\u672A\u80FD\u521B\u5EFA`
|
|
510
|
+
);
|
|
511
|
+
}
|
|
471
512
|
setPromoteBusy(false);
|
|
472
513
|
};
|
|
473
514
|
const submitClose = async (task) => {
|
|
@@ -726,6 +767,10 @@ function EngramTaskDock({ sessionId, useSessions, useWorkspaces, useProjection,
|
|
|
726
767
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "ed-btn", style: { ...btn, ...btnSolid, padding: "3px 9px", fontSize: 11.5 }, disabled: promoteBusy || toPromote.length === 0, onClick: () => void promoteAll(), title: "\u628A\u672C\u8F6E\u5F85\u529E\u8F6C\u6210 ESR \u4EFB\u52A1\u8349\u7A3F\uFF08\u8DE8\u4F1A\u8BDD\u4FDD\u7559\uFF09", children: promoteBusy ? "\u2026" : promoted.size > 0 ? toPromote.length > 0 ? `\u5DF2\u6C89\u6DC0 ${promoted.size} \u9879 \xB7 \u518D\u6C89\u6DC0 ${toPromote.length} \u9879` : `\u5DF2\u6C89\u6DC0 ${promoted.size} \u9879` : `\u6C89\u6DC0\u5230 ESR\uFF08${toPromote.length} \u9879\uFF09` }),
|
|
727
768
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10.5, color: "var(--dsw-alias-label-dimmed, var(--dsh-color-muted-weak, #9ca3af))" }, children: "\u8F6C\u8349\u7A3F \xB7 \u8DE8\u4F1A\u8BDD\u95ED\u73AF" })
|
|
728
769
|
] }),
|
|
770
|
+
promoteNote && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 11, color: "#dc2626", lineHeight: 1.4 }, role: "alert", children: [
|
|
771
|
+
"\u26A0 ",
|
|
772
|
+
promoteNote
|
|
773
|
+
] }),
|
|
729
774
|
activeTasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "flex-start", fontSize: 11.5, lineHeight: 1.5, color: "var(--dsw-alias-label-dimmed, var(--dsh-color-muted, #6b7280))", paddingTop: 2 }, children: [
|
|
730
775
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, style: { color: badgeEsr.color }, children: "\u21B3" }),
|
|
731
776
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
@@ -1327,7 +1372,7 @@ function EngramGraph({ workspace, entities, tasks, links, nameOf }) {
|
|
|
1327
1372
|
setView({ x: pan.current.vx + dx, y: pan.current.vy + dy, k: view.k });
|
|
1328
1373
|
}
|
|
1329
1374
|
};
|
|
1330
|
-
const endPointer = (
|
|
1375
|
+
const endPointer = () => {
|
|
1331
1376
|
if (dragging.current) {
|
|
1332
1377
|
const p = posRef.current.get(dragging.current.id);
|
|
1333
1378
|
if (p) {
|
|
@@ -1639,7 +1684,7 @@ function EngramPreview({ api, workspace, defaultWorkspace, workspaces }) {
|
|
|
1639
1684
|
);
|
|
1640
1685
|
(0, import_react4.useEffect)(() => {
|
|
1641
1686
|
void refresh();
|
|
1642
|
-
const id = setInterval(() => void refresh(),
|
|
1687
|
+
const id = setInterval(() => void refresh(), POLL_MS);
|
|
1643
1688
|
return () => clearInterval(id);
|
|
1644
1689
|
}, [refresh]);
|
|
1645
1690
|
const copy = (which) => {
|
|
@@ -1950,13 +1995,12 @@ function EngramTelemetry({ api, workspace }) {
|
|
|
1950
1995
|
);
|
|
1951
1996
|
(0, import_react5.useEffect)(() => {
|
|
1952
1997
|
void refresh();
|
|
1953
|
-
const id = setInterval(() => void refresh(),
|
|
1998
|
+
const id = setInterval(() => void refresh(), POLL_MS);
|
|
1954
1999
|
return () => clearInterval(id);
|
|
1955
2000
|
}, [refresh]);
|
|
1956
2001
|
const toolRows = (0, import_react5.useMemo)(() => {
|
|
1957
2002
|
if (!stats) return [];
|
|
1958
2003
|
const entries = Object.entries(stats.totals.counts ?? {}).sort((a, b) => b[1] - a[1]);
|
|
1959
|
-
const max = entries.length > 0 ? entries[0][1] : 0;
|
|
1960
2004
|
return entries.slice(0, 8).map(([name, count]) => ({
|
|
1961
2005
|
name,
|
|
1962
2006
|
count,
|
|
@@ -2180,7 +2224,7 @@ function gapsOf(t) {
|
|
|
2180
2224
|
if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
|
|
2181
2225
|
return gaps;
|
|
2182
2226
|
}
|
|
2183
|
-
function EngramDetail({ target, api,
|
|
2227
|
+
function EngramDetail({ target, api, tasks, nodes, links, onClose, onNavigateMemory, onChanged }) {
|
|
2184
2228
|
const { dark, vars } = useEngramTheme();
|
|
2185
2229
|
const [gArt, setGArt] = (0, import_react6.useState)("");
|
|
2186
2230
|
const [gEval, setGEval] = (0, import_react6.useState)("");
|
|
@@ -2674,13 +2718,6 @@ function StatCard({ num, label }) {
|
|
|
2674
2718
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: s2.cardLabel, children: label })
|
|
2675
2719
|
] });
|
|
2676
2720
|
}
|
|
2677
|
-
function taskGaps2(t) {
|
|
2678
|
-
const gaps = [];
|
|
2679
|
-
if (!t.artifact) gaps.push("artifact");
|
|
2680
|
-
if (!t.evaluation) gaps.push("evaluation");
|
|
2681
|
-
if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
|
|
2682
|
-
return gaps;
|
|
2683
|
-
}
|
|
2684
2721
|
function groupByWorkspace(items) {
|
|
2685
2722
|
const groups = /* @__PURE__ */ new Map();
|
|
2686
2723
|
for (const m of items) {
|
|
@@ -2729,7 +2766,7 @@ function EngramSection({ api, t }) {
|
|
|
2729
2766
|
const ov = await api.overview();
|
|
2730
2767
|
setOverview(ov);
|
|
2731
2768
|
const wsList = workspace ? [workspace] : Object.keys(ov.workspaces);
|
|
2732
|
-
const [mem, taskGroups2, linkGroups2, nodeGroups2
|
|
2769
|
+
const [mem, taskGroups2, linkGroups2, nodeGroups2] = await Promise.all([
|
|
2733
2770
|
api.memories({
|
|
2734
2771
|
workspace: workspace || void 0,
|
|
2735
2772
|
q: q || void 0,
|
|
@@ -2752,6 +2789,8 @@ function EngramSection({ api, t }) {
|
|
|
2752
2789
|
}, [api, workspace, q, kind, status]);
|
|
2753
2790
|
(0, import_react7.useEffect)(() => {
|
|
2754
2791
|
void refresh();
|
|
2792
|
+
const id = setInterval(() => void refresh(), POLL_MS);
|
|
2793
|
+
return () => clearInterval(id);
|
|
2755
2794
|
}, [refresh]);
|
|
2756
2795
|
const workspaces = (0, import_react7.useMemo)(() => overview ? Object.keys(overview.workspaces) : [], [overview]);
|
|
2757
2796
|
const kindsPresent = (0, import_react7.useMemo)(() => overview ? Object.keys(overview.kinds) : [], [overview]);
|
|
@@ -2989,11 +3028,11 @@ function EngramSection({ api, t }) {
|
|
|
2989
3028
|
] }, t2.id)),
|
|
2990
3029
|
gcReport.removedLinks.slice(0, 3).map((l) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.mono, children: [
|
|
2991
3030
|
"- link ",
|
|
2992
|
-
l.source.slice(0, 8),
|
|
3031
|
+
(l.source ?? "?").slice(0, 8),
|
|
2993
3032
|
" --",
|
|
2994
|
-
l.relation,
|
|
3033
|
+
l.relation ?? "",
|
|
2995
3034
|
"--> ",
|
|
2996
|
-
l.target.slice(0, 8)
|
|
3035
|
+
(l.target ?? "?").slice(0, 8)
|
|
2997
3036
|
] }, l.id))
|
|
2998
3037
|
] })
|
|
2999
3038
|
] }),
|
|
@@ -3075,7 +3114,7 @@ function EngramSection({ api, t }) {
|
|
|
3075
3114
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: { ...s2.td, minWidth: 0 }, children: [
|
|
3076
3115
|
/* @__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 }),
|
|
3077
3116
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", marginTop: 2, flexWrap: "wrap" }, children: [
|
|
3078
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { ...s2.mono,
|
|
3117
|
+
/* @__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: [
|
|
3079
3118
|
fmtDate2(r.m.createdAt),
|
|
3080
3119
|
" \xB7 ",
|
|
3081
3120
|
r.m.id.slice(0, 8),
|
|
@@ -3171,7 +3210,7 @@ ${r.m.text.slice(0, 60)}`)) void act(() => api.remove(r.m.id, r.m.workspace));
|
|
|
3171
3210
|
" \u4E2A\u4EFB\u52A1"
|
|
3172
3211
|
] }),
|
|
3173
3212
|
items.map((task) => {
|
|
3174
|
-
const gaps =
|
|
3213
|
+
const gaps = taskGaps(task);
|
|
3175
3214
|
const isStable = task.state === "stable";
|
|
3176
3215
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: s2.subPanel, children: [
|
|
3177
3216
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontWeight: 600, fontSize: 13 }, children: [
|
|
@@ -3284,7 +3323,6 @@ ${r.m.text.slice(0, 60)}`)) void act(() => api.remove(r.m.id, r.m.workspace));
|
|
|
3284
3323
|
{
|
|
3285
3324
|
target: detail,
|
|
3286
3325
|
api,
|
|
3287
|
-
memories,
|
|
3288
3326
|
tasks,
|
|
3289
3327
|
nodes,
|
|
3290
3328
|
links,
|
|
@@ -3330,10 +3368,11 @@ var ADVANCED_GROUPS = [
|
|
|
3330
3368
|
{
|
|
3331
3369
|
id: "retention-adv",
|
|
3332
3370
|
title: "\u751F\u547D\u5468\u671F\u4E0E GC\uFF08\u9AD8\u7EA7\uFF09",
|
|
3333
|
-
description: "\u5BB9\u91CF\u4E0A\u9650\u4E0E\u5B9A\u671F\u56DE\u6536",
|
|
3371
|
+
description: "\u5BB9\u91CF\u4E0A\u9650\u4E0E\u5B9A\u671F\u56DE\u6536\uFF08\u81EA\u52A8 GC = \u66FF\u4EE3 DSH \u81EA\u52A8 compact\uFF09",
|
|
3334
3372
|
fields: [
|
|
3335
|
-
{ key: "maxMemoriesPerWorkspace", label: "\u5DE5\u4F5C\u533A\u8BB0\u5FC6\u4E0A\u9650", kind: "num", min: 0, max: 1e4 },
|
|
3373
|
+
{ 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 },
|
|
3336
3374
|
{ 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" },
|
|
3375
|
+
{ 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" },
|
|
3337
3376
|
{ 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 },
|
|
3338
3377
|
{ 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" }
|
|
3339
3378
|
]
|
|
@@ -3822,20 +3861,12 @@ var import_client = require("react-dom/client");
|
|
|
3822
3861
|
// client/src/EngramBoard.tsx
|
|
3823
3862
|
var import_react9 = require("react");
|
|
3824
3863
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
3825
|
-
function taskGaps3(t) {
|
|
3826
|
-
const gaps = [];
|
|
3827
|
-
if (!t.artifact) gaps.push("artifact");
|
|
3828
|
-
if (!t.evaluation) gaps.push("evaluation");
|
|
3829
|
-
if (!t.memoryRefs || t.memoryRefs.length === 0) gaps.push("memory_ref");
|
|
3830
|
-
return gaps;
|
|
3831
|
-
}
|
|
3832
3864
|
var COLUMNS = [
|
|
3833
3865
|
{ key: "draft", title: "\u8349\u7A3F", sub: "\u672A\u88AB\u6FC0\u6D3B", color: "#94a3b8", match: (t) => t.state === "draft" },
|
|
3834
|
-
{ key: "gapped", title: "\u8FDB\u884C\u4E2D", sub: "\u8BC1\u636E\u6709\u7F3A\u53E3", color: "#f59e0b", match: (t) => t.state === "active" &&
|
|
3835
|
-
{ key: "ready", title: "\u5C31\u7EEA", sub: "\u8BC1\u636E\u9F50\uFF0C\u53EF\u95ED\u73AF", color: "#10b981", match: (t) => t.state === "active" &&
|
|
3866
|
+
{ key: "gapped", title: "\u8FDB\u884C\u4E2D", sub: "\u8BC1\u636E\u6709\u7F3A\u53E3", color: "#f59e0b", match: (t) => t.state === "active" && taskGaps(t).length > 0 },
|
|
3867
|
+
{ key: "ready", title: "\u5C31\u7EEA", sub: "\u8BC1\u636E\u9F50\uFF0C\u53EF\u95ED\u73AF", color: "#10b981", match: (t) => t.state === "active" && taskGaps(t).length === 0 },
|
|
3836
3868
|
{ key: "stable", title: "\u5DF2\u95ED\u73AF", sub: "\u51ED\u636E\u9F50\u5907", color: "#6366f1", match: (t) => t.state === "stable" }
|
|
3837
3869
|
];
|
|
3838
|
-
var fmtDate3 = (ts) => ts ? new Date(ts).toLocaleDateString("zh-CN", { month: "short", day: "numeric" }) : "\u2013";
|
|
3839
3870
|
function buildCloseEvidence(bulkArtifact, bulkEval, bulkRefs, task) {
|
|
3840
3871
|
const refs = bulkRefs.split(/[,\s]+/).map((x) => x.trim()).filter(Boolean);
|
|
3841
3872
|
return {
|
|
@@ -3844,31 +3875,6 @@ function buildCloseEvidence(bulkArtifact, bulkEval, bulkRefs, task) {
|
|
|
3844
3875
|
memoryRefs: refs.length > 0 ? refs : task.memoryRefs
|
|
3845
3876
|
};
|
|
3846
3877
|
}
|
|
3847
|
-
function buildTasksMarkdown(tasks) {
|
|
3848
|
-
const esc = (s4) => String(s4 ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
3849
|
-
const rows = tasks.slice().sort((a, b) => a.state.localeCompare(b.state) || (a.createdAt ?? 0) - (b.createdAt ?? 0)).map((t) => {
|
|
3850
|
-
const ev = `${t.artifact ? "artifact\u2713" : "artifact\u2717"} \xB7 ${t.evaluation ? "eval\u2713" : "eval\u2717"} \xB7 ${(t.memoryRefs?.length ?? 0) > 0 ? "ref\u2713" : "ref\u2717"}`;
|
|
3851
|
-
return `| ${t.state} | ${esc(t.name)} | ${esc(t.workspace.replace(/^.*[\\/]/, ""))} | ${esc(taskGaps3(t).join(", ") || "\u2014")} | ${ev} | ${fmtDate3(t.createdAt ?? 0)} |`;
|
|
3852
|
-
});
|
|
3853
|
-
return `# ESR \u4EFB\u52A1\u5BFC\u51FA \xB7 ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
|
|
3854
|
-
|
|
3855
|
-
| \u72B6\u6001 | \u4EFB\u52A1 | \u5DE5\u4F5C\u533A | \u8BC1\u636E\u7F3A\u53E3 | \u8BC1\u636E | \u521B\u5EFA |
|
|
3856
|
-
|---|---|---|---|---|---|
|
|
3857
|
-
${rows.join("\n") || "(\u65E0\u4EFB\u52A1)"}`;
|
|
3858
|
-
}
|
|
3859
|
-
function shortId(id) {
|
|
3860
|
-
return id.length > 12 ? `${id.slice(0, 6)}\u2026${id.slice(-4)}` : id;
|
|
3861
|
-
}
|
|
3862
|
-
function blockedBy(t, all) {
|
|
3863
|
-
if (t.state === "stable") return 0;
|
|
3864
|
-
const open = new Set(all.filter((x) => x.state !== "stable").map((x) => x.id));
|
|
3865
|
-
return (t.deps ?? []).filter((d) => d.kind !== "relates-to" && open.has(d.id)).length;
|
|
3866
|
-
}
|
|
3867
|
-
function shortAgent(a) {
|
|
3868
|
-
if (!a) return "";
|
|
3869
|
-
const bare = a.split(/[@#/:\\]+/).pop();
|
|
3870
|
-
return bare.replace(/^session[-_]/i, "").replace(/^agent[-_]/i, "").slice(0, 14);
|
|
3871
|
-
}
|
|
3872
3878
|
var TREND_LABEL = {
|
|
3873
3879
|
new: "\u65B0",
|
|
3874
3880
|
strengthening: "\u589E\u5F3A",
|
|
@@ -3904,7 +3910,6 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
3904
3910
|
const [bulkRefs, setBulkRefs] = (0, import_react9.useState)("");
|
|
3905
3911
|
const [bulkBusy, setBulkBusy] = (0, import_react9.useState)(false);
|
|
3906
3912
|
const [bulkMsg, setBulkMsg] = (0, import_react9.useState)(null);
|
|
3907
|
-
const loadedWs = (0, import_react9.useRef)(/* @__PURE__ */ new Set());
|
|
3908
3913
|
const [obsItems, setObsItems] = (0, import_react9.useState)(null);
|
|
3909
3914
|
const [obsOpen, setObsOpen] = (0, import_react9.useState)(false);
|
|
3910
3915
|
const [model, setModel] = (0, import_react9.useState)(null);
|
|
@@ -3917,10 +3922,8 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
3917
3922
|
const wsList = Object.keys(ov.workspaces);
|
|
3918
3923
|
const entries = await Promise.all(
|
|
3919
3924
|
wsList.map(async (w) => {
|
|
3920
|
-
const
|
|
3921
|
-
|
|
3922
|
-
const res = shouldLoad ? await Promise.all([api.tasks(w, true), api.nodes(w), api.links(w)]) : null;
|
|
3923
|
-
return [w, res ? { tasks: res[0].items, nodes: res[1].items, links: res[2].items } : null];
|
|
3925
|
+
const res = await Promise.all([api.tasks(w, true), api.nodes(w), api.links(w)]);
|
|
3926
|
+
return [w, { tasks: res[0].items, nodes: res[1].items, links: res[2].items }];
|
|
3924
3927
|
})
|
|
3925
3928
|
);
|
|
3926
3929
|
setTasksByWs((prev) => {
|
|
@@ -3953,25 +3956,9 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
3953
3956
|
}, [api]);
|
|
3954
3957
|
(0, import_react9.useEffect)(() => {
|
|
3955
3958
|
void refresh();
|
|
3956
|
-
const id = setInterval(() => void refresh(),
|
|
3959
|
+
const id = setInterval(() => void refresh(), POLL_MS);
|
|
3957
3960
|
return () => clearInterval(id);
|
|
3958
3961
|
}, [refresh]);
|
|
3959
|
-
(0, import_react9.useEffect)(() => {
|
|
3960
|
-
if (!ws || denied) return;
|
|
3961
|
-
const id = setInterval(() => {
|
|
3962
|
-
const fetchWs = async () => {
|
|
3963
|
-
try {
|
|
3964
|
-
const [res, nodeRes, linkRes] = await Promise.all([api.tasks(ws, true), api.nodes(ws), api.links(ws)]);
|
|
3965
|
-
setTasksByWs((prev) => ({ ...prev, [ws]: res.items }));
|
|
3966
|
-
setNodesByWs((prev) => ({ ...prev, [ws]: nodeRes.items }));
|
|
3967
|
-
setLinksByWs((prev) => ({ ...prev, [ws]: linkRes.items }));
|
|
3968
|
-
} catch {
|
|
3969
|
-
}
|
|
3970
|
-
};
|
|
3971
|
-
void fetchWs();
|
|
3972
|
-
}, 2e4);
|
|
3973
|
-
return () => clearInterval(id);
|
|
3974
|
-
}, [ws, denied, api]);
|
|
3975
3962
|
const workspaces = (0, import_react9.useMemo)(
|
|
3976
3963
|
() => overview ? Object.entries(overview.workspaces).sort((a, b) => b[1].tasks - a[1].tasks) : [],
|
|
3977
3964
|
[overview]
|
|
@@ -4004,7 +3991,7 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
4004
3991
|
}
|
|
4005
3992
|
if (t.state === "draft") continue;
|
|
4006
3993
|
active += 1;
|
|
4007
|
-
const gates =
|
|
3994
|
+
const gates = taskGaps(t);
|
|
4008
3995
|
gateTotal += 3;
|
|
4009
3996
|
gateFilled += 3 - gates.length;
|
|
4010
3997
|
if (gates.length === 0) ready += 1;
|
|
@@ -4033,7 +4020,6 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
4033
4020
|
setBusy(true);
|
|
4034
4021
|
try {
|
|
4035
4022
|
await api.createTask(targetWs, name, newDesc);
|
|
4036
|
-
loadedWs.current.add(targetWs);
|
|
4037
4023
|
setNewName("");
|
|
4038
4024
|
setNewDesc("");
|
|
4039
4025
|
setCreating(false);
|
|
@@ -4322,7 +4308,7 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
4322
4308
|
"@",
|
|
4323
4309
|
shortAgent(t.assignee)
|
|
4324
4310
|
] }),
|
|
4325
|
-
(col.key === "gapped" || col.key === "ready") &&
|
|
4311
|
+
(col.key === "gapped" || col.key === "ready") && taskGaps(t).map((g) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { style: hb.gap, children: [
|
|
4326
4312
|
g,
|
|
4327
4313
|
" \u2717"
|
|
4328
4314
|
] }, g)),
|
|
@@ -4334,7 +4320,7 @@ function EngramBoard({ api, onRequestClose }) {
|
|
|
4334
4320
|
"\xB7ref",
|
|
4335
4321
|
(t.memoryRefs?.length ?? 0) > 0 ? "\u2713" : "\u2717"
|
|
4336
4322
|
] }),
|
|
4337
|
-
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: hb.meta, children:
|
|
4323
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { style: hb.meta, children: fmtDateShort(t.createdAt) })
|
|
4338
4324
|
] })
|
|
4339
4325
|
] })
|
|
4340
4326
|
] }),
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-engram — Context GC as a mountable HOST plugin entry (agent-preset row).
|
|
3
|
+
*
|
|
4
|
+
* Web/agent-preset compositions keep compaction on the AGENT plane: the
|
|
5
|
+
* `standard` preset (and friends) mount `@deepseek-ai/dsh-compaction-basic`
|
|
6
|
+
* inside a `compaction` group carrying its own isolate realm. A host-plane row
|
|
7
|
+
* cannot reach that realm, so replacing the default summarizer per-session
|
|
8
|
+
* needs a row that can be listed IN the preset's `compaction` group:
|
|
9
|
+
*
|
|
10
|
+
* ```yaml
|
|
11
|
+
* - id: compaction
|
|
12
|
+
* name: cordis:group
|
|
13
|
+
* group: true
|
|
14
|
+
* isolate:
|
|
15
|
+
* compaction: true
|
|
16
|
+
* toolResultPruner: true
|
|
17
|
+
* config:
|
|
18
|
+
* - id: engram-compaction
|
|
19
|
+
* name: dsh-engram/compaction # this entry
|
|
20
|
+
* - id: command-compact
|
|
21
|
+
* name: '@deepseek-ai/dsh-command-compact'
|
|
22
|
+
* ...
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Config (all optional): `gcReplacesCompaction` (true → Context GC; false →
|
|
26
|
+
* plain BasicCompactionEngine), `gcNarrative`, `gcNarrativeMaxTokens`,
|
|
27
|
+
* `gcNarrativeMaxChars`. Values default to the plugin DEFAULTS.
|
|
28
|
+
*
|
|
29
|
+
* The engine reads engram/ESR state through the storage-domain service shared
|
|
30
|
+
* with the host plane (best-effort reads for pointer classification — the
|
|
31
|
+
* entry never writes).
|
|
32
|
+
*/
|
|
33
|
+
import { openEngramDomain } from "./store.js";
|
|
34
|
+
import { loadCompactionEngine, collectPointerContext } from "./context-gc.js";
|
|
35
|
+
import { workspaceKey as wk } from "./util.js";
|
|
36
|
+
|
|
37
|
+
export const name = "dsh-engram-compaction";
|
|
38
|
+
export const inject = ["llm", "tokenMeter", "sessions"];
|
|
39
|
+
|
|
40
|
+
/** One cached domain per storage-domain service (read-only for compaction). */
|
|
41
|
+
const domains = new WeakMap();
|
|
42
|
+
|
|
43
|
+
async function domainFor(ctx) {
|
|
44
|
+
const storageDomain = ctx.get("storageDomain");
|
|
45
|
+
if (!storageDomain) return null;
|
|
46
|
+
let domain = domains.get(storageDomain);
|
|
47
|
+
if (!domain) {
|
|
48
|
+
domain = await openEngramDomain(storageDomain).catch(() => null);
|
|
49
|
+
if (domain) domains.set(storageDomain, domain);
|
|
50
|
+
}
|
|
51
|
+
return domain;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function apply(ctx, config = {}) {
|
|
55
|
+
const mode = config.gcReplacesCompaction === false ? "default" : "context-gc";
|
|
56
|
+
const deps =
|
|
57
|
+
mode === "context-gc"
|
|
58
|
+
? {
|
|
59
|
+
narrativeEnabled: config.gcNarrative !== false,
|
|
60
|
+
narrativeMaxTokens: config.gcNarrativeMaxTokens ?? 1024,
|
|
61
|
+
narrativeMaxChars: config.gcNarrativeMaxChars ?? 4000,
|
|
62
|
+
readWorkspace: async (workspace, report) => {
|
|
63
|
+
if (!workspace) return null;
|
|
64
|
+
const domain = await domainFor(ctx);
|
|
65
|
+
if (!domain) return null;
|
|
66
|
+
return collectPointerContext(domain, wk(workspace), report);
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
: {};
|
|
70
|
+
|
|
71
|
+
return loadCompactionEngine(ctx, { auto: true }, deps, { mode })
|
|
72
|
+
.then((Engine) => {
|
|
73
|
+
if (!Engine) {
|
|
74
|
+
ctx.logger?.warn?.("engram context-gc: dsh-compaction-basic unavailable — no compaction engine");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
new Engine();
|
|
78
|
+
ctx.logger?.info?.(
|
|
79
|
+
`engram context-gc: compaction = ${mode === "context-gc" ? "Context GC (mechanical eviction + re-fetch pointers)" : "default LLM summarizer (gcReplacesCompaction:false)"}`,
|
|
80
|
+
);
|
|
81
|
+
})
|
|
82
|
+
.catch((error) => {
|
|
83
|
+
ctx.logger?.warn?.(`engram context-gc mount failed: ${String(error)}`);
|
|
84
|
+
});
|
|
85
|
+
}
|