@modusensus/dsh-mneme 0.7.7 → 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/CHANGELOG.md +1 -1
- package/CONTRIBUTING.md +46 -0
- package/README.md +6 -3
- package/SECURITY.md +48 -4
- package/dsh-mneme/CHANGELOG.md +26 -6
- package/dsh-mneme/README.md +8 -6
- package/dsh-mneme/lib/inject.js +2 -2
- package/dsh-mneme/lib/summarize.js +3 -1
- package/dsh-mneme/package.json +1 -1
- package/dsh-mneme/scripts/check-sync.js +42 -0
- package/dsh-mneme/src/inject.js +2 -2
- package/dsh-mneme/src/summarize.js +3 -1
- package/dsh-mneme/test/inject.test.js +18 -0
- package/dsh-mneme/test/lib-smoke.test.js +109 -0
- package/dsh-mneme/test/summarize.test.js +15 -0
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
> **完整版本历史见 [dsh-mneme/CHANGELOG.md](dsh-mneme/CHANGELOG.md)**(正式版本正源,0.3.8 → 0.7.
|
|
3
|
+
> **完整版本历史见 [dsh-mneme/CHANGELOG.md](dsh-mneme/CHANGELOG.md)**(正式版本正源,0.3.8 → 0.7.8)。以下为仓库早期开发记录(0.1.0–0.2.x,2026-08-13~14,已归档)。
|
|
4
4
|
|
|
5
5
|
All notable changes to dsh-mneme are documented here.
|
|
6
6
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -102,6 +102,22 @@ Common scripts (all run under `dsh-mneme/`):
|
|
|
102
102
|
|
|
103
103
|
---
|
|
104
104
|
|
|
105
|
+
## Scope: Platform Adaptation & Wrapper PRs
|
|
106
|
+
|
|
107
|
+
DSH upstream is still in developer preview — APIs and service interfaces change frequently. Until DSH reaches a stable release (RC or GA), **PRs that adapt dsh-mneme to secondary platforms or wrap it into other hosts are not a priority** and are reviewed with extra caution:
|
|
108
|
+
|
|
109
|
+
- Desktop shells (e.g. a Tauri/Electron wrapper around `dsh web`)
|
|
110
|
+
- Standalone CLI packaging
|
|
111
|
+
- Ports/wrappers of dsh-mneme into other plugin ecosystems
|
|
112
|
+
|
|
113
|
+
These tend to bind against unstable upstream APIs: a single upstream change can break them, and the maintenance burden falls back on this project. The currently supported platform is the **Web Profile** (`dsh web`).
|
|
114
|
+
|
|
115
|
+
Exceptions: if a contributor is willing to **own long-term maintenance** (track upstream changes and fix breakage), open a Discussion first to scope the work — maintainers will evaluate and review the PR.
|
|
116
|
+
|
|
117
|
+
Bug-fix PRs for existing desktop compatibility issues are still welcome.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
105
121
|
## Release Process (Maintainers)
|
|
106
122
|
|
|
107
123
|
Versioning follows semantic versioning (`MAJOR.MINOR.PATCH`). Full flow:
|
|
@@ -115,6 +131,13 @@ Versioning follows semantic versioning (`MAJOR.MINOR.PATCH`). Full flow:
|
|
|
115
131
|
|
|
116
132
|
---
|
|
117
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
|
+
|
|
118
141
|
## Miscellaneous
|
|
119
142
|
|
|
120
143
|
- Security issues go through [SECURITY.md](SECURITY.md) or a GitHub Security Advisory — never paste sensitive info into a public Issue.
|
|
@@ -226,6 +249,22 @@ npm run test:coverage # c8 覆盖率
|
|
|
226
249
|
|
|
227
250
|
---
|
|
228
251
|
|
|
252
|
+
## 范围:平台适配与封装 PR
|
|
253
|
+
|
|
254
|
+
DSH 上游仍处于 developer preview 阶段,API 与服务接口变动频繁。在 DSH 稳定(RC 或正式版)之前,以下方向的 PR **不作为优先项**,且会以额外谨慎的态度审查:
|
|
255
|
+
|
|
256
|
+
- 桌面端适配(例如基于 `dsh web` 的 Tauri/Electron 桌面壳)
|
|
257
|
+
- 独立 CLI 封装
|
|
258
|
+
- 把 dsh-mneme 移植/封装到其他插件体系
|
|
259
|
+
|
|
260
|
+
这类 PR 往往绑定不稳定的上游接口——上游一次变动就可能使其失效,而维护责任会落到本项目头上。当前唯一受支持的平台是 **Web Profile**(`dsh web`)。
|
|
261
|
+
|
|
262
|
+
例外:如果贡献者愿意**承担长期维护**(跟进上游变更并修复 break),请先开 Discussion 沟通范围,维护者会评估并 review 该 PR。
|
|
263
|
+
|
|
264
|
+
针对现有 desktop 兼容问题的 **bug 修复 PR 依然欢迎**。
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
229
268
|
## 发布流程(维护者)
|
|
230
269
|
|
|
231
270
|
版本号遵循语义化版本(`MAJOR.MINOR.PATCH`)。完整流程:
|
|
@@ -239,6 +278,13 @@ npm run test:coverage # c8 覆盖率
|
|
|
239
278
|
|
|
240
279
|
---
|
|
241
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
|
+
|
|
242
288
|
## 其他
|
|
243
289
|
|
|
244
290
|
- 安全问题请走 [SECURITY.md](SECURITY.md) 或 GitHub Security Advisory,不要在公开 Issue 贴敏感信息。
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-3E63DD?style=flat-square" alt="license"></a>
|
|
11
11
|
<a href="https://github.com/modusensus/dsh-mneme/actions"><img src="https://img.shields.io/github/actions/workflow/status/modusensus/dsh-mneme/test.yml?style=flat-square&label=CI" alt="CI"></a>
|
|
12
12
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-24%2B-3E63DD?style=flat-square&logo=nodedotjs&logoColor=white" alt="node"></a>
|
|
13
|
-
<a href="https://github.com/modusensus/dsh-mneme"><img src="https://img.shields.io/badge/tests-
|
|
13
|
+
<a href="https://github.com/modusensus/dsh-mneme"><img src="https://img.shields.io/badge/tests-812%20passed-3E63DD?style=flat-square" alt="tests"></a>
|
|
14
14
|
<a href="https://codecov.io/gh/modusensus/dsh-mneme"><img src="https://img.shields.io/codecov/c/github/modusensus/dsh-mneme/main?style=flat-square" alt="coverage"></a>
|
|
15
15
|
<a href="https://github.com/awesome-dsh-plugin/awesome-dsh-plugin"><img src="https://awesome-dsh-plugin.com/badge.svg" alt="Awesome"></a>
|
|
16
16
|
</p>
|
|
@@ -110,6 +110,7 @@ dsh web
|
|
|
110
110
|
| **v0.7.5** | 分层记忆类型 user/fact + Web 总览视图 + stats 端点 | ✅ |
|
|
111
111
|
| **v0.7.6** | issue #48 修复:截断/前缀 id 也能精确操作(统一 resolveMemoryId)+ client.js 改 src 正源 | ✅ |
|
|
112
112
|
| **v0.7.7** | issue #23 图谱回填:sleep 批量实体抽取 phase + node:sqlite 兼容修复 | ✅ |
|
|
113
|
+
| **v0.7.8** | issues #58 #59 修复:DSH 0.1.2-rc.1 兼容——Session.events 改为 snapshotEvents() 垫片,autoSummarize 与 hot-context 注入恢复 | ✅ |
|
|
113
114
|
| **v0.8.0** | 图谱增强:兴趣漂移可视化 + scope 隔离(issue #17)+ 跨 workspace 共享 | 🚧 计划中(9 月末) |
|
|
114
115
|
|
|
115
116
|
## 🧪 本地开发
|
|
@@ -117,7 +118,7 @@ dsh web
|
|
|
117
118
|
```bash
|
|
118
119
|
cd dsh-mneme
|
|
119
120
|
npm install
|
|
120
|
-
npm test #
|
|
121
|
+
npm test # 812 个测试
|
|
121
122
|
npm run stress # 三轴线压测
|
|
122
123
|
npm run sync # src → lib 同步
|
|
123
124
|
```
|
|
@@ -217,6 +218,8 @@ Works out of the box. Enable these as needed:
|
|
|
217
218
|
| **v0.7.4** | Issue #40 prompt-brace escaping + Issue #41 overlay close-button overlap fix | ✅ |
|
|
218
219
|
| **v0.7.5** | Layered memory types (user/fact) + Overview view + stats endpoint | ✅ |
|
|
219
220
|
| **v0.7.6** | Issue #48 fix: truncated/prefix ids resolve for exact ops (unified resolveMemoryId) + client.js now src-authored | ✅ |
|
|
221
|
+
| **v0.7.7** | Issue #23 graph backfill: sleep batch entity extraction phase + node:sqlite compat fix | ✅ |
|
|
222
|
+
| **v0.7.8** | Issues #58 #59 fix: DSH 0.1.2-rc.1 compat — Session.events moved to snapshotEvents() shim; autoSummarize & hot-context injection restored | ✅ |
|
|
220
223
|
| **v0.8.0** | Graph enhancement: interest-drift visualization + scope isolation (issue #17) + cross-workspace sharing | 🚧 Planned (late Sep) |
|
|
221
224
|
|
|
222
225
|
## 🧪 Local Development
|
|
@@ -224,7 +227,7 @@ Works out of the box. Enable these as needed:
|
|
|
224
227
|
```bash
|
|
225
228
|
cd dsh-mneme
|
|
226
229
|
npm install
|
|
227
|
-
npm test #
|
|
230
|
+
npm test # 812 tests
|
|
228
231
|
npm run stress # three-axis stress test
|
|
229
232
|
npm run sync # src → lib sync
|
|
230
233
|
```
|
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**: `
|
|
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
|
-
- **安全团队**:`
|
|
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-
|
|
674
|
-
*Policy version: 2.
|
|
717
|
+
*Last updated: 2026-09-06*
|
|
718
|
+
*Policy version: 2.3*
|
package/dsh-mneme/CHANGELOG.md
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
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
|
+
|
|
14
|
+
## [0.7.8] - 2026-09-06
|
|
15
|
+
|
|
16
|
+
### 🐛 修复:DSH 0.1.2-rc.1 兼容(issues #58 #59)
|
|
17
|
+
|
|
18
|
+
- **现象**:DSH 0.1.2-rc.1 起官方移除 `Session.events` 属性、改为 `snapshotEvents()` 方法。autoSummarize(会话摘要)与 hot-context(短期上下文)注入都依赖 `session.events` 读取会话事件,升级后取到 undefined 而**静默失效**——会话摘要不再落库、短期上下文块不再渲染。
|
|
19
|
+
- **修复**:`src/summarize.js` 与 `src/inject.js` 两处取事件统一改为兼容垫片 `session.snapshotEvents?.() ?? session.events`——0.1.2-rc.1 及以后走新方法,更早 0.1.x 仍走 `.events`,**新旧 DSH 通吃**。
|
|
20
|
+
- **说明**:兼容垫片,老版本 DSH(0.1.x 早期)行为完全不受影响,无需任何配置改动。
|
|
21
|
+
- 新增 2 个回归用例(会话只提供 `snapshotEvents()` 时 hot-context 渲染与会话摘要均恢复),全套 **812 通过**。
|
|
22
|
+
|
|
3
23
|
## [0.7.7] - 2026-09-05
|
|
4
24
|
|
|
5
25
|
### 🆕 issue #23:sleep 批量实体抽取回填实体图谱
|
|
6
26
|
|
|
7
27
|
- **背景**:写路径抽取(index.js)只有 `entityExtractionEnabled` 开启才触发——每次写入一次 LLM 调用,是刻意的成本取舍——所以默认安装下记忆从不累积实体,这正是 issue #23 报的"实体图谱面板一片空白"(用户明明有很多记忆)。
|
|
8
28
|
- **新增 sleep 批量抽取 phase**:默认关 `sleepEntityExtractionEnabled`,开睡循环时按**最老优先**把没有实体属性的记忆逐条过 `extractEntities`(每轮上限 `sleepEntityExtractionMaxPerRun` 默认 20)。
|
|
9
|
-
- **下沉为有界 SQL
|
|
10
|
-
-
|
|
11
|
-
- **metadata
|
|
12
|
-
-
|
|
13
|
-
- **node:sqlite
|
|
14
|
-
- 新增 9 个用例(禁用/跳过、stamp 不重复、无实体也 stamp、backfill、失败重试、metadata merge
|
|
29
|
+
- **下沉为有界 SQL 查询**:新增 `store.listForEntityExtraction({limit, offset})`,`WHERE` 只留行级条件(未归档/未遗忘/非 summary/内容非空/`metadata.entity_extracted_at` 为空),`ORDER BY created_at LIMIT ? OFFSET ?` 分页取最老候选;实体是否已存在是跨表检查(`getAttrsByMemory`),由 JS 在每页上过滤。不再 `service.all()` 全表加载。
|
|
30
|
+
- **幂等防重**:抽取前先 stamp `metadata.pending_extracted_at`,成功后替换为 `entity_extracted_at`,失败清除 pending——一条记忆不会被并发/崩溃重复抽取,失败的下轮重试。
|
|
31
|
+
- **metadata 合并不覆盖**:`store.setMemoryMetadata` 改为 merge 语义,打实体时间戳不会抹掉其他路径刚写入的 metadata 字段。
|
|
32
|
+
- **失败不阻断**:单条抽取失败只跳过该条、记入 `failed` 计数,整轮状态由 `deriveStatus` 正确折叠 `failed`——一次 LLM 抖动不会 abort 整个 sleep 周期。
|
|
33
|
+
- **node:sqlite 兼容修复**:初版实现用 better-sqlite3 的 `.pluck()`(node:sqlite 不存在),改为 `.all().map(row => getById(row.id))`;`listForEntityExtraction` 的 `forgotten IS NULL` 条件与 `save` 硬编码写入的 `forgotten=0` 永不匹配导致查询恒空,修正为 `(forgotten = 0 OR forgotten IS NULL)`(与 archived 同构)。
|
|
34
|
+
- 新增 9 个用例(禁用/跳过、stamp 不重复、无实体也 stamp、backfill、失败重试、metadata merge、pending 清除、failed 状态折叠、分页最老优先),全套 **810 通过**。
|
|
15
35
|
|
|
16
36
|
## [0.7.6] - 2026-09-02
|
|
17
37
|
|
package/dsh-mneme/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/@modusensus/dsh-mneme)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
8
|
-
[](https://github.com/modusensus/dsh-mneme)
|
|
9
9
|
[](https://github.com/modusensus/dsh-mneme/actions)
|
|
10
10
|
[](https://nodejs.org)
|
|
11
11
|
[](https://www.npmjs.com/package/@modusensus/dsh-mneme)
|
|
@@ -241,7 +241,8 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
|
|
|
241
241
|
|
|
242
242
|
| 版本 | 亮点 |
|
|
243
243
|
|------|------|
|
|
244
|
-
| **v0.7.
|
|
244
|
+
| **v0.7.8** | DSH 0.1.2-rc.1 兼容(issues #58 #59):官方移除 `Session.events` 属性改为 `snapshotEvents()` 方法,autoSummarize 与 hot-context(短期上下文)注入取不到事件而失效;改用兼容垫片 `session.snapshotEvents?.() ?? session.events`,新旧 DSH 通吃,老版本行为不受影响;新增 2 个回归用例;812 测试全绿 |
|
|
245
|
+
| **v0.7.7** | issue #23 实体图谱回填:sleep 批量实体抽取 phase(`sleepEntityExtractionEnabled` 默认关;最老优先、SQL LIMIT/OFFSET 分页下沉为有界查询不整表扫描;`pending_extracted_at` 幂等防重、成功/失败清除;metadata 合并不覆盖其他路径写入);node:sqlite 兼容修复(pluck→all+map、`forgotten=0` 查询条件);810 测试全绿 |
|
|
245
246
|
| **v0.7.6** | issue #48 修复:`memory_update`/`memory_delete`/`memory_forget`/`memory_archive` 支持截断/前缀短 id(新增 `service.resolveMemoryId`:精确命中优先 + 唯一前缀解析 + 多命中拒绝列出候选 + `memory_delete` 未命中幂等补 `logger.warn`;纯通配符/空白兜底);Web bundle `client.js` 改 src 正源;801 测试全绿 |
|
|
246
247
|
| **v0.7.5** | 分层记忆类型:新增 `user`(用户画像)/`fact`(原子事实)轻量记忆类型(单表 `type` 扩展,不动 schema)+ Web 面板「总览」视图(记忆分层卡片 + 用户画像卡 + 类型分布 + 近 7 天趋势)+ `/api/dsh-mneme/stats` 统计端点;kimi-k2.7-code 复验(days 整数化等);790 测试全绿 |
|
|
247
248
|
| **v0.7.4** | issue #40 修复:记忆内容含 `{{...}}` 模板语法时整轮崩溃(注入边界 run-based 花括号转义 `{{a}}`→`{\{a\}\}`,奇数连续如 `{{{a}}}` 也不残留字面 `{{`;新增 `escapePromptVariables` 配置默认开);issue #41 修复:记忆窗口关闭按钮与宿主窗口控制按钮重叠无法点击(顶栏左对齐,关闭按钮离开右上角宿主控制区);782 测试全绿 |
|
|
@@ -297,7 +298,8 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
|
|
|
297
298
|
| **v0.7.3** | ✅ 完成 | issue #38 新功能 | 左下角入口按钮可选开关 `showSidebarTrigger`(默认开,settings-over-config);Web 面板设置一键关闭,与 dsh-cost-meter 等 footer 插件冲突可隐藏按钮、记忆库标签不受影响;776 测试全绿 |
|
|
298
299
|
| **v0.7.4** | ✅ 完成 | issue #40 + #41 修复 | 注入边界 run-based 花括号转义(`{{a}}`→`{\{a\}\}`、奇数连续如 `{{{a}}}` 不残留字面,`escapePromptVariables` 默认开)+ 记忆窗口顶栏左对齐、关闭按钮避开宿主窗口控制按钮区;782 测试全绿 |
|
|
299
300
|
| **v0.7.5** | ✅ 完成 | 分层记忆类型 + 总览视图 | 借鉴 meow-memory 分层概念、贴合单表架构:新增 `user`(用户画像)/`fact`(原子事实)类型,注入/镜像/梦境/质量过滤全链路打通;Web 面板「总览」视图(分层卡片 + 用户画像卡 + 类型分布 + 近 7 天趋势);`/api/dsh-mneme/stats` 端点;kimi-k2.7-code 复验;790 测试全绿 |
|
|
300
|
-
| **v0.7.7** | ✅ 完成 | issue #23 图谱回填 | sleep 批量实体抽取 phase:默认关 `sleepEntityExtractionEnabled`,按最老优先、SQL LIMIT/OFFSET 分页(下沉为有界查询,不再整表扫描)批量抽取未打标记忆的实体(修复 issue #23 实体图谱空白);`pending_extracted_at` 幂等防重、成功/失败清除,metadata 合并不覆盖;
|
|
301
|
+
| **v0.7.7** | ✅ 完成 | issue #23 图谱回填 | sleep 批量实体抽取 phase:默认关 `sleepEntityExtractionEnabled`,按最老优先、SQL LIMIT/OFFSET 分页(下沉为有界查询,不再整表扫描)批量抽取未打标记忆的实体(修复 issue #23 实体图谱空白);`pending_extracted_at` 幂等防重、成功/失败清除,metadata 合并不覆盖;node:sqlite 兼容修复(pluck→all+map、`forgotten=0` 查询条件);810 测试全绿 |
|
|
302
|
+
| **v0.7.8** | ✅ 完成 | DSH 0.1.2-rc.1 兼容(issues #58 #59) | 官方移除 `Session.events` 属性、改用 `snapshotEvents()` 方法后 autoSummarize 与 hot-context 注入失效;改为兼容垫片 `session.snapshotEvents?.() ?? session.events`,新旧 DSH 通吃,老版本不受影响;新增 2 个回归用例;812 测试全绿 |
|
|
301
303
|
| **v0.7.6** | ✅ 完成 | issue #48 修复 | 四工具统一 `service.resolveMemoryId`:截断/前缀短 id 也能精确操作(精确命中优先、唯一前缀解析、多命中拒绝并列出候选、`memory_delete` 未命中幂等返回 + `logger.warn` 留痕);Web bundle `client.js` 改 src 正源;801 测试全绿 |
|
|
302
304
|
| **v0.8.0** | 🚧 计划中(9 月末) | 图谱增强 | 兴趣漂移可视化 + scope 隔离(issue #17)+ 跨 workspace 记忆共享 + 更多 heat 信号 |
|
|
303
305
|
|
|
@@ -307,7 +309,7 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带
|
|
|
307
309
|
|
|
308
310
|
### 前置条件
|
|
309
311
|
|
|
310
|
-
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH
|
|
312
|
+
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)— 兼容 DSH 0.1.x,已验证 0.1.2-rc.1(`Session.events` → `snapshotEvents()` 变更已由插件垫片兼容,新旧版本通吃)
|
|
311
313
|
- Node 24+(`node:sqlite`)
|
|
312
314
|
|
|
313
315
|
### 安装步骤
|
|
@@ -464,7 +466,7 @@ src/
|
|
|
464
466
|
├── client.js # Web 面板 bundle(ModuleLoader 自注册;v0.7.6 起 src 正源)
|
|
465
467
|
└── index.js # 插件接线
|
|
466
468
|
lib/ # src 的同步分发产物(npm run sync,prepack 自动执行;无手写例外)
|
|
467
|
-
test/ #
|
|
469
|
+
test/ # 812 个 node:test 测试(含审计与三轴线压测不变量)
|
|
468
470
|
scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压测 · sync-lib.js 同步 · benchmark-recall.js 召回基准
|
|
469
471
|
```
|
|
470
472
|
|
|
@@ -473,7 +475,7 @@ scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压
|
|
|
473
475
|
```bash
|
|
474
476
|
cd dsh-mneme
|
|
475
477
|
npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试)
|
|
476
|
-
npm test # 运行
|
|
478
|
+
npm test # 运行 812 个测试
|
|
477
479
|
npm run stress # 三轴线压测:长会话检索 / 冲突仲裁 / 多 Agent 并发(离线 mock LLM)
|
|
478
480
|
npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行)
|
|
479
481
|
```
|
package/dsh-mneme/lib/inject.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dsh-mneme/package.json
CHANGED
|
@@ -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.
|
|
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} 个文件)`);
|
package/dsh-mneme/src/inject.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
|
@@ -179,6 +179,24 @@ test("issue #40: hot-context rounds with {{...}} are escaped", () => {
|
|
|
179
179
|
assert.ok(!text.includes("}}"), "no raw }} in hot context");
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
test("session with only snapshotEvents() (DSH 0.1.2-rc.1) still renders hot context", () => {
|
|
183
|
+
const { contexts } = setup();
|
|
184
|
+
const text = contexts[0].text({
|
|
185
|
+
agent: {
|
|
186
|
+
session: {
|
|
187
|
+
id: "s1",
|
|
188
|
+
snapshotEvents: () => [
|
|
189
|
+
{ type: "user/message", data: { source: { kind: "user" }, content: ["用快照接口提问"] } },
|
|
190
|
+
{ type: "assistant/message", data: { source: { kind: "assistant" }, content: ["快照返回的答复"] } }
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
assert.ok(text.includes("[短期上下文]"), "hot context rendered");
|
|
196
|
+
assert.ok(text.includes("用快照接口提问"), "user query picked up from snapshotEvents");
|
|
197
|
+
assert.ok(text.includes("快照返回的答复"), "assistant reply picked up from snapshotEvents");
|
|
198
|
+
});
|
|
199
|
+
|
|
182
200
|
test("issue #40: escapePromptVariables=false passes {{...}} through verbatim", () => {
|
|
183
201
|
const { contexts, service } = setup({ escapePromptVariables: false });
|
|
184
202
|
service.saveWithDedupe({ type: "preference", title: "模板", content: "{{挖空}} 与 {{hl|term}}", importance: 5 });
|
|
@@ -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
|
+
});
|
|
@@ -85,6 +85,21 @@ test("turn/end event triggers summarization and stores entries", async () => {
|
|
|
85
85
|
assert.ok(all.some((m) => m.type === "preference"));
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
test("session with only snapshotEvents() (DSH 0.1.2-rc.1) still summarizes", async () => {
|
|
89
|
+
const { events, store } = setup();
|
|
90
|
+
const handler = events.find((e) => e.name === "session/event").fn;
|
|
91
|
+
const session = {
|
|
92
|
+
id: "s6",
|
|
93
|
+
requestHeader: () => ({ config: { provider: "deepseek", model: "deepseek-chat" } }),
|
|
94
|
+
snapshotEvents: () => [userMessage("用快照接口提问"), { seq: 2, type: "turn/end" }]
|
|
95
|
+
};
|
|
96
|
+
await handler(session, { seq: 2, type: "turn/end" });
|
|
97
|
+
assert.equal(store.count(), 2);
|
|
98
|
+
const all = store.all();
|
|
99
|
+
assert.ok(all.some((m) => m.type === "decision"));
|
|
100
|
+
assert.ok(all.some((m) => m.type === "preference"));
|
|
101
|
+
});
|
|
102
|
+
|
|
88
103
|
test("skips summarization for events other than turn/end", async () => {
|
|
89
104
|
const { events, store, calls } = setup();
|
|
90
105
|
const handler = events.find((e) => e.name === "session/event").fn;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modusensus/dsh-mneme",
|
|
3
|
-
"version": "0.7.
|
|
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": {
|