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/README.md CHANGED
@@ -1,25 +1,25 @@
1
1
  # dsh-lite-memory
2
2
 
3
- **没有常驻的记忆正文:不写系统提示,不逐轮注入,命中才读。**
4
- **No memory in the prompt: nothing injected per turn, read only on a hit.**
3
+ **全库不常驻:没有命中的轮次不追加记忆内容,命中时最多三条卡片,正文按需读。**
4
+ **Nothing resident: a turn that misses adds no memory text; a hit adds at most three cards, bodies read on demand.**
5
5
 
6
6
  ---
7
7
 
8
8
  ## 它是什么 / What it is
9
9
 
10
- 给 DeepSeek Harness 的轻量记忆:不写常驻系统提示、不逐轮注入、不扫描文件。记忆只在两种时刻出现——你说「记一下」,或者这句话跟已有条目真有关(最多三条标题,正文仍按需读)。
10
+ 给 DeepSeek Harness 的轻量记忆:全库不进常驻提示、不逐轮注入、不扫描文件。记忆只在两种时刻出现——你说「记一下」,或者这句话跟已有条目真有关(最多三条卡片:标题、摘要、前提,正文仍按需读)。
11
11
 
12
- Lightweight memory for the DeepSeek Harness. No standing system prompt, nothing injected per turn, no file scanning. Memory appears at exactly two moments: you ask it to remember, or the message genuinely overlaps an existing entry (at most three titles; bodies are read on demand).
12
+ Lightweight memory for the DeepSeek Harness. Nothing resident: no standing prompt, no per-turn injection, no file scanning. Memory appears at exactly two moments: you ask it to remember, or the message genuinely overlaps an existing entry (at most three cards with title, summary and preconditions; bodies are read on demand).
13
13
 
14
14
  ## 触发方式 / Triggers
15
15
 
16
16
  | 场景 | 行为 |
17
17
  | --- | --- |
18
18
  | **L0** 你明确要求「记住这个」 | 立即写盘,一条确认行 |
19
- | **L1** 它发现有跨会话价值的经验 | 一行灰字 `memory · suggested "X" · sug_x` + 一个「保留」按钮;标题加一句话,看得懂才点,不点就永远不写,48 小时内不重复 |
19
+ | **L1** 它发现有跨会话价值的经验 | 一行灰字 `memory · suggested "X"` + 一个「保留」按钮;标题加一句话,看得懂才点,不点就永远不写,48 小时内不重复 |
20
20
  | **L2** 常识、一次性参数、凭据的值 | 永不写入 |
21
21
 
22
- 这些行只属于产生它的那一轮:每轮按那轮自己的工具结果重建,重进会话还能看见,别的轮不会混进来。再记一次时插件自己判是更新还是新增:同层、同前置条件下,标题(或这条用过的旧标题)完全一致就更新原条目,换过说法而标题变了的旧标题记成别名以后也认得。像但不同名的不写:先在结果里回「相近:X」和它的 id,确认要更新就带那个 id 再存,确认是另一条才加 `resolution="distinct"`。更新时没给的字段保持原值,正文整段替换——旧正文只作内部撤回依据,不展示、不参与检索;前置条件不同的同一主题是两条。
22
+ 这些行只属于产生它的那一轮:每轮按那轮自己的工具结果重建,重进会话还能看见,别的轮不会混进来。再记一次时插件自己判是更新还是新增:同层、同前置条件下,标题(或这条用过的旧标题)完全一致就更新原条目,换过说法而标题变了的旧标题记成别名以后也认得。像但不同名的不写:先在结果里回「相近:X」,确认要更新就用那个标题重存,确认是另一条才加 `resolution="distinct"`。更新时没给的字段保持原值,正文整段替换——旧正文只作内部撤回依据,不展示、不参与检索;前置条件不同的同一主题是两条。
23
23
 
24
24
  写入时分两层:默认只在这个项目生效;用户说「以后所有项目都这样」才进全局层(200 条起提示,硬上限 1000 条),检索时一起查。同一主题在不同前提下用 `when` 区分,各存一条。
25
25
 
@@ -27,7 +27,7 @@ Lightweight memory for the DeepSeek Harness. No standing system prompt, nothing
27
27
 
28
28
  触发词:`记住`、`记一下`、`上次`、`规范`、`约定`、`路径`、`凭据怎么取`。
29
29
 
