@modusensus/dsh-mneme 0.7.8 → 0.7.10

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.
@@ -67,13 +67,24 @@ test("tab-first activation with hero-screen overlay fallback", () => {
67
67
  "a failed tab activation must open the fallback overlay"
68
68
  );
69
69
  assert.ok(
70
- /if \(!tab\) \{ resolve\(false\); return; \}/.test(clientSource),
70
+ /if \(candidates\.length === 0\) \{ resolve\(false\); return; \}/.test(clientSource),
71
71
  "activation must resolve false when the tab ring is absent (hero screen)"
72
72
  );
73
73
  assert.ok(
74
74
  /aria-selected.*true/.test(clientSource),
75
75
  "activation is only confirmed once the host marks the tab selected"
76
76
  );
77
+ // Same-labelled tabs from other plugins must never be activated by mistake:
78
+ // the click is only trusted once OUR explorer view actually rendered, and
79
+ // hidden tab panes are excluded before any click happens.
80
+ assert.ok(
81
+ /querySelector\("\.mneme-x"\) !== null/.test(clientSource),
82
+ "activation must verify the memory explorer rendered, not just the tab state"
83
+ );
84
+ assert.ok(
85
+ /offsetParent === null/.test(clientSource),
86
+ "hidden tab panes (settings dialogs etc.) must be excluded from activation"
87
+ );
77
88
  });
78
89
 
79
90
  // The fallback overlay reuses the full MemoryExplorer (three columns, graph,
@@ -0,0 +1,109 @@
1
+ // lib/ 是 npm 实际加载的产物(package main 指向 lib/index.js),而现有测试只引 src/,
2
+ // 覆盖不到发布产物——issue #65 就是 src 适配了、lib 没同步,npm 包静默跑旧代码。
3
+ // 本文件直接从 lib/ 导入,复跑 DSH 0.1.2-rc.1 snapshotEvents 关键用例,
4
+ // 并静态断言 src → lib 逐文件一致,防止同类回归再次发生。
5
+ import test from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
9
+ import { join, relative } from "node:path";
10
+ import { createStore } from "../lib/store.js";
11
+ import { createService } from "../lib/service.js";
12
+ import { createInjector } from "../lib/inject.js";
13
+ import { createSettings } from "../lib/settings.js";
14
+ import { createSummarizer, parseSummaryJson } from "../lib/summarize.js";
15
+
16
+ function setup(over = {}) {
17
+ const store = createStore(":memory:");
18
+ const service = createService({ store, mirror: null, config: {} });
19
+ const settings = createSettings(store.db);
20
+ const contexts = [];
21
+ const ctx = {
22
+ systemPrompt: {
23
+ context(def) {
24
+ contexts.push(def);
25
+ return () => {};
26
+ }
27
+ }
28
+ };
29
+ const config = { maxInjectedItems: 3, importanceThreshold: 3, ...over };
30
+ const injector = createInjector(ctx, service, settings, config);
31
+ return { contexts, injector };
32
+ }
33
+
34
+ test("lib: session with only snapshotEvents() (DSH 0.1.2-rc.1) still renders hot context", () => {
35
+ const { contexts } = setup();
36
+ const text = contexts[0].text({
37
+ agent: {
38
+ session: {
39
+ id: "s1",
40
+ snapshotEvents: () => [
41
+ { type: "user/message", data: { source: { kind: "user" }, content: ["用快照接口提问"] } },
42
+ { type: "assistant/message", data: { source: { kind: "assistant" }, content: ["快照返回的答复"] } }
43
+ ]
44
+ }
45
+ }
46
+ });
47
+ assert.ok(text.includes("[短期上下文]"), "hot context rendered");
48
+ assert.ok(text.includes("用快照接口提问"), "user query picked up from snapshotEvents");
49
+ assert.ok(text.includes("快照返回的答复"), "assistant reply picked up from snapshotEvents");
50
+ });
51
+
52
+ test("lib: session with only snapshotEvents() (DSH 0.1.2-rc.1) still summarizes", async () => {
53
+ const store = createStore(":memory:");
54
+ const service = createService({ store, mirror: null, config: {} });
55
+ const events = [];
56
+ const ctx = {
57
+ on(name, fn) {
58
+ events.push({ name, fn });
59
+ return () => {};
60
+ },
61
+ llm: {
62
+ stream() {
63
+ const json = JSON.stringify([
64
+ { type: "decision", title: "选型", content: "确定用 node:sqlite", importance: 4 },
65
+ { type: "preference", title: "语言", content: "用户喜欢中文交流", importance: 5 }
66
+ ]);
67
+ return (async function* () {
68
+ yield { type: "finish", kind: "ok" };
69
+ yield { type: "block-start", block: { type: "text" } };
70
+ yield { type: "text-delta", delta: json };
71
+ yield { type: "block-end", block: { type: "text" } };
72
+ })();
73
+ }
74
+ }
75
+ };
76
+ const summarizer = createSummarizer(ctx, service, { autoSummarize: true });
77
+ const handler = events.find((e) => e.name === "session/event").fn;
78
+ const session = {
79
+ id: "s6",
80
+ requestHeader: () => ({ config: { provider: "deepseek", model: "deepseek-chat" } }),
81
+ snapshotEvents: () => [{ type: "user/message", data: { content: ["用快照接口提问"] } }, { seq: 2, type: "turn/end" }]
82
+ };
83
+ await handler(session, { seq: 2, type: "turn/end" });
84
+ assert.equal(store.count(), 2);
85
+ assert.ok(store.all().some((m) => m.type === "decision"));
86
+ assert.ok(store.all().some((m) => m.type === "preference"));
87
+ summarizer.dispose();
88
+ });
89
+
90
+ test("lib/ mirrors src/ — every src file identical in lib (npm loads lib!)", () => {
91
+ const srcRoot = fileURLToPath(new URL("../src/", import.meta.url));
92
+ const libRoot = fileURLToPath(new URL("../lib/", import.meta.url));
93
+ const mismatches = [];
94
+ const walk = (dir) => {
95
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
96
+ const full = join(dir, entry.name);
97
+ if (entry.isDirectory()) walk(full);
98
+ else if (entry.isFile()) {
99
+ const rel = relative(srcRoot, full);
100
+ const counterpart = join(libRoot, rel);
101
+ if (!existsSync(counterpart) || !readFileSync(full).equals(readFileSync(counterpart))) {
102
+ mismatches.push(rel);
103
+ }
104
+ }
105
+ }
106
+ };
107
+ walk(srcRoot);
108
+ assert.deepEqual(mismatches, [], "src 文件在 lib 缺失或内容不一致——请先 npm run sync 并提交");
109
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modusensus/dsh-mneme",
3
- "version": "0.7.8",
3
+ "version": "0.7.10",
4
4
  "description": "Structured memory engine for DeepSeek Harness. Offline semantic search, entity-attribute-timeline, autoDream self-consolidation, and human-editable Markdown storage.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -45,6 +45,7 @@
45
45
  }
46
46
  },
47
47
  "scripts": {
48
+ "prepack": "node dsh-mneme/scripts/check-sync.js",
48
49
  "prepublishOnly": "node -e \"const fs=require('fs');const v=JSON.parse(fs.readFileSync('./dsh-mneme/package.json','utf8')).version;const p=JSON.parse(fs.readFileSync('./package.json','utf8'));p.version=v;fs.writeFileSync('./package.json',JSON.stringify(p,null,2)+'\\n')\""
49
50
  },
50
51
  "peerDependencies": {