@modusensus/dsh-mneme 0.7.8 → 0.7.9

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/CONTRIBUTING.md CHANGED
@@ -131,6 +131,13 @@ Versioning follows semantic versioning (`MAJOR.MINOR.PATCH`). Full flow:
131
131
 
132
132
  ---
133
133
 
134
+ ## Contact
135
+
136
+ - **General questions & contributions**: [GitHub Discussions](https://github.com/modusensus/dsh-mneme/discussions) or `work@modusensus.space`
137
+ - **Security vulnerabilities**: report privately via [SECURITY.md](SECURITY.md) — never open a public issue for vulnerabilities
138
+
139
+ ---
140
+
134
141
  ## Miscellaneous
135
142
 
136
143
  - Security issues go through [SECURITY.md](SECURITY.md) or a GitHub Security Advisory — never paste sensitive info into a public Issue.
@@ -271,6 +278,13 @@ DSH 上游仍处于 developer preview 阶段,API 与服务接口变动频繁
271
278
 
272
279
  ---
273
280
 
281
+ ## 联系方式
282
+
283
+ - **一般问题与贡献咨询**:[GitHub Discussions](https://github.com/modusensus/dsh-mneme/discussions) 或 `work@modusensus.space`
284
+ - **安全漏洞**:请通过 [SECURITY.md](SECURITY.md) 私有提交,不要在公开 Issue 中提交漏洞
285
+
286
+ ---
287
+
274
288
  ## 其他
275
289
 
276
290
  - 安全问题请走 [SECURITY.md](SECURITY.md) 或 GitHub Security Advisory,不要在公开 Issue 贴敏感信息。
package/SECURITY.md CHANGED
@@ -65,6 +65,22 @@ The following security features are implemented and maintained in the project:
65
65
  - Your proposed fix (if any)
66
66
  - Whether you are requesting credit / disclosure preferences
67
67
 
68
+ ### Scope and Out of Scope
69
+
70
+ **In scope** — issues this policy covers:
71
+ - Remote API authentication or authorization bypass (`apiToken`-protected surfaces)
72
+ - Memory integrity violations (CAS/fence bypass, unauthorized overwrite, replay paths)
73
+ - Mirror consistency and recovery logic that can cause silent data corruption
74
+ - Exploitable vulnerabilities in shipped runtime dependencies (supported versions only)
75
+
76
+ **Out of scope** — we will not act on:
77
+ - Social engineering or phishing against maintainers or users
78
+ - DoS claims that require unrealistic local resource exhaustion with no product defect
79
+ - Vulnerabilities present only in unsupported versions (`< 0.6.0`)
80
+ - Best-practice hardening suggestions with no demonstrable security impact
81
+
82
+ Not sure whether your finding is in scope? Report it anyway via the private channel — we will triage and respond.
83
+
68
84
  ### Response Timeline
69
85
 
70
86
  | Phase | Time | Action |
@@ -77,6 +93,12 @@ The following security features are implemented and maintained in the project:
77
93
 
78
94
  **Note**: For Critical vulnerabilities, if a full fix cannot be developed within 14 days, we will provide a temporary mitigation or workaround within that timeframe.
79
95
 
96
+ ### Escalation (If You Do Not Hear Back)
97
+
98
+ - If you do not receive an acknowledgment within **48 hours**, re-send to `work@modusensus.space` with subject prefix: `[SECURITY][FOLLOW-UP]`.
99
+ - If there is still no response after another **48 hours**, open a **private security advisory** via [GitHub Security Advisories](https://github.com/modusensus/dsh-mneme/security/advisories) and reference your original report timestamp.
100
+ - Do not disclose details publicly while escalation is in progress.
101
+
80
102
  ### Severity Classification
81
103
 
82
104
  We follow the [CVSS v3.1](https://www.first.org/cvss/v3.1/specification-document) standard:
@@ -322,7 +344,7 @@ All contributors must comply with the following security requirements:
322
344
 
323
345
  ## Contact
324
346
 
325
- - **Security Team**: `guanqishi26@gmail.com`
347
+ - **Security Team**: `work@modusensus.space`
326
348
  - **Private Advisory**: [GitHub Security Advisories](https://github.com/modusensus/dsh-mneme/security/advisories)
327
349
  - **GPG Key**: Available upon request for encrypted communication
328
350
 
@@ -401,6 +423,22 @@ This project is licensed under the **MIT License**. See [LICENSE](https://github
401
423
  - 你建议的修复方案(如有)
402
424
  - 是否要求在公告中署名 / 披露偏好
403
425
 
426
+ ### 受理范围与不受理范围
427
+
428
+ **受理范围**——本政策覆盖以下问题:
429
+ - 远程 API 鉴权 / 授权绕过(`apiToken` 保护面)
430
+ - 记忆完整性破坏(CAS/fence 绕过、未授权覆盖、重放路径)
431
+ - 可导致静默数据损坏的镜像一致性与恢复逻辑缺陷
432
+ - 在受支持版本中可利用的已发布运行时依赖漏洞
433
+
434
+ **不受理范围**——以下问题我们不予处理:
435
+ - 针对维护者或用户的社工 / 钓鱼类问题
436
+ - 仅通过不现实的本地资源耗尽触发、且无产品缺陷支撑的 DoS 声明
437
+ - 仅存在于不支持版本(`< 0.6.0`)的问题
438
+ - 无可证明安全影响的"最佳实践建议"类问题
439
+
440
+ 不确定你的发现是否在受理范围内?仍请通过私有渠道提交——我们会评估并回复。
441
+
404
442
  ### 响应时间线
405
443
 
406
444
  | 阶段 | 时间 | 行动 |
@@ -413,6 +451,12 @@ This project is licensed under the **MIT License**. See [LICENSE](https://github
413
451
 
414
452
  **备注**:对于严重漏洞,如果 14 天内无法提供完整修复,我们将在该时间内提供临时缓解措施或替代方案。
415
453
 
454
+ ### 无响应升级路径
455
+
456
+ - 若 **48 小时内**未收到确认,请重新发送到 `work@modusensus.space`,邮件标题加前缀:`[SECURITY][FOLLOW-UP]`。
457
+ - 若再次等待 **48 小时**仍无回复,请通过 [GitHub 私有安全公告](https://github.com/modusensus/dsh-mneme/security/advisories) 提交,并注明首次报告时间。
458
+ - 升级处理中请勿公开披露漏洞细节。
459
+
416
460
  ### 严重程度分级
417
461
 
418
462
  我们遵循 [CVSS v3.1](https://www.first.org/cvss/v3.1/specification-document) 标准:
@@ -658,7 +702,7 @@ dsh-mneme:
658
702
 
659
703
  ## 联系方式
660
704
 
661
- - **安全团队**:`guanqishi26@gmail.com`
705
+ - **安全团队**:`work@modusensus.space`
662
706
  - **私有公告**:[GitHub Security Advisories](https://github.com/modusensus/dsh-mneme/security/advisories)
663
707
  - **GPG 密钥**:如需加密通信,可应请求提供
664
708
 
@@ -670,5 +714,5 @@ dsh-mneme:
670
714
 
671
715
  ---
672
716
 
673
- *Last updated: 2026-08-28*
674
- *Policy version: 2.2*
717
+ *Last updated: 2026-09-06*
718
+ *Policy version: 2.3*
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.9] - 2026-09-06
4
+
5
+ ### 🐛 修复:v0.7.8 的 rc.1 snapshotEvents 适配未同步到 lib/(issue #65)
6
+
7
+ - **现象**:v0.7.8 为兼容 DSH 0.1.2-rc.1(`Session.events` → `snapshotEvents()`)只改了 `src/inject.js` 与 `src/summarize.js`,发布产物 `lib/` 从未同步——npm 包实际加载的是 `lib/`(`main` 指向 `lib/index.js`),用户在 rc.1 下安装 0.7.8 运行的仍是未适配代码。因代码带 `?.` / `?? []` 容错,**不报错但热记忆注入与会话摘要静默失效**。
8
+ - **修复**:将 src 的兼容垫片同步到 `lib/`(两文件 3 处),`src/` 与 `lib/` 逐文件一致。
9
+ - **防再犯(双保险)**:
10
+ - 新增 `scripts/check-sync.js`:断言 `src/` 与 `lib/` 一致性,root `prepack` 钩子调用——发布前不一致直接 fail;
11
+ - 新增 `test/lib-smoke.test.js`:从 `lib/` 直接导入复跑 snapshotEvents 关键用例 + 静态断言 src→lib 逐文件一致,CI 每次全量测试拦截。
12
+ - 新增 3 个用例(lib 版 snapshotEvents 注入/摘要 + 一致性断言),全套 **815 通过**。
13
+
3
14
  ## [0.7.8] - 2026-09-06
4
15
 
5
16
  ### 🐛 修复:DSH 0.1.2-rc.1 兼容(issues #58 #59)
@@ -8,7 +8,7 @@ import { createHotMemory } from "./hot-memory.js";
8
8
  // falls back to the legacy rule-based pick, never breaking the render.
9
9
  function lastUserQuery(ctx) {
10
10
  try {
11
- const events = ctx?.agent?.session?.events;
11
+ const events = ctx?.agent?.session?.snapshotEvents?.() ?? ctx?.agent?.session?.events;
12
12
  if (!Array.isArray(events) || events.length === 0) return "";
13
13
  for (let i = events.length - 1; i >= 0; i--) {
14
14
  const event = events[i];
@@ -34,7 +34,7 @@ function lastUserQuery(ctx) {
34
34
  // returns [] on any failure, and the hot block simply does not render.
35
35
  function extractRounds(ctx, maxRounds) {
36
36
  try {
37
- const events = ctx?.agent?.session?.events;
37
+ const events = ctx?.agent?.session?.snapshotEvents?.() ?? ctx?.agent?.session?.events;
38
38
  if (!Array.isArray(events) || events.length === 0) return [];
39
39
  const rounds = [];
40
40
  let pendingQuery = null;
@@ -78,7 +78,9 @@ function toProtocolChunk(chunk) {
78
78
  // check below.
79
79
  function collectMessages(session) {
80
80
  const messages = [];
81
- for (const event of session.events ?? []) {
81
+ // DSH 0.1.2-rc.1 Session 改用 snapshotEvents(),兼容旧版 .events
82
+ const events = session.snapshotEvents?.() ?? session.events ?? [];
83
+ for (const event of events) {
82
84
  const kind = event.data?.source?.kind;
83
85
  if (event.type !== "user/message") continue;
84
86
  if (kind !== undefined && kind !== "user") continue;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@modusensus/dsh-mneme",
3
3
  "description": "Cross-session memory plugin for DeepSeek Harness with autoDream consolidation: SQLite store, Markdown mirrors, 7 model tools, automatic injection, session summarization, user profile/rules, custom slash commands, vector (semantic) search, and a Web GUI panel",
4
- "version": "0.7.8",
4
+ "version": "0.7.9",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -0,0 +1,42 @@
1
+ // 发布前校验 src/ 与 lib/ 一致性。
2
+ //
3
+ // npm 包实际加载 lib/(package main → lib/index.js),而 root 层 `npm publish`
4
+ // 不触发 dsh-mneme/ 的 prepack → sync 同步。历史教训:PR #60 只改了 src 忘了同步
5
+ // lib,v0.7.8 发出去的 npm 包跑的还是旧代码(issue #65)。此脚本让这类漂移在发布时
6
+ // 直接 fail,而不是带着旧产物上线。
7
+ //
8
+ // 用法:node scripts/check-sync.js (root package.json 的 prepack 钩子自动调用)
9
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
10
+ import { join, relative } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+
13
+ const root = join(fileURLToPath(new URL("..", import.meta.url))); // dsh-mneme/
14
+ const srcDir = join(root, "src");
15
+ const libDir = join(root, "lib");
16
+
17
+ function walk(dir) {
18
+ const out = [];
19
+ for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
20
+ const full = join(dir, entry.name);
21
+ if (entry.isDirectory()) out.push(...walk(full));
22
+ else if (entry.isFile()) out.push(full);
23
+ }
24
+ return out;
25
+ }
26
+
27
+ const bad = [];
28
+ for (const file of walk(srcDir)) {
29
+ const rel = relative(srcDir, file);
30
+ const dest = join(libDir, rel);
31
+ if (!existsSync(dest)) {
32
+ bad.push(`missing lib/${rel}`);
33
+ } else if (!readFileSync(file).equals(readFileSync(dest))) {
34
+ bad.push(`differ lib/${rel}`);
35
+ }
36
+ }
37
+ if (bad.length) {
38
+ console.error(`✗ src/ 与 lib/ 不一致(${bad.length} 处)——npm 包实际加载 lib/,请先 npm run sync 并提交:`);
39
+ for (const line of bad) console.error(` ${line}`);
40
+ process.exit(1);
41
+ }
42
+ console.log(`✓ src/ 与 lib/ 一致(${walk(srcDir).length} 个文件)`);
@@ -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.9",
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": {