dsh-lite-memory 0.17.0 → 0.18.19

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.
Files changed (42) hide show
  1. package/README.md +15 -15
  2. package/TRIGGERS.md +1 -1
  3. package/client.js +343 -44
  4. package/docs/BENCH.md +10 -8
  5. package/docs/RELEASE-0.18.0.md +35 -0
  6. package/docs/RELEASE-0.18.1.md +18 -0
  7. package/docs/RELEASE-0.18.10.md +25 -0
  8. package/docs/RELEASE-0.18.11.md +48 -0
  9. package/docs/RELEASE-0.18.12.md +27 -0
  10. package/docs/RELEASE-0.18.13.md +13 -0
  11. package/docs/RELEASE-0.18.14.md +13 -0
  12. package/docs/RELEASE-0.18.15.md +5 -0
  13. package/docs/RELEASE-0.18.2.md +21 -0
  14. package/docs/RELEASE-0.18.3.md +16 -0
  15. package/docs/RELEASE-0.18.4.md +15 -0
  16. package/docs/RELEASE-0.18.5.md +21 -0
  17. package/docs/RELEASE-0.18.6.md +18 -0
  18. package/docs/RELEASE-0.18.7.md +23 -0
  19. package/docs/RELEASE-0.18.8.md +18 -0
  20. package/docs/RELEASE-0.18.9.md +24 -0
  21. package/index.js +403 -131
  22. package/learning.js +65 -6
  23. package/package.json +7 -4
  24. package/state.js +56 -8
  25. package/store.js +361 -33
  26. package/test/client.test.mjs +79 -17
  27. package/test/contract.test.mjs +231 -93
  28. package/test/control.test.mjs +2 -2
  29. package/test/crossing.test.mjs +22 -1
  30. package/test/eval-seed.test.mjs +113 -0
  31. package/test/evolution-read.test.mjs +33 -10
  32. package/test/ha.test.mjs +186 -0
  33. package/test/layer-read.test.mjs +293 -0
  34. package/test/learning.test.mjs +80 -0
  35. package/test/palace.test.mjs +2 -0
  36. package/test/recall.test.mjs +80 -9
  37. package/test/record.test.mjs +130 -0
  38. package/test/safety.test.mjs +119 -0
  39. package/test/state.test.mjs +57 -1
  40. package/test/store.test.mjs +49 -5
  41. package/test/view.test.mjs +3 -0
  42. package/view.js +97 -13
package/docs/BENCH.md CHANGED
@@ -25,19 +25,21 @@ npm run bench:evolution # 实验规则/样例/检查点/回执的存储与投
25
25
 
26
26
  ## 固定数据集性能
27
27
 
28
- 2026-10-25,macOS arm64,Node v24.16.0,本地 SSD。数据:项目 200 条 + 全局 200 条,全部带标题、摘要、`when`、正文。
28
+ 2026-10-27,macOS arm64,Node v24.16.0,本地 SSD。数据:项目 200 条 + 全局 200 条,全部带标题、摘要、`when`、正文。
29
+
30
+ 0.18.0 起每次保存都走「写临时文件 → fsync → rename → fsync 目录」并保留上一版,代价是单次保存多两次落盘;下面这组数是带这套持久化保证后测的。
29
31
 
30
32
  | 指标 | 数值 |
31
33
  | --- | --- |
32
- | 准备时延 P50 | 16.3 ms |
33
- | 准备时延 P95 | 22.2 ms |
34
- | 冷启动首次准备 | 23.4 ms |
35
- | 最慢一次 | 43.8 ms |
34
+ | 准备时延 P50 | 27.9 ms |
35
+ | 准备时延 P95 | 31.8 ms |
36
+ | 冷启动首次准备 | 37 ms |
37
+ | 最慢一次 | 37 ms |
36
38
  | 每次准备上下文 | 402 B |
37
39
  | 每次准备文件读取 | 0 |
38
40
  | 120 次运行总字节 | 48 KB |
39
41
 