30
- 真命中看这句话和记忆的标题(含旧标题)、标签、触发词、摘要有没有对得上的词:有就多一行「已有记忆可能相关」,最多三条标题和 id,每条前面标归属(`[global]` 或项目目录名如 `[DSH]`);没对上就不出现,不改命中计数,正文要 `memory_read` 才读。一条记忆写了触发词,用户说到那个词也算命中。
30
+ 真命中看这句话和记忆的标题(含旧标题)、标签、触发词、摘要有没有对得上的词:有就多一行「已有记忆可能相关」,最多三条标题,每条前面标归属(`[global]` 或项目目录名如 `[DSH]`);没对上就不出现,不改命中计数,正文要 `memory_read` 才读。一条记忆写了触发词,用户说到那个词也算命中。
31
31
 
32
32
  | Case | Behaviour |
33
33
  | --- | --- |
@@ -35,7 +35,7 @@ Lightweight memory for the DeepSeek Harness. No standing system prompt, nothing
35
35
  | **L1** it spots something worth keeping | one muted line `memory · suggested "X" · sug_x` with a single 「保留」 button — readable before you click, nothing written unless you click, at most once per two days |
36
36
  | **L2** common sense, one-off parameters, credential values | never written |
37
37
 
38
- Those lines belong to the turn that made them: each turn is rebuilt from its own tool results, so reopening the session shows them again and no other turn leaks in. Saving again updates an entry only when the title (or one of its earlier titles) matches exactly under the same precondition. A merely similar title writes nothing first: the result reports the closest entry and its id, so the caller either updates that id or re-saves with `resolution="distinct"`. An update keeps every field it was not given and replaces the body wholesale; the previous body is kept only for internal undo and never shown or searched. The same subject under different preconditions stays two entries.
38
+ Those lines belong to the turn that made them: each turn is rebuilt from its own tool results, so reopening the session shows them again and no other turn leaks in. Saving again updates an entry only when the title (or one of its earlier titles) matches exactly under the same precondition. A merely similar title writes nothing first: the result names the closest entry, so the caller either re-saves with that title or adds `resolution="distinct"`. An update keeps every field it was not given and replaces the body wholesale; the previous body is kept only for internal undo and never shown or searched. The same subject under different preconditions stays two entries.
39
39
 
40
40
  Writes land in one of two layers: the current project by default, or the global layer (warns past 200, hard ceiling 1000) when the user says the rule holds for every project. Both layers are searched together, and a `when` field separates the same subject under different preconditions.
41
41
 
@@ -43,9 +43,9 @@ Before writing, distill: the title is the conclusion and the body keeps only the
43
43
 
44
44
  ## 点一下就存 / One click, no turn
45
45
 
46
- 「保留」是同源直写:不产生消息、不动输入框、不打断当前回复。点完按钮消失,行变成 `memory · kept`。真写过的行尾才有 `✕`,按 id 撤销:改过的条目还原成上一版(显示 `restored`),刚建的条目移入归档(显示 `archived`);条目后来又改过就停下并说明已经变过,不覆盖后来的修改。没变、需要确认、保存失败的行没有撤销。
46
+ 「保留」是同源直写:不产生消息、不动输入框、不打断当前回复。点完按钮消失,行变成 `memory · kept`。真写过的行尾才有 `✕`,按标题撤销:改过的条目还原成上一版(显示 `restored`),刚建的条目移入归档(显示 `archived`);条目后来又改过就停下并说明已经变过,不覆盖后来的修改。没变、需要确认、保存失败的行没有撤销。
47
47
 
48
- The 「保留」 button writes straight to disk from the page: no message, no extra model turn, the input box untouched. It disappears and the row becomes `memory · kept` — that is the whole interaction. Only a row that really changed gets the `✕`, and it undoes by id: an edited entry goes back to its previous body (`restored`), a freshly created one moves to the archive (`archived`); if the entry changed again it stops and says so instead of overwriting. Noop, needs-confirmation and failed saves get no undo.
48
+ The 「保留」 button writes straight to disk from the page: no message, no extra model turn, the input box untouched. It disappears and the row becomes `memory · kept` — that is the whole interaction. Only a row that really changed gets the `✕`, and it undoes by title: an edited entry goes back to its previous body (`restored`), a freshly created one moves to the archive (`archived`); if the entry changed again it stops and says so instead of overwriting. Noop, needs-confirmation and failed saves get no undo.
49
49
 
50
50
  ## 每轮引用条 / Per-turn references
51
51
 
