dsh-engram 0.9.1 → 0.9.2
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 +36 -0
- package/README.zh.md +30 -0
- package/docs/RELEASE-NOTES-v0.9.2.md +83 -0
- package/lib/api.js +8 -2
- package/lib/client.js +1017 -625
- package/lib/esrModel.mjs +25 -6
- package/lib/mental.js +47 -10
- package/package.json +3 -1
- package/scripts/host-mount-probe.mjs +10 -2
package/lib/esrModel.mjs
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
// <autogenerated by client/build-model.mjs — do not edit directly>
|
|
2
2
|
// Compiles client/src/esrModel.ts for the node test runner.
|
|
3
3
|
|
|
4
|
+
// client/src/i18n/index.ts
|
|
5
|
+
var DEFAULT_LOCALE = "en-US";
|
|
6
|
+
var translate = (key) => key;
|
|
7
|
+
var localeTag = DEFAULT_LOCALE;
|
|
8
|
+
function activeLocale() {
|
|
9
|
+
return localeTag;
|
|
10
|
+
}
|
|
11
|
+
function format(text, params) {
|
|
12
|
+
return text.replace(
|
|
13
|
+
/\{(\w+)\}/g,
|
|
14
|
+
(match, name) => Object.prototype.hasOwnProperty.call(params, name) ? String(params[name]) : match
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function stringParams(params) {
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [name, value] of Object.entries(params)) out[name] = String(value);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
function tr(key, params) {
|
|
23
|
+
const text = params === void 0 ? translate(key) : translate(key, stringParams(params));
|
|
24
|
+
return params === void 0 ? text : format(text, params);
|
|
25
|
+
}
|
|
26
|
+
|
|
4
27
|
// client/src/esrModel.ts
|
|
5
28
|
function taskGaps(t) {
|
|
6
29
|
const gaps = [];
|
|
@@ -23,7 +46,7 @@ function shortAgent(a) {
|
|
|
23
46
|
return bare.replace(/^session[-_]/i, "").replace(/^agent[-_]/i, "").slice(0, 14);
|
|
24
47
|
}
|
|
25
48
|
function fmtDateShort(ts) {
|
|
26
|
-
return ts ? new Date(ts).toLocaleDateString(
|
|
49
|
+
return ts ? new Date(ts).toLocaleDateString(activeLocale(), { month: "short", day: "numeric" }) : "\u2013";
|
|
27
50
|
}
|
|
28
51
|
function buildTasksMarkdown(tasks) {
|
|
29
52
|
const esc = (s) => String(s ?? "").replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
@@ -31,11 +54,7 @@ function buildTasksMarkdown(tasks) {
|
|
|
31
54
|
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"}`;
|
|
32
55
|
return `| ${t.state} | ${esc(t.name)} | ${esc(t.workspace.replace(/^.*[\\/]/, ""))} | ${esc(taskGaps(t).join(", ") || "\u2014")} | ${ev} | ${fmtDateShort(t.createdAt ?? 0)} |`;
|
|
33
56
|
});
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
| \u72B6\u6001 | \u4EFB\u52A1 | \u5DE5\u4F5C\u533A | \u8BC1\u636E\u7F3A\u53E3 | \u8BC1\u636E | \u521B\u5EFA |
|
|
37
|
-
|---|---|---|---|---|---|
|
|
38
|
-
${rows.join("\n") || "(\u65E0\u4EFB\u52A1)"}`;
|
|
57
|
+
return tr("# ESR \u4EFB\u52A1\u5BFC\u51FA \xB7 {0}\n\n| \u72B6\u6001 | \u4EFB\u52A1 | \u5DE5\u4F5C\u533A | \u8BC1\u636E\u7F3A\u53E3 | \u8BC1\u636E | \u521B\u5EFA |\n|---|---|---|---|---|---|\n{1}", { "0": (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), "1": rows.join("\n") || tr("(\u65E0\u4EFB\u52A1)") });
|
|
39
58
|
}
|
|
40
59
|
var POLL_MS = 2e4;
|
|
41
60
|
export {
|
package/lib/mental.js
CHANGED
|
@@ -17,6 +17,42 @@ import { hashText } from "./util.js";
|
|
|
17
17
|
|
|
18
18
|
const DEFAULT_MAX_AGE_MS = 10 * 60 * 1000;
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Standing-answer copy. The language is a RENDER parameter, not a cache key:
|
|
22
|
+
* the cached row and the `esr_model` tool keep their default (zh) copy, while
|
|
23
|
+
* the GUI asks the route for its active language. Russian is the language this
|
|
24
|
+
* client currently ships.
|
|
25
|
+
*/
|
|
26
|
+
const COPY = {
|
|
27
|
+
zh: {
|
|
28
|
+
allWorkspaces: "全部工作区",
|
|
29
|
+
heading: "常驻摘要",
|
|
30
|
+
tasks: (active, ready, stable) => `- 任务:${active} 进行中(${ready} 就绪)· ${stable} 已闭环`,
|
|
31
|
+
graph: (nodes, links) => `- 实体图:${nodes} 节点 · ${links} 链接`,
|
|
32
|
+
observation: (n, evidence) => `- 观测:${n} 条信念(累计 ${evidence} 证据)`,
|
|
33
|
+
memories: (n, kindHist) => `- 记忆:${n} 条${kindHist ? `(${kindHist})` : ""}`,
|
|
34
|
+
topBeliefs: "重点信念:",
|
|
35
|
+
risks: "未闭环风险:",
|
|
36
|
+
riskLine: (name, gaps, ageDays) => `- ${name} · ${gaps} · ${ageDays}天`,
|
|
37
|
+
},
|
|
38
|
+
ru: {
|
|
39
|
+
allWorkspaces: "Все пространства",
|
|
40
|
+
heading: "постоянная сводка",
|
|
41
|
+
tasks: (active, ready, stable) => `- Задачи: ${active} в работе (${ready} готовы) · закрыто: ${stable}`,
|
|
42
|
+
graph: (nodes, links) => `- Граф сущностей: узлов ${nodes} · связей ${links}`,
|
|
43
|
+
observation: (n, evidence) => `- Наблюдения: ${n} · доказательств всего: ${evidence}`,
|
|
44
|
+
memories: (n, kindHist) => `- Память: ${n}${kindHist ? ` (${kindHist})` : ""}`,
|
|
45
|
+
topBeliefs: "Ключевые убеждения:",
|
|
46
|
+
risks: "Незакрытые риски:",
|
|
47
|
+
riskLine: (name, gaps, ageDays) => `- ${name} · ${gaps} · ${ageDays} дн.`,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Copy table for a render language; unknown languages fall back to the source language. */
|
|
52
|
+
function copyFor(lang) {
|
|
53
|
+
return COPY[lang] ?? COPY.zh;
|
|
54
|
+
}
|
|
55
|
+
|
|
20
56
|
/** Escape a name for markdown (pipes/newlines). */
|
|
21
57
|
function mdCell(value) {
|
|
22
58
|
return String(value ?? "")
|
|
@@ -29,10 +65,11 @@ function mdCell(value) {
|
|
|
29
65
|
* Aggregate the workspace into a standing-answer markdown blob. Purely derives
|
|
30
66
|
* from already-indexed data — safe to run on every read.
|
|
31
67
|
*/
|
|
32
|
-
export function compileSummary(domain, ws, { now = Date.now() } = {}) {
|
|
68
|
+
export function compileSummary(domain, ws, { now = Date.now(), lang = "zh" } = {}) {
|
|
69
|
+
const L = copyFor(lang);
|
|
33
70
|
const summary = domain.summarize();
|
|
34
71
|
const scope = ws && summary.workspaces[ws] ? { ...summary.workspaces[ws] } : { ...summary.totals };
|
|
35
|
-
const label = ws ? String(ws).replace(/^.*[\\/]/, "") :
|
|
72
|
+
const label = ws ? String(ws).replace(/^.*[\\/]/, "") : L.allWorkspaces;
|
|
36
73
|
|
|
37
74
|
let tasks = [];
|
|
38
75
|
try {
|
|
@@ -53,7 +90,7 @@ export function compileSummary(domain, ws, { now = Date.now() } = {}) {
|
|
|
53
90
|
if (gaps.length === 0) ready += 1;
|
|
54
91
|
else {
|
|
55
92
|
const ageDays = Math.max(0, Math.floor((now - (t.createdAt ?? now)) / 86400000));
|
|
56
|
-
risky.push(
|
|
93
|
+
risky.push(L.riskLine(mdCell(t.name), gaps.join("/"), ageDays));
|
|
57
94
|
}
|
|
58
95
|
}
|
|
59
96
|
const stable = tasks.filter((t) => t.state === "stable").length;
|
|
@@ -77,18 +114,18 @@ export function compileSummary(domain, ws, { now = Date.now() } = {}) {
|
|
|
77
114
|
}
|
|
78
115
|
|
|
79
116
|
const lines = [
|
|
80
|
-
`## ${label} ·
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
117
|
+
`## ${label} · ${L.heading}`,
|
|
118
|
+
L.tasks(active.length, ready, stable),
|
|
119
|
+
L.graph(scope.nodes ?? 0, scope.links ?? 0),
|
|
120
|
+
L.observation(obs.length, obsEvidence),
|
|
121
|
+
L.memories(scope.memories ?? 0, kindHist),
|
|
85
122
|
];
|
|
86
123
|
if (topObs.length > 0) {
|
|
87
|
-
lines.push("",
|
|
124
|
+
lines.push("", L.topBeliefs);
|
|
88
125
|
for (const o of topObs) lines.push(`- ${mdCell(o.text)} — ×${o.proof?.count ?? 0}`);
|
|
89
126
|
}
|
|
90
127
|
if (risky.length > 0) {
|
|
91
|
-
lines.push("",
|
|
128
|
+
lines.push("", L.risks);
|
|
92
129
|
lines.push(...risky.slice(0, 5));
|
|
93
130
|
}
|
|
94
131
|
return lines.join("\n");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-engram",
|
|
3
3
|
"description": "Minimalist long-term memory for DeepSeek Harness, distilling symbolic-index + pi-esr ideas: zero-LLM auto-capture, a symbolic [ENGRAM] index with progressive disclosure, and an ESR-lite evidence-closure protocol (esr_task / esr_node / esr_close / esr_link). Real agent-behaviour telemetry (usage rollup + /stats) and a deterministic offline recall benchmark (npm run eval). Hot path is model-free; storage on ctx.storageDomain; web memory viewer, observability panel and config card through DSH's native settings slots.",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.2",
|
|
5
5
|
"packageManager": "pnpm@11.22.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
"eval": "node eval/recall-bench.mjs",
|
|
48
48
|
"gate:audit": "node eval/gate-audit.mjs",
|
|
49
49
|
"host:probe": "node scripts/host-mount-probe.mjs",
|
|
50
|
+
"i18n:check": "node tools/i18n/wrap.mjs --check",
|
|
51
|
+
"i18n:wrap": "node tools/i18n/wrap.mjs",
|
|
50
52
|
"playbook:audit": "node eval/playbook-audit.mjs",
|
|
51
53
|
"prepublishOnly": "node scripts/prepublish-check.mjs",
|
|
52
54
|
"quality:ab": "node eval/quality-ab.mjs",
|
|
@@ -68,12 +68,20 @@ export function apply(ctx) {
|
|
|
68
68
|
result.config = status.config;
|
|
69
69
|
} catch (error) { result.statusError = String(error?.message ?? error); }
|
|
70
70
|
try {
|
|
71
|
+
// Enough of the Session surface for the real token meter to fold an empty
|
|
72
|
+
// log: it walks \`seq\` through \`eventAt()\` and lazily builds projection
|
|
73
|
+
// cells through \`snapshotEvents()\`. Without these it throws before the
|
|
74
|
+
// engine ever reads its own ctx, which would prove nothing.
|
|
71
75
|
const agent = {
|
|
72
76
|
session: {
|
|
73
77
|
id: "probe-session",
|
|
74
|
-
header: { cwd: "/tmp" },
|
|
78
|
+
header: { id: "probe-session", cwd: "/tmp" },
|
|
79
|
+
inheritedEventCount: 0,
|
|
80
|
+
seq: 0,
|
|
75
81
|
messages: [],
|
|
76
82
|
events: [],
|
|
83
|
+
eventAt: () => void 0,
|
|
84
|
+
snapshotEvents: () => [],
|
|
77
85
|
requestHeader: () => ({ config: { provider: "deepseek-official", model: "deepseek-flash" } }),
|
|
78
86
|
},
|
|
79
87
|
options: {},
|
|
@@ -81,7 +89,7 @@ export function apply(ctx) {
|
|
|
81
89
|
const out = await ctx.compaction.compactIfNeeded(agent, "pressure", new AbortController().signal);
|
|
82
90
|
result.pressure = { ok: true, result: out ?? null };
|
|
83
91
|
} catch (error) {
|
|
84
|
-
result.pressure = { ok: false, error: String(error?.message ?? error) };
|
|
92
|
+
result.pressure = { ok: false, error: String(error?.message ?? error), stack: String(error?.stack ?? "").split("\\n").slice(0, 6) };
|
|
85
93
|
}
|
|
86
94
|
fs.writeFileSync(OUT, JSON.stringify(result, null, 1));
|
|
87
95
|
}, 8000);
|