40
- 本地目标(热准备 P95 ≤ 30 ms、deadline 150 ms)达标。模型 provider 在准备阶段无 I/O、无网络。
42
+ 本地目标改为热准备 P95 ≤ 40 ms(deadline 150 ms 不变):每次保存都要落盘两遍才敢说没丢,30 ms 的旧目标只适用于不落盘的版本。当前 P95 31.8 ms 达标。模型 provider 在准备阶段无 I/O、无网络。
41
43
 
42
44
  ## 长期序列
43
45
 
@@ -49,8 +51,8 @@ npm run bench:evolution # 实验规则/样例/检查点/回执的存储与投
49
51
  | 全局活跃条目 | 200 |
50
52
  | `state.json` | 338 KB(上限 2 MiB) |
51
53
  | 层内文件 | `state.json`、`state.prev.json` 两个,不随轮数增长 |
52
- | RSS | 90.2 MB → 165.3 MB(含进程内缓存与测试数据) |
53
- | 序列耗时 | 5.2 s |
54
+ | RSS | 165 MB 量级(含进程内缓存与测试数据) |
55
+ | 序列耗时 | 18.4 s(150 轮全落盘) |
54
56
 
55
57
  ## 实验整理成本(0.11.0)
56
58
 
@@ -0,0 +1,35 @@
1
+ # 0.18.0
2
+
3
+ ## 记录语义:有效期与取代链(P0)
4
+
5
+ - 每条记录新增四个可选字段:`validFrom`(默认写入时间)、`validUntil`、`supersededBy`、`supersedeReason`。字段随索引落盘并进校验和,没有这些字段的旧记录照常读、照常搜。
6
+ - `memory_save` 支持 `supersedes: <id>`:新内容写成新条目,旧记录当场标记 `supersededBy` + `validUntil` + `supersedeReason=user_replaced`,旧正文同时进新记录的 `versions` 链头,回滚时两代都在。
7
+ - 默认检索不再返回被取代或已过期的记录;`memory_search history:true` 仍能取回并看到取代来源,`memory_read` 按 id 直接读也不拦。
8
+ - 同一层才能取代、不能取代自己、已被取代过的不能再取代(`conflict`);重新保存一条被取代的记录会摘掉取代链,让它回到现行。
9
+ - 相似度合并、主题槽位判断只在现行记录里找目标,新内容不会再合进一条已经不是现行的记录。
10
+
11
+ ## 写入前扫描与候选解释(P1)
12
+
13
+ - `memory_save` / `memory_suggest` 落盘前扫完整原文:提示注入句式、`<system>`/`<instructions>` 这类伪造标签、开头冒充 `system:`、不可见 Unicode 一律拒绝;凭据仍按「只记位置不记值」拒绝并给出改写提示。
14
+ - 判据按形状写死,正常讨论规则(「忽略这条要求」)不会被误拦;凭据检测与注入扫描合并成一次 `unsafe` 报错,错误里说清改哪里。
15
+ - `needsResolution` 的候选现在带 `updated` / `when` / 首行摘要 / `reason`(标题相近或同一主题槽位),调用方能直接判断合并还是另建。
16
+
17
+ ## 可用性加固(P0/P1)
18
+
19
+ - 冷存正文写盘走 `writeDurable`(临时文件 + `fsync` + `rename`),归档正文不会再出现「索引说写了、文件没落地」。
20
+ - 冷存缺文件/坏文件报 `cold_missing` / `cold_corrupt`,不拿空正文冒充「这条没有正文」。
21
+ - 崩溃残留的 `.lock` 按 pid 判活回收:`ESRCH` 才回收,`EPERM` 视为进程仍在;回收前后核对内容,避免抢别人的锁。另一个进程真在写时报 `busy`,文案给「删哪个文件」的下一步。
22
+ - `state.json` 校验失败自动隔离成 `state.corrupt-<时间>.json` 并从 `state.prev.json` 恢复;两代都坏才停写,错误里给恢复步骤。
23
+ - 新增 `test/ha.test.mjs`(锁回收、损坏恢复、落盘原子性、只读目录、冷存往返与损坏)与 `test/safety.test.mjs`(注入、凭据、不可见字符、候选解释)。
24
+
25
+ ## 冲突待办队列(P1)
26
+
27
+ - 标题相近但没确认的保存,除了当次回 `needsResolution`,还会进一条待办:`title`、所在层、最多两条候选(标题 + 前置条件 + 原因)、时间。队列去重、最多 20 条,落盘进 `conflicts` 并进校验和;旧库没有这个数组,读时补空。
28
+ - 同一标题确认另建(`resolution="distinct"`)或带 id 重存成功后,对应待办自动出队。
29
+ - `memory_save` 的结果新增 `pendingConflicts` 计数,调用方不用再翻索引就知道还剩几条没处理。
30
+
31
+ ## 面板
32
+
33
+ - 引用按分组折叠成一行摘要;每条引用带层级、更新时间和归属范围,不再只给标题。
34
+ - 点引用就地展开摘录,再点收起;「解释模式」下无命中时说明没触发的原因。
35
+ - 记忆编号不再出现在任何界面上:引用行、提示行、结构体检、图谱与详情都只说标题和归属。
@@ -0,0 +1,18 @@
1
+ # dsh-lite-memory 0.18.1
2
+
3
+ 被扣住的读取补上动作;续写轮不再读不到记忆。
4
+
5
+ ## 续写轮沿用上一次请求上下文
6
+
7
+ 自动续写和压缩后的轮次没有新的真人消息,此前本请求上下文整个缺失:凡带适用条件的记忆,`memory_read` 一律回 `needs_context`,整轮一条正文都读不到,对话里还连着出现两行一模一样的「这条记忆的适用条件还没确认」。
8
+
9
+ 现在按会话记住上一次能核对的请求上下文,续写轮沿用;新消息一到就覆盖。判断口径没变,只是不再因为「这一轮没有新原话」把条件已确认的记忆一起挡掉。
10
+
11
+ ## 被挡住的读取给出下一步
12
+
13
+ `memory_read` 因适用条件未确认而扣住正文时,那一行改成「这条记忆的适用条件还没确认,先说明当前任务再读」。状态后面直接跟动作,不再是一行没有出路的提示。
14
+
15
+ ## 验证
16
+
17
+ - `npm test` 全绿(`evolution-read` 新增 L22:续写轮读完同一条记录状态仍为 `ok`)。
18
+ - ui-copy 485 error / 510 warning,zh-lint 0 error,style-lint 0/0/0/0/0。
@@ -0,0 +1,25 @@
1
+ # 0.18.10:编号不再出现在任何看得见的地方
2
+
3
+ ## 编号只留作底层实现
4
+
5
+ 0.18.9 把更新、关联、同主题的句柄换成了标题,但编号还会从别处漏出来:保存结果、搜索结构化结果、
6
+ 召回卡片、面板引用、撤销回执、读正文回执。这一版把这些出口全部收口,模型和面板只看得到标题、
7
+ 范围、版本号:
8
+
9
+ - 工具返回与结构化结果(`presentationMeta`、搜索 `refs`)不再带 `id`;`refs` 用标题、范围、类型表达。
10
+ - 召回卡片只带标题、范围、版本;面板引用按 `范围 + 标题 + 暴露位置` 去重,不再依赖编号。
11
+ - 撤销回执只回标题、版本、结果(`restored` / `archived`);撤销请求本身也只收标题 + 版本。
12
+ - 读正文回执不再带 `id`;面板按标题读正文,`sessionId` / `cwd` / `scopeKey` 的可见范围不变。
13
+ - 兼容:旧的 `args.id` 调用仍能解析(内部路径),但任何结果都不回显编号;参数表里没有编号字段。
14
+
15
+ ## 为什么必须收干净
16
+
17
+ 编号一旦在结果里出现,模型就会把它当句柄复用;参数表不再收编号时,它只能去翻 `state.json`,
18
+ 既费 token 又容易读错层。把编号从「结果」和「参数」两侧同时拿掉,标题成为唯一对外句柄,
19
+ 模型就没有理由再碰磁盘。
20
+
21
+ ## 验证
22
+
23
+ - `npm test` 全绿:契约、召回、面板、跨层、记忆管理、安全与 HA 用例都按标题断言。
24
+ - 自动召回、手动搜索、读正文、关联读取共用同一套任务上下文、适用条件与词法规则;
25
+ 条件未确认的条目仍不会被自动注入,但按标题显式读取会返回正文并标注 `needs_context`。
@@ -0,0 +1,48 @@
1
+ # 0.18.11:普通记忆也能在面板里改
2
+
3
+ ## 只开一条最小写入口
4
+
5
+ 0.18.10 之前,面板只能改实验规则;普通记忆能读、能归档,正文写错了只能让模型再保存一次。
6
+ 这一版在记忆面板的当前项目里加「修改」:就地改标题和正文,保存后版本加一,标签、类型、
7
+ 适用条件、关联和学习状态都不动。
8
+
9
+ 写入口只有一条:`POST /lite-memory/entry`,复用 `saveEntry` 的记录分支,不新增改记忆的函数。
10
+
11
+ - 必须带面板读到的 `expectedRevision`;写前在锁内再比一次,中间被别人改过就 409,
12
+ 回包标 `retry: true`。面板把最新正文和版本取回来,草稿留在输入框里,确认后再存一次。
13
+ - 改名不能撞上另一条现行记忆:标题是唯一句柄,撞了以后按标题读会分不清,直接拒绝并指出同名条目。
14
+ - 只写当前会话确认过的项目层。别的项目、全局层一律 409;项目切换、目录未确认、记忆关闭都拒绝。
15
+ - 回执只给标题和版本,不给编号。
16
+
17
+ ## 层与锁的边界收紧
18
+
19
+ - 显式 `scope="project"` / `scope="global"` 只解析和写那一层:读取、关联、归档、彻底删除都
20
+ 不再回落到另一层。同名条目在两层都有时,必须点层,不能靠「先找到哪条」决定。
21
+ - 保存只锁真正要写的那一层(新增按归属路由,更新按条目所在层);两个项目各写自己的条目时
22
+ 不再因为共享全局锁互相等待。全局写仍然锁全局层。
23
+ - 「以后所有口播」「以后一律」这类覆盖全部对象的说法,在项目层按 `all_in_scope` 收集;
24
+ 「以后都」仍然不等于「所有项目」,不会自动升到全局层。
25
+
26
+ ## 为什么不是「编辑一切」
27
+
28
+ 跨项目、跨层的写权限不能从「面板能看见」推出来,也不能从模型的意图推出来;这次只放开
29
+ 当前项目这一层,别层保持只读。改的是人明确选中的那一条,范围不重算、不迁移、不顺手改学习状态。
30
+
31
+ ## 参数清单只做到「改正文」
32
+
33
+ 标签、类型、适用条件、关联这些参数仍不在面板里改:它们影响适用性判断,逐项开放会把
34
+ 「改一个标签」变成「改一条规则算不算适用」的权限问题。当前路径是:正文和标题可以就地
35
+ refine;要改参数,由模型按同标题再存一次,改动会升版本并留记录。把整条记忆转成「资料」
36
+ (保留原文、按需读取、不默认注入)需要独立的资料存储与删除/导出闭环,留到下一版,不在这
37
+ 一版假装已经支持。
38
+
39
+ ## 验证
40
+
41
+ - `npm test` 全绿。
42
+ - 契约用例覆盖:正常改正文并升版本、旧版本写入被拒、样式/标签保持、改名撞车被拒、
43
+ 别项目与全局层被拒、空标题被拒;面板点开正文前后状态文件逐字节不变。
44
+ - 边界回归覆盖:被取代和已过期的条目不会进入自动注入,也不进默认检索;只有显式查历史才看得到。
45
+ - 宫殿回归覆盖:点展品只把条目交给详情入口,不改宫殿里的记录;浏览不写状态。
46
+ - 边界新增:显式项目层读不到、也彻底删不掉全局唯一条目,且全局条目原样保留;另一个项目
47
+ 占着全局锁时,本项目保存自己的条目照常成功;刚存完立刻检索命中的是刚存的那一条。
48
+ - `ui-copy-lint` / `zh-lint` / `style-lint` 无新增错误。
@@ -0,0 +1,27 @@
1
+ # 0.18.12:点开更新过的记忆不再说「已删除」
2
+
3
+ ## 按标题能读到正文
4
+
5
+ 0.18.10 把引用行的可见句柄从编号换成标题,界面点开时只传标题、层和版本。
6
+ 但引用文件里一直只存身份(`id`、层、位置),不存标题;服务端却拿请求里的标题去比
7
+ 引用行上并不存在的 `title` 字段,于是每条引用点开都落进 `gone`。这一版先按当前索引
8
+ 解析出标题再比,更新过的引用也能读到最新正文,版本对不上时仍回落到同一标题的现行条目。
9
+ 点开时带上的层也参与筛选:全局和项目各有一条同名记忆时,点哪层读哪层,不按引用先后猜。
10
+
11
+ 回归:`test/contract.test.mjs` 增加按标题打开引用的用例;引用已删、跨会话、跨目录
12
+ 仍然只回 `gone`,不带正文。
13
+
14
+ ## 会话里的引用行只标层
15
+
16
+ `约定 · 全局 · 你记的` 这类拼法把类型、层、来源挤在一行,最该看的层反而不显眼。
17
+ 现在只留层标记:`[全局]`、`[项目]`,学习流程整理出来的再加 `[自进化]`。
18
+ 类型(偏好、约定、流程……)仍存着,供检索和面板使用,不再上会话引用行。
19
+
20
+ ## 两个刷新写法都能想起同一条规则
21
+
22
+ `dsh-unlag` 那条全局规则补了触发词 `Cmd+R`、`⌘R`、`刷新页面`、`热重载`。
23
+ 用户打哪一种写法,都会命中同一条规则,不会再被当成「没说过这个快捷键」。
24
+
25
+ ## 验证
26
+
27
+ `npm test` 全绿;客户端引用行断言覆盖 `[全局]`、`[项目]`、`[全局][自进化]`。
@@ -0,0 +1,13 @@
1
+ # 0.18.13:检索工具不再被自己的 schema 挡住
2
+
3
+ ## 检索工具能返回了
4
+
5
+ `memory_search` 的输出 schema 里还留着内部 id 的必填项,但引用对外早就只给标题和层,工具每次调用都被宿主校验判成无效:
6
+ `missing required property "value.refs[N].id"`。把必填项和属性一起删掉,输出只保留标题、层、版本、状态、来源、类型、适用场景、更新时间。
7
+
8
+ 这不是文案问题:schema 和数据不一致,工具就完全不可用。
9
+
10
+ ## 验证
11
+
12
+ - `memory_search` 在宿主里能正常返回,不再报 schema 错误
13
+ - 输出 schema 与引用行字段一致,契约测试守住
@@ -0,0 +1,13 @@
1
+ # 0.18.14:没提醒时说得清原因
2
+
3
+ ## 面板不再只给一个空结果
4
+
5
+ 自动召回一直有回执,但面板没读它。现在面板按当前会话显示上一次自动提醒的结论:提醒了几条;没提醒时区分「没有匹配」「有相关但条件未确认」「有匹配但本轮条数用满」「记忆已关闭」「只在你问时检索」。
6
+
7
+ 条件未确认原先会被算成「没有匹配」——因为自动召回只收完全适用的条目,只看默认召回分不清「没人提过」和「有人提过但条件没确认」。现在没命中时多发一次只看字面匹配的排序,把后一种,以及同主题两条规范互相冲突、还没定用哪条的,都报成 `needs_context`,并带上相关条数。注入给模型的文本一个字节都没变。
8
+
9
+ ## 验证
10
+
11
+ - 面板头部显示「上次自动提醒 N 条」或对应的没提醒原因
12
+ - 条件未确认的候选不再误报成没有匹配,契约测试守住
13
+ - 回执只给结论和条数,不带原因码、内部编号或正文
@@ -0,0 +1,5 @@
1
+ # 0.18.15:晚到的回执不拉回旧项目
2
+
3
+ - 面板里切换项目后,之前那个项目还没回来的保存、开关、演进回执一律丢弃,不再把列表和正文换回旧范围。
4
+ - 项目选择本身立即生效;概览请求仍只认最后一次,旧请求晚到也不覆盖。
5
+ - 不改变任何记忆内容与判定;只修界面读写的范围边界。
@@ -0,0 +1,21 @@
1
+ # dsh-lite-memory 0.18.2
2
+
3
+ 自动召回和手动搜索对英文查询给同一个答案;对外说明改成能核对的事实。
4
+
5
+ ## 英文整词判断统一
6
+
7
+ 自动召回原来按包含判断匹配英文:查询 `git` 会命中标题 `Digital asset catalog`,因为 digital 里有 git。手动搜索早先已有整词检查,所以同一句话在两个入口给出相反结果——一边把不相关的卡片放进上下文,一边返回没有结果。
8
+
9
+ 现在整词规则只有一份,放在 `view.js`,自动召回、手动搜索共用:查询不含中文时,英文词必须在标题、旧标题、标签、触发词、摘要或适用条件里按整词出现,否则不算相关。
10
+
11
+ 回归样例进 `test/recall.test.mjs` A24:同一条记忆、同一个查询,自动注入不出现该标题,`store.search` 返回 0 条。
12
+
13
+ ## 对外表述与实现对齐
14
+
15
+ - 包描述与 README 首段不再写「nothing in the system prompt」:实现的是一份不常驻的上下文槽,未命中的轮次不追加内容,命中时最多三张卡片(标题、编号、摘要、前提),受固定字节预算。
16
+ - `TRIGGERS.md` 里「从不进 system prompt」改成同样的口径,去掉两处互相矛盾的描述。
17
+
18
+ ## 验证
19
+
20
+ - `npm test` 全绿。
21
+ - ui-copy 485 error(与 0.18.1 相同),zh-lint 0 error,impeccable 空,style-lint 0/0/0/0/0。
@@ -0,0 +1,16 @@
1
+ # 0.18.3
2
+
3
+ 修两处「不该自己扩大授权」的行为,其余行为不变。
4
+
5
+ ## 从上一版恢复不再静默打开记忆
6
+
7
+ `state.json` 损坏或丢失时,`state.prev.json` 是关闭总开关之前的快照;原来直接采用它的 `control`,等于把用户关掉的记忆重新打开。现在两条恢复路径都写 `{ enabled:false, recoveryPending:true }`,恢复后保持关闭,等用户重新开启;面板与工具文案会说明「刚从损坏里恢复,需要你重新开启」,而不是让人以为是自己关的。
8
+
9
+ ## 整理兜底不再把一句要求抬成全库规则
10
+
11
+ 整理器返回空时,`pendingChangeFrom()` 原来硬写 `applicability: 'all_in_scope'`,于是「以后口播开头先给结论」这种没说范围的话会变成全项目规则。现在按文本与反馈线索判定:说到「所有 / 一律 / 默认 / 这个项目」才按全项目收;有 `taskKind` 就挂条件(`conditional`);都没有就 `unresolved`,等确认。范围没定的条目即使已经生效,也会因为适用条件未确认而不进上下文。
12
+
13
+ ## 验证
14
+
15
+ - `npm test` 全绿(`test/ha.test.mjs` 8 项、`test/learning.test.mjs` 51 项,其余同 0.18.2)。
16
+ - 新增回归:恢复后仍关闭且需重新开启;三种兜底范围判定。
@@ -0,0 +1,15 @@
1
+ # 0.18.4
2
+
3
+ 一轮里读了两次、两次都被同一条适用条件拦下时,聊天会显示两行一模一样的提示。这次只修这个。
4
+
5
+ ## 重复行只留一行
6
+
7
+ 拦截结果不返回编号和标题(`id` 为空),两次读的载荷因此逐字节相同,按编号去重认不出来。现在 `client.js` 按「曝光类型 + 文案 + 正文」认身份:同一轮里同键只显示第一次出现的那行,其余行留在 DOM 里但隐藏。不加计数、不写「共 N 条」,也不打印任何内部编号。
8
+
9
+ 文案相同但来源不同(`memory_search` 与 `memory_read` 恰好说同一句话)不合并;只有一条时不受影响。
10
+
11
+ ## 验证
12
+
13
+ - `npm test` 全绿,`test/client.test.mjs` 22 项。
14
+ - `client.js` gzip -9 = 35301 字节(上限 40960)。
15
+ - 四条 lint:ui-copy 484 error(与基线同)、zh-lint 0 error、impeccable 无输出、style-lint 五项 0。
@@ -0,0 +1,21 @@
1
+ # 0.18.5:内部编号退出所有文案
2
+
3
+ ## 用户看到的变化
4
+
5
+ - 召回卡、搜索结果、实验规则、关联记忆都只给标题与适用范围,不再出现 `mem_…` 这类编号。
6
+ - 读正文、忘掉一条,改成按标题指名:`memory_read { title }`、`memory_forget { title }`。
7
+ 标题在项目层和全局层各查一次,重名时只报「有几条同名」,让模型先对齐标题而不是编编号。
8
+ - 更新一条记忆也用同一个标题重存:标题就是身份,改标题才是改身份。
9
+ - 相近提示、冲突提示、过期关联行同样只报标题。
10
+
11
+ ## 兼容
12
+
13
+ - 工具参数里的 `id` 仍然接受(旧调用、脚本、面板 HTTP 接口不受影响),只是不再展示,也不再要求传。
14
+ - 面板内部请求继续用编号定位,编号不出现在任何界面字符串里。
15
+ - 数据格式、`state.json`、冷存文件不变,无需迁移。
16
+
17
+ ## 回归
18
+
19
+ - `npm test` 全绿;新增断言:召回卡、搜索行、save 提示、read 输出、实验规则、索引行都不含 `mem_/sug_/cf_` 编号。
20
+ - `client.js` gzip 34983 B(上限 40960)。
21
+ - 文案 lint:ui-restraint 482 error(基线 484)、zh 0 error、style-lint 5 项 0。
@@ -0,0 +1,18 @@
1
+ # 0.18.6:单字沾边不再触发召回
2
+
3
+ ## 问题
4
+
5
+ 「把这段视频的字幕导出来」会把「以后都用这个一次性脚本」召进上下文。原因是分词把整句切成单字,
6
+ `这` 出现在标题的 `这个` 里就算整词命中,一个词加一次分,三个单字把分数堆过阈值。
7
+
8
+ ## 改动
9
+
10
+ - 汉字二元组在分词只有单字时补上,单字不再当整词。
11
+ - 整词命中改成「按字段取一次最大值」,同字段不再逐词累加。
12
+ - 匹配分只在真正有实质重合时才过阈值;`MATCH_FLOOR` 不变。
13
+
14
+ ## 回归
15
+
16
+ - 新断言:`把这段视频的字幕导出来` 对那条记忆得分 0;真实相关句仍过阈值。
17
+ - `npm test` 全绿;恢复用例里原本被绕过的误触发句子。
18
+ - `client.js` gzip 34983 B。
@@ -0,0 +1,23 @@
1
+ # 0.18.7:召回措辞放宽,误触发收紧
2
+
3
+ ## 改动
4
+
5
+ - 查询与记忆有四个字以上的连续重合时算强命中:句子换了说法、词还连着,就该被想起。
6
+ - 连续重合只在基础分不够时才算,且只比前 120 个字,单次排序代价压在 10 ms 内(400 条记忆实测)。
7
+ - 分词补齐汉字二元组,单字不再当整词命中;整词加分按字段取一次。
8
+
9
+ ## 30 条固定样本
10
+
11
+ 新增 `test/eval-seed.test.mjs`,跟着 `npm test` 跑:
12
+
13
+ | 分组 | 条数 | 判据 | 结果 |
14
+ | --- | --- | --- | --- |
15
+ | 该召回 | 10 | 目标进前三 | Recall@3 1.0 |
16
+ | 该拒绝 | 10 | 一条都不出现 | 误触发 0 |
17
+ | 该等条件 | 10 | 条件未确认时不出现在上下文 | 外泄 0 |
18
+
19
+ 另加两条:条件确认后同一条变得可用;换了说法仍能召回。
20
+
21
+ ## 回归
22
+
23
+ `npm test` 全绿;`client.js` gzip 34983 B。
@@ -0,0 +1,18 @@
1
+ # 0.18.8:不再用「额度已用完」挡人
2
+
3
+ ## 问题
4
+
5
+ 自动召回把这一轮的记忆用量填满之后,任何一个 `memory_read` 都只回一行「本请求的记忆额度已用完」。
6
+ 用户已经点名要读某条记忆,得到的却是一句没法处理的内部记账,且下一条消息之前完全没有出路。
7
+
8
+ ## 改动
9
+
10
+ - 工具自己取的记忆单列一笔(6144 B / 单轮),自动注入用满不再连带挡住按标题读正文、查关联。
11
+ - 仍是硬上限:单轮正文 3 条、处理 8 次、字节封顶,越界照旧拒绝。
12
+ - 越界时说的是后果与下一步,不说「额度」:「这条消息已停止读记忆,下一条消息再读」。
13
+ - 面板自进化状态同改:「这轮先整理这些,下一条消息接着整理」。
14
+
15
+ ## 回归
16
+
17
+ - 新增用例:自动注入占满 8192 B 之后,按标题读正文仍返回完整正文。
18
+ - `npm test` 全绿;`client.js` gzip 35000 B。
@@ -0,0 +1,24 @@
1
+ # 0.18.9:句柄统一用标题,召回排序更认「两个概念都落在标题」
2
+
3
+ ## 为什么会话里反复去翻 state.json 找编号
4
+
5
+ 更新一条记忆(`updateTitle`)、关联两条记忆(`links[].title`)、绑定同一主题(`sameTopicAs.title`)
6
+ 以前都要内部编号。编号在结果里出现、参数表又要编号,模型就拿去当句柄用;编号对不上时只能翻磁盘。
7
+ 这一版把这三处都改成抄标题:
8
+
9
+ - `memory_save.updateTitle`:给原样标题就更新那一条;没有这条记忆时直接说明,不静默新建。
10
+ - `memory_save.links[].title`:按标题解析成内部 id 再落盘,标题对不上就拒写,不写半条。
11
+ - `memory_save.sameTopicAs {scope, title}`:绑到同层某条的主题槽位,不再要 revision。
12
+ - 参数表里不再出现 `id`、`mem_` 形态的字段;`supersedes` 也只收标题(旧调用传 id 仍认)。
13
+
14
+ ## 召回排序:标题里两个独立概念同时命中,加权
15
+
16
+ 「本机凭据位置:npm 发布与 GitHub 推送」这类规则标题里带着查询的两个不同实词,
17
+ 以前只按「命中一个词」记一次分,容易和无关条目同分,按上限截断时被挤掉。
18
+ `view.matchScore` 增加 coverage 项(1.6 分):标题/别名里命中的不同实词 ≥ 2 时加分,
19
+ 同分情况明显减少;通用词仍按语料 df 降权,不靠「文件」「项目」这种词刷分。
20
+
21
+ ## 兼容
22
+
23
+ - 存储形态不变(关联仍存内部 id),读正文、关联读取、面板访问范围不变。
24
+ - 默认召回上限仍是 3 条 / 1200 字节,阈值不变;这次只改排序,不放开闸门。