@@ -57,30 +57,30 @@ Each completed turn keeps a few lines under 「已编辑 N 个文件」, built f
57
57
 
58
58
  会话头的大脑图标打开居中的记忆面板。顶部是「启用记忆」「自进化」两个开关,右上角「显示设置」里另有「启用图谱」「启用记忆宫殿」两个只影响展示的开关,默认关闭,按项目记住;内容分「记忆」「自进化」两个页签。默认看当前项目,也可以切到其他项目或全局层;切过去只读,不回写、不迁移旧格式。
59
59
 
60
- 「记忆」页签里,搜索框右侧可以在「列表」「图谱」「宫殿」之间切换;列表、图谱或宫殿占满内容宽度,选中记忆后右侧才出现正文与归属。图谱按真实数据画,点记忆节点读正文,点标签或主题节点转成搜索,右上角可缩放。宫殿按类型分四间:习惯与规范、方法与经验、资料与入口、其他记录;一次只展开一间,每间每页 24 条,点展品只读正文。宫殿只决定怎么陈列,不改记忆、学习状态和额度。「自进化」页签只作用于当前项目,按「全部 / 候选 / 试用 / 生效 / 暂停」筛选,也能切列表与图谱;详情给出当前经验、适用范围、当前状态、学习依据和变化记录。候选可采纳、修改、放弃,试用可采纳、修改、暂停,生效可修改、暂停,暂停可恢复或查看记录。
60
+ 「记忆」页签里,搜索框右侧可以在「列表」「图谱」「宫殿」之间切换;列表、图谱或宫殿占满内容宽度,选中记忆后右侧才出现正文与归属。当前项目里点「修改」可以就地改标题和正文,保存后版本加一;别的项目和全局层只读。图谱按真实数据画,点记忆节点读正文,点标签或主题节点转成搜索,右上角可缩放。宫殿按类型分四间:习惯与规范、方法与经验、资料与入口、其他记录;一次只展开一间,每间每页 24 条,点展品只读正文。宫殿只决定怎么陈列,不改记忆、学习状态和额度。「自进化」页签只作用于当前项目,按「全部 / 候选 / 试用 / 生效 / 暂停」筛选,也能切列表与图谱;详情给出当前经验、适用范围、当前状态、学习依据和变化记录。候选可采纳、修改、放弃,试用可采纳、修改、暂停,生效可修改、暂停,暂停可恢复或查看记录。
61
61
 
62
62
  The brain icon in the session header opens a centered memory panel. Memory and Self-evolution sit at the top, and Display settings at the top right add two presentation-only switches, Graph and Memory palace, both off by default and remembered per project. Two tabs, Memories and Self-evolution, hold the content. It starts on the current project and can switch to another project or the global layer; switched scopes are read-only and never migrate or create state.
63
63
 
64
- On the Memories tab, the search row switches between list, graph and palace; either view fills the content width, and the body pane only appears after a memory is selected. The graph is drawn from real data: a memory node opens its body, a tag or topic hub becomes a search, and the top-right controls zoom it. The palace sorts memories into four rooms by kind: Habits and conventions, Methods and experience, References and entries, and Other notes; one room opens at a time, each page holds 24 items, and an exhibit only opens its body. The palace changes presentation only, never memory, learning state or quota. The Self-evolution tab acts on the current project only, filters by All / Candidate / Trial / Active / Paused, and also offers list and graph views. It shows the current rule, scope, state, evidence and change history. Candidates can be adopted, edited or discarded; trials can be adopted, edited or paused; active rules can be edited or paused; paused rules can be resumed or inspected.
64
+ On the Memories tab, the search row switches between list, graph and palace; either view fills the content width, and the body pane only appears after a memory is selected. In the current project, Edit changes a memory's title and body in place and bumps its revision on save; other projects and the global layer stay read-only. The graph is drawn from real data: a memory node opens its body, a tag or topic hub becomes a search, and the top-right controls zoom it. The palace sorts memories into four rooms by kind: Habits and conventions, Methods and experience, References and entries, and Other notes; one room opens at a time, each page holds 24 items, and an exhibit only opens its body. The palace changes presentation only, never memory, learning state or quota. The Self-evolution tab acts on the current project only, filters by All / Candidate / Trial / Active / Paused, and also offers list and graph views. It shows the current rule, scope, state, evidence and change history. Candidates can be adopted, edited or discarded; trials can be adopted, edited or paused; active rules can be edited or paused; paused rules can be resumed or inspected.
65
65
 
