@ilaomianyang/dsh-memory-graph 0.10.0
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 +259 -0
- package/LICENSE +21 -0
- package/README.en.md +466 -0
- package/README.md +459 -0
- package/client.js +1953 -0
- package/cordis.patch.yml +13 -0
- package/docs/auto-settings.png +0 -0
- package/docs/distill-button.png +0 -0
- package/docs/graph-dark.png +0 -0
- package/docs/graph-light.png +0 -0
- package/docs/session-tab-cards.png +0 -0
- package/docs/session-tab-open.png +0 -0
- package/docs/session-tab.png +0 -0
- package/docs/vault-panel.png +0 -0
- package/eval/REPORT.md +437 -0
- package/eval/questions.json +277 -0
- package/eval/run.mjs +460 -0
- package/eval/seed.mjs +489 -0
- package/icon.svg +26 -0
- package/index.js +243 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +77 -0
- package/src/auto.js +194 -0
- package/src/capture.js +237 -0
- package/src/config.js +280 -0
- package/src/distillapi.js +30 -0
- package/src/distillrun.js +106 -0
- package/src/extract.js +660 -0
- package/src/graph.js +1582 -0
- package/src/graphdata.js +475 -0
- package/src/http.js +32 -0
- package/src/pageapi.js +40 -0
- package/src/privacy.js +78 -0
- package/src/schema.js +323 -0
- package/src/search.js +317 -0
- package/src/tools.js +534 -0
- package/src/vault.js +888 -0
- package/test/auto.mjs +295 -0
- package/test/capture.mjs +264 -0
- package/test/client.mjs +1035 -0
- package/test/distillapi.mjs +212 -0
- package/test/extract.mjs +660 -0
- package/test/graphdata.mjs +295 -0
- package/test/live-supersede.mjs +78 -0
- package/test/live.mjs +156 -0
- package/test/page.mjs +116 -0
- package/test/schema.mjs +256 -0
- package/test/smoke.mjs +428 -0
- package/test/tools.mjs +238 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.10.0 — 2026-11-03
|
|
4
|
+
|
|
5
|
+
**发布到 npm**。`dsh-memory-graph` 这个名字在 npm 上已经被别人占了(maintainer `lilai123`,2026-11-01 发的 0.1.2,是个同名但不同的插件),所以换名发布。
|
|
6
|
+
|
|
7
|
+
**Changed**
|
|
8
|
+
|
|
9
|
+
- 包名 `dsh-memory-graph` → `@ilaomianyang/dsh-memory-graph`。跟着改的:`client.js` 的 `PACKAGE_NAME`(宿主按包名核对 client 是否注册,对不上就是 "loaded without registering")、仓库里 `cordis.patch.yml` 的 `name:`、README 两版的安装与卸载命令。
|
|
10
|
+
- `publishConfig.registry` 显式指向 `https://registry.npmjs.org/`。本机 npm 默认源是 npmmirror,镜像**只读**、不接受发布;不写死这一行,`npm publish` 会打到镜像上然后失败。
|
|
11
|
+
- `scripts.prepublishOnly: npm test`:发布前跑那 231 项,免得发上去一个红的版本。
|
|
12
|
+
|
|
13
|
+
**Fixed**
|
|
14
|
+
|
|
15
|
+
- `test/page.mjs` 里那个「磁盘上的图和服务出来的图是同一份渲染」的检查是**会闪的**:页面里的 `generatedAt` 带的是本机时区偏移(`…T13:21:53+08:00`),而规范化用的正则只认结尾的 `Z`,所以两次渲染只要跨了一秒就报差异。第一次 `npm publish` 就是被它拦下来的(发布前跑测试是对的,错的是那条检查)。现在两种写法都认。
|
|
16
|
+
|
|
17
|
+
**Added**
|
|
18
|
+
|
|
19
|
+
- README 两版新增「从 npm 装」:`plugin_manager action=install_bundle target="@ilaomianyang/dsh-memory-graph"`,并给出国内走 `registry="https://registry.npmmirror.com/"` 的写法(镜像自动同步 npm,滞后几分钟到几小时)。
|
|
20
|
+
|
|
21
|
+
**Removed**
|
|
22
|
+
|
|
23
|
+
- `package-lock.json`:里面还写着 `@local/dsh-memory-graph`(第一次本地装时的残留),且只有一个依赖,留着只会误导。
|
|
24
|
+
|
|
25
|
+
**Note**
|
|
26
|
+
|
|
27
|
+
- 换名后要重启一次应用:Web 侧的插件清单是启动时扫出来的,进程里缓存,改名跟新装一样得让进程重来。
|
|
28
|
+
## 0.9.1 — 2026-11-03
|
|
29
|
+
|
|
30
|
+
0.9.0 的面板能用了,但设置页的保存栏位置是错的:它浮在页面中间,压着「默认返回」那一行,下面还露着别的行。第一眼像"样式不对",其实是布局方式选错了。
|
|
31
|
+
|
|
32
|
+
**Fixed**
|
|
33
|
+
|
|
34
|
+
- 保存栏原来在滚动区里面、用 `position: sticky; bottom: 0` + `margin-bottom: -92px` 去抵消内边距。sticky 钉的是**外边距盒**,那个 -92px 就把整个栏往上提了 92px,于是它悬在离底边 92px 的地方,后面的内容从它下面滚过去。现在改成两行布局:`.mg-page` 是撑满高度的 flex 列,`.mg-root` 在里面滚,`.mg-bar` 是它的**兄弟**、静态定位,永远贴着底边,谁也不压谁。顺带没有魔法数字了(那 92px 是照着旧内边距手算的)。
|
|
35
|
+
- `test/client.mjs` 32 → 35:新增一项结构断言——动作栏必须在滚动区**外面**,滚动区必须在页面里。以后谁再把它挪回去,测试先红。
|
|
36
|
+
|
|
37
|
+
**Added**
|
|
38
|
+
|
|
39
|
+
- `docs/vault-panel.png`:侧栏面板的真机截图(裁掉了左侧边栏),补进两版 README 那一节。
|
|
40
|
+
## 0.9.0 — 2026-11-03
|
|
41
|
+
|
|
42
|
+
整库那张图以前只有一个入口:知道记忆库在哪,进去双击 `graph.html`。库的位置本身不是秘密,但"想看自己记得什么"不该先要求你记住一个路径。现在应用左侧栏多了一个 **记忆图谱**,点开就是它。
|
|
43
|
+
|
|
44
|
+
**Added**
|
|
45
|
+
|
|
46
|
+
- 侧栏入口 + 主面板:`sidebar.panellist` 注册一条(order 30,排在 上下文洞察 之后),`main` 用同一个 id 注册页面,外壳按这个字符串把两者连起来。
|
|
47
|
+
- `src/pageapi.js` + `/api/memory-graph.page`(GET):把 `graph.html` 当页面发出去。面板嵌的是**同一份渲染器**,不是第二套 React 重画——两份实现迟早会对不上,而"图里有的东西库里没有"这种错最难发现。只读:读卡片和实体页,不写文件、不调模型。
|
|
48
|
+
- `x-frame-options: SAMEORIGIN`:只有应用自己能把它嵌进去,顺手也挡住别的站点拿它做 frame。
|
|
49
|
+
- 面板顶栏两个按钮:**刷新**(重画,读到刚写进去的卡片)、**新窗口打开**(同一个页面开成真正的浏览器窗口,图能占满屏)。
|
|
50
|
+
- `src/vault.js`:`graphPage()` 返回渲染结果,`regenerateGraph()` 改成写它——磁盘和路由两个出口,一次渲染。
|
|
51
|
+
|
|
52
|
+
**Changed**
|
|
53
|
+
|
|
54
|
+
- 测试:`test/client.mjs` 32 → 34、新增 `test/page.mjs` 7 项,合计 **230** 项。
|
|
55
|
+
- 两版 README 都加了「不用找文件:侧栏里的记忆图谱」一节,「已知边界」里那条"整库图只能在 `graph.html` 里看"随之改写。
|
|
56
|
+
|
|
57
|
+
**Note**
|
|
58
|
+
|
|
59
|
+
- 面板是 iframe,所以搜索、缩放、过滤、深浅色仍然是页面自己那套,跟双击文件打开时一模一样——包括它的缺点(它不会跟着应用的主题切换,除非页面自己跟随系统)。
|
|
60
|
+
- 空库也给一页,只是写着"还没有卡片"。库读坏了就把原因印在页面上(转义后),不是白框。
|
|
61
|
+
## 0.8.0 — 2026-11-03
|
|
62
|
+
|
|
63
|
+
上一版给了个按钮,但按钮要人按。现在可以自己定义它什么时候跑:**攒够多少条**,或者**每隔多久**。两条规则互相独立,都可以单独开、单独关。
|
|
64
|
+
|
|
65
|
+
**Added**
|
|
66
|
+
|
|
67
|
+
- `src/auto.js`:一个每分钟的节拍 + 一个纯函数决定"这一拍该不该跑"。节拍只做算术(未蒸馏条数、距上次多久、是不是静默时段),**不调模型**;命中规则才跑一次。决定逻辑是纯函数,所以 19 项测试一秒跑完,不用等一分钟也不用花一个 token。
|
|
68
|
+
- 两条规则:`auto.minEpisodes`(未蒸馏情节攒够 N 条)与 `auto.everyMinutes`(距上次 N 分钟,1440 = 每天)。
|
|
69
|
+
- 三个共用的闸门:`auto.minGapMinutes`(最短间隔,默认 30)、`auto.quietHours`(静默时段,可跨午夜)、`auto.project`(自动卡片记到哪个项目)。
|
|
70
|
+
- 间隔**在调用之前**就落盘到 `.auto-state.json`(`lastAttemptAt`):跑空了也算数,重启也不重置,否则一个安静的库会被每分钟问一遍。
|
|
71
|
+
- `src/distillrun.js`:把「一次蒸馏」抽成共享 runner(单飞锁 + 报告 + 视图重建 + 卡片上限)。按钮、`memory_distill`、自动蒸馏三条路走同一把锁——正在跑时第二条路拿到"已经有一次在跑",不排队。
|
|
72
|
+
- `src/vault.js`:`readAutoState` / `writeAutoState`。和 `.distill-state.json`(游标)分开:游标回答"哪些行读过了",它回答"上次尝试是什么时候",混在一起就表达不了"读了但没产出"。
|
|
73
|
+
- 定时蒸的时钟从**打开那一刻**算起(`startedAt`),单独存:刚打开不会立刻花一次调用,也不会因为"时钟刚起"把「攒够就蒸」推迟一个最短间隔。两条规则因此各有各的触发条件,互不牵连。
|
|
74
|
+
- 设置页多了「自动蒸馏」一节(6 个字段,全部 volatile,改完立刻生效)。
|
|
75
|
+
|
|
76
|
+
**Changed**
|
|
77
|
+
|
|
78
|
+
- `src/distillapi.js` 只负责把报告翻成 HTTP 状态;锁和报告挪进 `src/distillrun.js`。
|
|
79
|
+
- `memory_distill` 的 `execute` 优先走 runner(`registerTools` 新增可选 `runner`),所以模型要的那次蒸馏不会和一次自动蒸馏撞在一起;`dryRun` 与取消信号照样透传。
|
|
80
|
+
- 测试:`test/client.mjs` 31 → 32、`test/tools.mjs` 21 → 22、`test/distillapi.mjs` 9 → 11、新增 `test/auto.mjs` 19 项、`test/capture.mjs` 11 → 12 项,合计 **221** 项。`test/schema.mjs` 的 volatile 叶子数改成"等于页面字段表",多一格少一格都失败。
|
|
81
|
+
|
|
82
|
+
**Fixed**
|
|
83
|
+
|
|
84
|
+
- 采集会把宿主的 runtime 快照当成你说的话记下来——实测库里就躺着一条 1501 字的图像工具说明,被 1500 字上限截断后当成一轮对话。原因是"这是不是人说的"只看 `source.kind`,而这条快照带的正是 `user`。现在来源只能**增加**跳过,不能否决文本规则,两条都过;判断之前先剥掉 `<system-reminder>…</system-reminder>` 这种有边界的块,所以一条真消息里带了提醒时,剥完留下你的原话,而不是整条丢掉。
|
|
85
|
+
- README 记下两个已知限制:采集不回溯(老会话没有情节,因为它只从插件在场那一刻开始),以及子智能体的开场提示词也会被当成一条情节(还没过滤)。
|
|
86
|
+
|
|
87
|
+
**发布**
|
|
88
|
+
|
|
89
|
+
- 包名从占位的 `@local/dsh-memory-graph` 改成 `dsh-memory-graph`(`package.json` / `client.js` 的 bundle id / `cordis.patch.yml` / 库里的 `vault.json` 标记四处一起改,它们必须一致)。补上 `repository` / `homepage` / `bugs` / `author`。
|
|
90
|
+
- README 补上 `git clone` 安装路径;截图裁掉左侧边栏(工作区名、会话标题、账号)。
|
|
91
|
+
- `.github/workflows/test.yml`:push / PR 时在 windows-latest 上跑 Node 20 与 22 的 `npm test`(221 项 + 离线评测,不联网、不碰记忆库)。
|
|
92
|
+
- `projectOf` 不再用 `path.basename`:它只认当前平台的斜杠,而会话头里的 cwd 可能是另一个系统写下的。两个斜杠都切。
|
|
93
|
+
|
|
94
|
+
**Note**
|
|
95
|
+
|
|
96
|
+
- 默认仍然是**关**。这不是保守,是定价:一次蒸馏是一次真模型调用,什么时候花这笔钱应该是用户的决定,不是插件的。
|
|
97
|
+
|
|
98
|
+
## 0.7.0 — 2026-11-03
|
|
99
|
+
|
|
100
|
+
蒸馏以前只有一条路:模型自己想起来调 `memory_distill`。采集是自动的、蒸馏不是,而界面上没有任何地方能按一下。现在会话页签的页头有 **蒸馏一次**。
|
|
101
|
+
|
|
102
|
+
**Added**
|
|
103
|
+
|
|
104
|
+
- `src/distillapi.js` + `/api/memory-graph.distill`(POST):跑的是 `memory_distill` 背后**同一个** `distill()`,用设置里选中的模型和当前生效的隐私规则,不另写一套。POST only——刷新页面、预取、GET 探路都不可能顺手花掉一次模型调用。
|
|
105
|
+
- 页头的「蒸馏一次」按钮:跑完在页头下面写一行「读出 12 条情节 → 3 张卡片 · 2 批」,随即重画图;失败写原因("抽取器已关闭"、"已经有一次在跑"、"没有新情节"),不是转圈。
|
|
106
|
+
- 一次只跑一个:同一进程内并发按下时,第二个拿到 `busy` 而不是排队——蒸馏花的是真钱,两开两花没有道理。
|
|
107
|
+
- 跑完顺手重建 `graph.html` 和库里的 README(`vault.regenerate*`),失败只 warn,不影响已经落盘的卡片。
|
|
108
|
+
- 卡片列表在响应里截到 40 条(`written` 仍是真数),免得一次大蒸馏把面板撑爆。
|
|
109
|
+
|
|
110
|
+
**Changed**
|
|
111
|
+
|
|
112
|
+
- `extractorServices(ctx)` 从 `tools.js` 挪进 `src/extract.js` 并导出:工具与路由必须借同一套 `llm` / 模型选择,否则两条路的模型会分叉。
|
|
113
|
+
- `json()` 抽到 `src/http.js`,两条路由共用一个响应助手。
|
|
114
|
+
- `src/tools.js` 少了 9 行(助手搬家),行为不变。
|
|
115
|
+
- 测试:`test/client.mjs` 29 → 31(按钮只 POST 一次、跑完重读、失败写原因)、新增 `test/distillapi.mjs` 9 项(同一函数、单飞锁、拒绝、抛错后解锁、视图刷新、卡片上限)。合计 197 项。
|
|
116
|
+
|
|
117
|
+
**Note**
|
|
118
|
+
|
|
119
|
+
- 为什么仍然不自动:一次蒸馏是一次真模型调用。没打招呼的后台调用等于替用户花钱,而且卡片会在他没看的时候进库。想要自动,正确的做法是一条定时提醒去调工具,而不是让插件自己决定什么时候花钱。
|
|
120
|
+
|
|
121
|
+
## 0.6.0 — 2026-11-02
|
|
122
|
+
|
|
123
|
+
记忆以前只能通过 `memory_search` 被模型看见。现在它也在**会话页签**里:对话 / 上下文 / 编队之后多一个「记忆」,点开就是这个会话留下的东西,以及它连着谁。
|
|
124
|
+
|
|
125
|
+
**Added**
|
|
126
|
+
|
|
127
|
+
- 会话视图:`client.js` 往 `conversation.view` 槽注册一格(id `memory-graph`、order 30、标签「记忆 / Memory」,排在「上下文」order 10 之后)。**用自家 id**,因为复用 `chat` / `trajectory` 会替换那一格而不是新增一格。
|
|
128
|
+
- 图的结构全部可证:会话 —`said`→ 本会话的每条情节;会话 —`recall`→ 本会话自己的话真的能唤回的卡片(复用 `searchCards`,和模型查的是同一个函数);卡片 —`mentions`→ 它提到的实体;情节 —`named`→ 它正文里点名的实体;被推翻的卡片 —`supersedes`→ 它的继任者。跨项目的卡片通过共享实体挂进来,虚线边框、暗一档。
|
|
129
|
+
- 点节点在右侧展开:卡片是标题 / 类型 / 状态 / 项目 / 日期 / 正文 / 实体 / 标签,情节是那一轮的用户原文,实体是"被多少段记忆提到过"。
|
|
130
|
+
- 数据通路:宿主 `ctx.connection.fetch.register` 注册 `/api/memory-graph.graph`(exact Fetch route)。Connection 先验浏览器签名、只把 `owner` Peer 放进来,所以插件里**没有第二套鉴权**;浏览器侧用同源 cookie 请求,`cache-control: no-store`。
|
|
131
|
+
- 布局在宿主算:确定性力导向(220 次迭代、种子位置按节点顺序、归一化到 1080×640),浏览器只把坐标画出来——没有每帧模拟,重开页签是同一张图。
|
|
132
|
+
- `src/graphdata.js`:`buildSessionGraph`(按 `session` 字段过滤情节、项目多数决、召回、实体桥、上限)、`layoutNodes`、`createGraphHandler`、`GRAPH_PATH`。
|
|
133
|
+
- `src/vault.js` 新增 `readEpisodes({ session, limit })`:游标式的 `readUndistilled` 回答不了"这个会话说过什么"(游标可能已经越过那些行),所以单独读全量再过滤。
|
|
134
|
+
- 三种空状态都说人话:没选会话、本会话还没留下情节(提示"聊一轮再刷新")、读不到记忆库(把宿主回的原因原样打出来,而不是空白面板)。
|
|
135
|
+
- 上限与截断:情节 40 / 卡片 60 / 实体 40 / 跨项目 12 / 边 420,超了写进 `truncated`,页头的计数照实报总数。
|
|
136
|
+
- 隐私:payload 只带用户侧原文(截断 180 字),assistant 一个字都不带——测试里塞了一句哨兵文本,断言它不出现在 JSON 里。
|
|
137
|
+
- 半空的图会说明自己:库里有情节但还没有卡片时,页头下面多一行提示,指向"蒸馏一次";没有这一行,第一眼看上去像是坏了。
|
|
138
|
+
- 布局按画布的**长宽分别归一化**(单轴扭曲上限 2.6:1):八个情节的会话以前缩在 1080×640 中间一个小圆里、三分之二画布是空的,现在铺满整条画布。
|
|
139
|
+
|
|
140
|
+
**Changed**
|
|
141
|
+
|
|
142
|
+
- 页签打开时每 20 秒静默轮询一次(`document.visibilityState === 'hidden'` 时不发),所以蒸馏完或改了卡片不用手点刷新。
|
|
143
|
+
- `client.js` 的 `apply` 拆成 `registerSlots(ctx, React)`,测试可以拿假 `ctx` 断言两个槽的**确切注册**(哪个槽、哪个 id、哪个 order、id 不是已发布的那几个)。
|
|
144
|
+
- 测试口径:`npm test` 197 项:`test/graphdata.mjs` 15 项、`test/client.mjs` 21 → 31 项、新增 `test/distillapi.mjs` 9 项。
|
|
145
|
+
|
|
146
|
+
**Note**
|
|
147
|
+
|
|
148
|
+
- 改完怎么生效(实测):`client.js` 刷新页面就够——客户端 bundle 是**每次请求现读文件**的,宿主只在启动时扫一次「有哪些包」。真正要重新 `apply` 的是宿主侧(`index.js` / `src/*.js`):在插件管理器里把「记忆图谱」关掉再打开,路由当场就注册上了,不用重启整个应用。设置页的即时生效本来就与这两者无关(volatile 字段)。
|
|
149
|
+
|
|
150
|
+
## 0.5.0 — 2026-11-02
|
|
151
|
+
|
|
152
|
+
蒸馏在真实记忆库上第一次跑就失败,暴露了两件事:一次调用喂不完一整个后台,以及同一件事会在图上长成两张卡。
|
|
153
|
+
|
|
154
|
+
**Added**
|
|
155
|
+
|
|
156
|
+
- `extract.js` 分批蒸馏:`maxEpisodesPerRun` 依旧是一次蒸馏读多少条,但每批条数按 `maxOutputTokens` 折算(约 320 输出 token 一条情节),一批一次调用、批批独立落盘。某一批失败(限流 / 超时 / 断网)时,前面几批的卡片和蒸馏游标都留着,`memory_distill` 会照实报"已经写下 N 张"。
|
|
157
|
+
- 截断自愈:某一批撞上输出上限就把这批对半拆开重试(递归到单条为止),单条还超才是真失败。两条路线都认这件事——harness 侧看 `finish.kind === 'max-tokens'`,http 侧看 `finish_reason === 'length'`(后者以前会当成 JSON 解析失败报错,误导方向)。
|
|
158
|
+
- 近义标题去重(`src/vault.js` 的 `titleSimilarity` / `NEAR_DUPLICATE`):同一项目里标题词面重合度 ≥ 0.55 视为同一张卡,归一化到老卡(id 不变、正文取更全的那份、实体取并集)。写进审计日志时会注明是哪个标题以多少分被并进来的。
|
|
159
|
+
- 带 `supersedes` 的卡片跳过近义判断:推翻是有意的写入,"监控换成 Prometheus"和"监控换到 Uptime Kuma"词面再像也是新决定。
|
|
160
|
+
- `test/live.mjs --backlog N` / `--tokens N`:把真实联网测试扩成"能看到分批"——12 条情节、3 000 输出上限时,日志里会写 `2/2 batch(es) of 9`。
|
|
161
|
+
|
|
162
|
+
**Changed**
|
|
163
|
+
|
|
164
|
+
- `memory_distill` 的文案分开报新卡与重新确认(`4 card(s) · 4 re-confirmed`),并写明分了几批、每批几条;`dryRun` 明确只预览第一批、还剩多少条。
|
|
165
|
+
- 设置页的「已自定义」标只在**真的和继承值不同**时出现:一次保存会钉住整行配置,值恰好等于默认的那些以前也被标成改过,看着像人改了三个设置。密钥例外(它的值不回传,只能看路径在不在)。`test/client.mjs` 21 项,其中一项就是拿"等值钉住"当输入。
|
|
166
|
+
- README / README.en:新增「一次蒸馏会分几批」一节、"已知边界"里把去重从"标题完全相同"改成"词面重合 ≥ 0.55(含 `supersedes` 例外)",实测表补上 12 条情节分两批那一行,并写下这次踩坑的原话:第一版一次调用喂完后台,被 3 000 token 截断,整批白跑。
|
|
167
|
+
- 测试口径:`npm test` 163 项检查(43 + 13 + 21 + 11 + 21 + 54)。
|
|
168
|
+
|
|
169
|
+
**Fixed**
|
|
170
|
+
|
|
171
|
+
- 23 条积压情节在有 3 000 输出上限时必定失败(`Extractor output reached maxOutputTokens`)——默认配置下第一次蒸馏就是不可用的。现在同一份后台分几次小调用,实测 12 条 → 2 批 → 4 张卡、5.9 s。
|
|
172
|
+
- 同一段对话分两批蒸馏时,模型给同一件事换了措辞(「长期记忆采用…纯文本库」/「长期记忆库采用…纯文本结构」),以前会写成两张卡、两个节点互相稀释检索分数。
|
|
173
|
+
|
|
174
|
+
## 0.4.0 — 2026-11-02
|
|
175
|
+
|
|
176
|
+
真正的设置页:插件在 **设置 → 记忆图谱** 里有自己的一页,四组、28 个字段,保存即生效。
|
|
177
|
+
|
|
178
|
+
**Added**
|
|
179
|
+
|
|
180
|
+
- `client.js`:手写 `window.__ModuleLoader__.load` factory 的浏览器侧 bundle,零构建、零 JSX、只 `require('react')`。往 `settings.section` 槽注册一页(id `memory-graph`,order 25,在「模型」和「内置插件」之间),四张分组卡片:采集 / 隐私 / 抽取器 / 检索。
|
|
181
|
+
- 每个字段都带中英文说明、单位、占位符和边界;改过的字段打「已改」标,旁边的「恢复默认」发的是 `unset`(退回继承层),不是把值写成空串。
|
|
182
|
+
- 底部粘着一条保存栏:未保存改动数、保存中、成功/失败提示;宿主只读时保存按钮直接禁用。保存被拒(`mutate` 返回 `false`)会清空草稿并提示"配置刚被别处改过",而不是假装成功。
|
|
183
|
+
- 抽取器那一组跟着路线走:`transport: 'harness'` 只显示模型相关的几项,选 `http` 才出现 endpoint / 路径 / key / temperature,`useCurrentModel` 与 `provider` 反过来隐藏。切换在下一次保存前就能看到效果(可见性用草稿值算,不是等回读)。
|
|
184
|
+
- 页面底部写明哪四项不在这页上(总开关 / 记忆库目录 / 默认项目 / 跨项目连接)、为什么、以及去 `cordis.patch.yml` 的哪一行改。
|
|
185
|
+
- `test/client.mjs`(20 项):用一个最小 React 替身把页面真挂起来跑——控件与字段表一致、`transport` 切换后 http 项才出现、空的内联 key 不会被当成"删除"发出去、越界数字到不了线上、宿主拒绝后清空草稿并报错、`unset` 与 `set` 不混用、样式表只有一个带包名标记的 `<style>`、`client.js` 里不许有 `export`/`import`(loader 按脚本求值);最后把 `client.js` 放进一个带 `window.__ModuleLoader__` 的假页面里求值,走一遍 factory → `apply` → `settings.section` 的**真实浏览器路径**(只 require `react`、注册 id 是包名、order 25、侧栏标签跟随语言)。
|
|
186
|
+
- `package.json`:新增 `exports["./client"]`、`dsh.client = { platform: 'web', inject: [] }`,`files` 带上 `client.js`。
|
|
187
|
+
|
|
188
|
+
**Fixed**
|
|
189
|
+
|
|
190
|
+
- **设置里一直没有这个插件的根因**:`@deepseek-ai/dsh-settings` 的 `describe()` 只投影声明为 `.volatile()` 的字段(`volatileForm(schema)` 拿不到 volatile 叶子就返回 `undefined`,整个条目被跳过)。0.3.0 导出的 `Config` 是对的,但 32 个叶子全是普通字段,所以永远不显示。现在 28 个"调用时才读"的字段标了 `.volatile()`,`enabled` / `vaultDir` / `defaultProject` / `crossProject` 保持普通(它们在激活期被消费,改了本来就是重载)。
|
|
191
|
+
- volatile 写入只走 `loader/volatile-update`、**不会重跑 `apply`**,所以三处快照都得跟着改,否则"即时生效"只是页面上的假象:`capture.enabled` 从注册期提前返回改成每条事件判断;`createRedactor` 在 volatile 更新时重建;`skipRules` 惰性重编译。
|
|
192
|
+
- `src/config.js` 新增 `unwrapConfig`:volatile 字段到达 `apply` 时是引用对象(`{[Symbol.for('cosmokit.volatile.write')]: fn, get: fn}`),直接 `{...DEFAULTS, ...config}` 会静默得到默认值加一个 `get` 函数。
|
|
193
|
+
|
|
194
|
+
**Changed**
|
|
195
|
+
|
|
196
|
+
- 配置入口从「schemastery 的通用表单」换成自己的页面;README / README.en 的「配置」一节重写(四组、默认值表、哪些键不在这页上、为什么)。
|
|
197
|
+
- **装完、或改过 `client.js` / `package.json` 之后必须重启一次 Harness**:Web 侧的插件清单是启动时扫一遍 loader 条目、读各自 `package.json` 的 `dsh.client` 定的,这份扫描结果在进程里缓存、不再重读。README 里写清了,免得装完以为插件坏了。
|
|
198
|
+
- 测试口径:`npm test` 151 项检查(38 + 13 + 20 + 11 + 21 + 48)。
|
|
199
|
+
|
|
200
|
+
## 0.3.0 — 2026-11-02
|
|
201
|
+
|
|
202
|
+
设置界面 + 直接复用设置里已经配好的模型。
|
|
203
|
+
|
|
204
|
+
**Added**
|
|
205
|
+
|
|
206
|
+
- `Config` schema(`src/schema.js`):DSH 的 Settings 表单是照着插件导出的 schema 画的,之前没导出,所以设置里根本没有这一行。现在 32 个设置项全部带中英文说明,包括 `vaultDir`、`capture.*`、`privacy.*`、`extractor.*`、`retrieval.*`;每一格都有默认值,空的 `config:` 依然合法。
|
|
207
|
+
- `test/schema.mjs`(9 项):表单和运行时两条默认值**双向**比对,多一格没用的、少一格看不见的都算失败(只放过 `vaultDir`——表单存原始文本、运行时存绝对路径);顺带锁住 `transport` 的取值、数值边界、`apiKey` 的 `secret` 角色、以及所有设置项都真的出现在投影出来的 schema 里。
|
|
208
|
+
- `extractor.transport: 'harness'`(默认):蒸馏不再要求另配 endpoint 和 key,而是向 Harness 的 `llm` 服务借一次调用,提供商 / 模型 / **思考强度**全部照搬 **Settings → 模型**里的当前选择(`agentDefaultModel.currentSelection()`)。设置里配过的 key 直接生效,插件不碰凭据。
|
|
209
|
+
- `extractor.useCurrentModel` / `provider` / `reasoningEffort`:想钉死一个抽取专用模型就关掉跟随、填 provider + model;想换思考档就填 `reasoningEffort`(留空沿用设置里的那一档)。
|
|
210
|
+
- `extractor.transport: 'http'`:原来的直连路线保留,一个字段切回去。`baseUrl` / `path` / `model` / `apiKeyEnv` / `apiKey` / `temperature` 现在都标注为 http 专用。
|
|
211
|
+
- `extractor.apiKey` 标了 `role('secret')`,同时在 README 里明说**不要用它**——表单里填的密钥会明文落进 profile 配置,`apiKeyEnv` 指向凭据库才是设计上的路线。
|
|
212
|
+
- `memory_stats` 新增一行 `extractor:`,说明当前走哪条路、用哪个模型、思考档是什么;`memory_distill` 的报告里也带上了 `model` 和来源。只报来源,不报值。
|
|
213
|
+
- `test/extract.mjs` 从 34 项扩到 48 项:新增 harness 路线的假 `llm` 服务(借用设置模型、显式覆盖、没选模型时的报错、`finish` 的 error / max-tokens / aborted、取消时说明是谁取消的、空回复、全程不碰 `fetch`)。
|
|
214
|
+
|
|
215
|
+
**Changed**
|
|
216
|
+
|
|
217
|
+
- harness 路线**不传 `temperature`**:采样交给被借用的那一路模型,避免和某些提供商的思考模式打架;`extractor.temperature` 因此只在 http 模式下生效。
|
|
218
|
+
- 报告与审计行改为 `model=<provider>/<model> via=harness route=<来源 · effort <档>>`。
|
|
219
|
+
- `test/live.mjs` / `test/live-supersede.mjs` 显式写 `transport: 'http'`:这两个脚本跑在普通 `node` 进程里,没有 Harness 的 `llm` 服务可借,改成默认 harness 后它们会连不上。
|
|
220
|
+
- `extractor.model` 的空值处理按路线分开:默认值改成空,http 模式在归一化时回退到 `deepseek-chat`;harness 模式下被清空就是空,由路由检查报「缺 provider/model」,而不是悄悄用一个别的模型顶上去。
|
|
221
|
+
- 插件不再是"零依赖":`index.js` 会试着加载 `@deepseek-ai/schemastery`(Harness 自带的那个,也是所有官方插件用的那个),加载不到就退化成"没有表单、跑默认值",插件本身照样起来。所以**裸仓库 + `link:` 安装需要先 `npm install` 一次**,README 的安装步骤已加这一句。
|
|
222
|
+
- `package.json` 的 `description` 同步改口径(原文写着 "Zero dependencies")。
|
|
223
|
+
|
|
224
|
+
## 0.2.0 — 2026-11-02
|
|
225
|
+
|
|
226
|
+
记忆图视图、离线评测,以及「问的是过去还是现在」这条通路。
|
|
227
|
+
|
|
228
|
+
**Added**
|
|
229
|
+
|
|
230
|
+
- `graph.html`:每次写入后重新生成的**单文件**记忆图。力导向布局,实体是枢纽节点,卡片按 kind 着色,虚边是「已被替换」,支持搜索、按项目过滤、点击卡片看详情、悬停高亮邻居、缩放平移。零依赖、无 CDN、无构建步骤,直接双击打开;跟随系统深浅色。入口是 `vault.regenerateGraph()`,`memory_stats` 会告诉你文件在哪。
|
|
231
|
+
- `eval/`:38 张卡片 / 4 条 supersede 链 / 33 道题的离线评测,跑的是仓库里真实的 `searchCards`。`npm run eval` 给出分组 recall@1 / recall@5 / MRR、逐题名次、negative 明细和 PASS/FAIL 判定;`eval/REPORT.md` 记录八个已知弱点和「这份评测容易高估你的地方」。
|
|
232
|
+
- `test/extract.mjs`(34 项):`fetch` 打桩测蒸馏器——prompt 构造、key 解析顺序、容错 JSON 解析、逐卡 project 归属、游标推进、dry run 不落盘、密文不落地。
|
|
233
|
+
- `test/live.mjs` / `test/live-supersede.mjs`:真模型端到端。前者 3 段 episode → 蒸馏 → 搜回来;后者记录一个决定、再改口,检查 `supersedes` 链和 `valid_to` 是不是真的动了。
|
|
234
|
+
- `extractor.baseUrl` / `extractor.model` / `extractor.maxEpisodesPerRun` 配置项;key 可以只写在 `~/.dsh/.credentials.yaml` 的 `refs:` 下,报告来源而不打印值。
|
|
235
|
+
|
|
236
|
+
**Changed**
|
|
237
|
+
|
|
238
|
+
- 检索:`superseded` 卡片基础权重 0.4 → **0.15**,并新增「命中作废卡则把它当前有效的继任卡拉进来」(权重 0.9)——措辞停留在旧词时,答案应该是最新状态。
|
|
239
|
+
- 检索:问句意图分流。命中历史意图(最早 / 当初 / 上一版 / 原来那 / used to …)时,作废卡恢复全权重、**且不再触发继任卡拉取**;否则维持降权 + 拉取。同一句话换个时态就该给另一个答案。
|
|
240
|
+
- 中文分词:多字虚词表扩充(`什么 怎么 如何 哪些 这个 那个 …`),并新增一条**字规则**——bigram 两端只要碰到纯语法字就丢弃,取代「枚举虚词 + 削尾字」。`有`、`不`、`得` 刻意不在字表里:它们在 `所有`、`不同`、`得到` 里是实词的一部分,第一版把它们也算进去,把「所有项目都要零依赖吗」这个问题在问的东西一起丢掉了。
|
|
241
|
+
- `postToolUse` 之后重新生成 README 与 graph.html 的调用合并成 `refreshViews()`,任一步失败只记 warn,不影响写入。
|
|
242
|
+
|
|
243
|
+
**Fixed**
|
|
244
|
+
|
|
245
|
+
- 作废卡在前面挡路:措辞没跟上改口时,正确的新卡片排不到前面。
|
|
246
|
+
- `原来 Kuma 只做外部拨测啊` 这类「原来 = 原来如此」被当成历史意图,把作废卡放大 6.7 倍。`原来` 已从意图正则里拿掉(保留 `原来那`)。
|
|
247
|
+
|
|
248
|
+
## 0.1.0 — 2026-11-01
|
|
249
|
+
|
|
250
|
+
第一个可用版本。
|
|
251
|
+
|
|
252
|
+
**Added**
|
|
253
|
+
|
|
254
|
+
- 三层存储,纯文本、每张卡片一个文件、append-only:`episodes/`(原话,永不改写)→ `cards/`(结论型记忆,带 kind / project / entities / valid_from / valid_to)→ `entities/`(跨项目枢纽)。
|
|
255
|
+
- 八个工具:`memory_search`、`memory_timeline`、`memory_get`、`memory_remember`、`memory_forget`、`memory_distill`、`memory_graph`、`memory_stats`。**只在模型主动查的时候给**,不做自动注入。
|
|
256
|
+
- 采集:`session/event` 钩子把用户原话和助手回复写进当天的 episode 文件,注入的上下文(`runtime-context`、`skill-catalog`、`time-context`、`compact-checkpoint`)跳过。
|
|
257
|
+
- 隐私:写入前过一遍 deny 正则(API key、token、私钥、身份证、手机号…),命中整段打码,`memory_forget` 保留审计痕迹(retract 而不是删除)。
|
|
258
|
+
- 蒸馏:走自己配置的 OpenAI 兼容接口(`/chat/completions`),输出逐卡带 project 的 JSON;游标 `maxDistillState` 保证不重复蒸馏。
|
|
259
|
+
- 时间有效性:`supersedes` / `supersededBy` / `valid_to`,改口不是覆盖而是接龙,历史可以回头查。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-memory-graph contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|