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/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("zh-CN", { month: "short", day: "numeric" }) : "\u2013";
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 `# ESR \u4EFB\u52A1\u5BFC\u51FA \xB7 ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
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(`- ${mdCell(t.name)} · ${gaps.join("/")} · ${ageDays}天`);
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
- `- 任务:${active.length} 进行中(${ready} 就绪)· ${stable} 已闭环`,
82
- `- 实体图:${scope.nodes ?? 0} 节点 · ${scope.links ?? 0} 链接`,
83
- `- 观测:${obs.length} 条信念(累计 ${obsEvidence} 证据)`,
84
- `- 记忆:${scope.memories ?? 0} 条${kindHist ? `(${kindHist})` : ""}`,
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.1",
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);