66
66
  - 关闭项目记忆后,这个项目不再使用任何记忆(包括全局层):自动注入、检索、读取、引用、保存全部停,服务端也拒绝记忆工具和面板修改动作。自进化同时关闭并禁用;重新打开记忆后自进化仍为关闭,要单独开启。
67
67
  - 关闭只改权限,不删数据:候选、试用、生效、证据、整理额度和默认绑定都保留,计数照常显示。
68
68
  - 记忆工具与面板管理动作都在服务端拒绝,不能靠改前端绕过;其他项目不受影响。
69
69
  - 范围只来自服务端枚举的层;不接受客户端拼路径,也不能写别的项目。
70
- - 图谱只按数据画:每条有效记忆一个节点,共享标签和主题做枢纽,显式关联画实线;自进化图谱只连共用条件。位置由 id 决定,同一批数据每次一样。
70
+ - 图谱只按数据画:每条有效记忆一个节点,共享标签和主题做枢纽,显式关联画实线;自进化图谱只连共用条件。位置由条目自身算出来,同一批数据每次一样。
71
71
 
72
72
  - Turning project memory off stops all memory use in that project, including the global layer: no injection, search, read, citation or save, and the server rejects both memory tools and panel mutations. Self-evolution turns off with it; after memory is reopened, self-evolution stays off until switched on.
73
73
  - Closing is a permission change, not deletion: candidates, trials, active rules, evidence, organize quota and default bindings are kept, and counts stay visible.
74
74
  - Memory tools and panel management actions are rejected server-side, so a modified client cannot bypass the switch; other projects are unaffected.
75
75
  - Scopes come only from the server-side list; the page cannot assemble a path or write another project's notes.
76
- - The graph is data only: one node per active memory, shared tags and topics as hubs, explicit links as solid edges; positions derive from ids, so the same data looks the same every time.
76
+ - The graph is data only: one node per active memory, shared tags and topics as hubs, explicit links as solid edges; positions derive from each entry, so the same data looks the same every time.
77
77
 
78
78
  ## 为什么轻 / Why it stays light
79
79
 
80
80
  - **0 依赖**,存储层无定时器、无监听、无网络,只有同步单文件写;写入用一把有超时的锁,别的进程正在写就明确报「忙」,不抢别人的锁。
81
81
  - 搜索最多 4 条、400 字;正文只在 `memory_read` 里出现,全文永不进上下文。
82
82
  - 普通检索只列当前有效的记录;已经归档或被更新替换的旧说法不混进来,要查历史得在 `memory_search` 里显式要 `history`。
83
- - 上限:单条正文 4096 字节、整库正文 1 MB、每层 1000 条、标题与摘要各 60 字;每层 200 条只当提示线。90 天未用只留 `id + 标题`。到了硬上限不拒绝新记忆:把同层最久没被读的一条移进归档,用户固定过的规则不动,保存结果里写明是哪条(正文与完整记录都留着,可人工取回),单条本身超预算才报错;用户明确说「别记」也进归档。文件从不删除。
83
+ - 上限:单条正文 4096 字节、整库正文 1 MB、每层 1000 条、标题与摘要各 60 字;每层 200 条只当提示线。90 天未用只留标题与版本。到了硬上限不拒绝新记忆:把同层最久没被读的一条移进归档,用户固定过的规则不动,保存结果里写明是哪条(正文与完整记录都留着,可人工取回),单条本身超预算才报错;用户明确说「别记」也进归档。文件从不删除。
84
84
  - 一个项目一个目录,项目之间互相看不见。
85
85
 
86
86
  - **Zero dependencies.** The store has no timers, no listeners, no network — just synchronous single-file writes, guarded by a lock that gives up after 500 ms instead of stealing it.
package/TRIGGERS.md CHANGED
@@ -77,7 +77,7 @@
77
77
 
78
78
  ## 新会话为什么不会乱
79
79
 
80
- - 记忆只在工具结果里出现,从不进 system prompt,也从不伪造用户消息。
80
+ - 记忆只在工具结果和命中时的上下文槽里出现:命中才占位,整卡受预算限制,也从不伪造用户消息。
81
81
  - 结果行永远是「记忆 · …」,模型能分清这是历史笔记,不是用户本轮要求。
82
82
  - 摘要必须是名词短语;「你要…」「请…」这类指令式写法违反约定(工具描述里也会提醒),但写入校验不会替模型改。
83
83
  - 存储按项目目录哈希隔离,别的项目看不到。