dsh-memory-fish 0.0.0-stage → 0.7.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 ADDED
@@ -0,0 +1,99 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/), versioning follows [SemVer](https://semver.org/).
5
+
6
+ ## [0.7.0] — 2026-10-25
7
+
8
+ ### Added
9
+ - **Dream reminder (C1)** — no scheduler dependency, no session-event writes.
10
+ `buildInjection` now computes whether a dream is due (`dreamAfterHours`, default
11
+ 24) or memories are due for review, and appends a two-line `### 睡梦` nudge to
12
+ the injected block. Zero cost when nothing is due; nothing to initialize on
13
+ install or clean up on uninstall.
14
+ - Config: `dreamReminder` (default `true`), `dreamAfterHours` (default `24`).
15
+ - `stats.capturedSinceDream` tracks new journal records since the last dream and
16
+ resets after `memory_dream`.
17
+ - Tests: reminder appears when due, disappears after a dream (51 assertions).
18
+
19
+ ### Branding
20
+ - New mark: a chubby fish whose body is a memory core — three memory strata,
21
+ a small neural net, RAM-style belly pins, and a puzzle-piece dorsal fin for
22
+ "plugin". Deliberately not the DeepSeek whale.
23
+ - `docs/logo.png` (with background), `docs/logo-transparent.png`,
24
+ `docs/banner.png` (1280×640 social preview), `icon.svg` (plugin icon),
25
+ regenerable with `python docs/make-art.py`.
26
+
27
+ ## [0.6.0] — 2026-10-25
28
+
29
+ ### Changed
30
+ - **Split the single-file implementation into modules** under `lib/`:
31
+ `config`, `util`, `store`, `inject`, `core`, `dream`, `panel`, `tools`, `lock`, `log`.
32
+ Entry point is now `index.mjs`. No behavior change intended.
33
+ - `package.json`: `engines`, `scripts`, `repository`/`homepage`/`bugs` metadata,
34
+ `files` whitelist, English description, version bump.
35
+
36
+ ### Added
37
+ - **Secret redaction for journal capture** (`redactSecrets`): message text is
38
+ filtered before it is appended to `journal/*.jsonl`, both at the call site and
39
+ inside `Store.appendJournal`. `[REDACTED]` marker on hit.
40
+ - **Cross-process write lock** (`lib/lock.mjs`): mutating tools acquire
41
+ `<dir>/.index.lock`, break stale locks after 15 s, time out after 5 s.
42
+ Mutating tool calls also re-read the index under the lock.
43
+ - **Optional debug logging** (`lib/log.mjs`, config `debug: true`) to
44
+ `<dir>/debug.log`, 1 MB rotation, no-op by default.
45
+ - **Config normalization** (`resolveConfig`): unknown keys ignored, numeric
46
+ fields coerced, `debug` added.
47
+ - `SECURITY.md`, `CHANGELOG.md`, `LICENSE`, `.gitignore`, CI workflow,
48
+ English README.
49
+ - Tests: journal redaction, lock release, P3 temporal assertions (46 total).
50
+
51
+ ### Fixed
52
+ - `memory_search` no longer returns values with `undefined` fields (lossless JSON).
53
+ - Historical versions are excluded from spaced-repetition reinforcement.
54
+
55
+ ## [0.5.0] — 2026-10-25
56
+
57
+ ### Added
58
+ - **Temporal validity (P3)**: `validFrom` / `validTo` / `history` per memory.
59
+ Same-topic updates seal the previous version into history (max 10).
60
+ - `memory_search({ asOf })` returns the version valid at that point in time;
61
+ default searches only current versions. Historical matches carry
62
+ `historical: true` and are not reinforced.
63
+ - Migration for pre-0.5 entries.
64
+
65
+ ## [0.4.0] — 2026-10-25
66
+
67
+ ### Added
68
+ - **Three-stage sleep (P0)**: SWS replay/compression, SHY salience-weighted
69
+ decay, REM associative links. Report includes all three phases.
70
+ - **Spaced repetition (P1)**: `stability`, `lastReview`, `nextReview`, `reviews`.
71
+ Search reinforces ×1.3, read ×1.1; due items marked `⏰` and listed in the
72
+ dream report.
73
+ - **Salience + novelty gate (P2)**: automatic 0–10 salience score, NOOP above
74
+ 0.92 similarity, update between 0.75 and 0.92, insert below 0.75.
75
+ - L1 hot index capped at 12 lines (working-memory calibration).
76
+
77
+ ## [0.3.0] — 2026-10-25
78
+
79
+ ### Added
80
+ - Client plugin: a **「小肥鱼记忆」 section in DSH Settings** (React panel).
81
+ Standalone `/memory-fish/panel` page kept as fallback.
82
+
83
+ ## [0.2.0] — 2026-10-25
84
+
85
+ ### Added
86
+ - **Auto-capture** of user/assistant messages into `journal/YYYY-MM-DD.jsonl`
87
+ (never injected; 30-day retention; subagent sessions skipped).
88
+ - **Sleep mechanism (`memory_dream`)**: reviews the journal, extracts
89
+ candidate memories via cue words, writes `dream/YYYY-MM-DD.md`.
90
+ - **Visual panel** at `/memory-fish/panel` with budget bar, profile editor,
91
+ search, pin/forget, dream/consolidate, reports.
92
+ - Host routes with same-origin guard.
93
+
94
+ ## [0.1.0] — 2026-10-25
95
+
96
+ ### Added
97
+ - Initial release: L0–L3 layered memory, 600-token injection budget,
98
+ write-time dedupe, decay/archival, secret refusal, nine tools,
99
+ Markdown/JSON storage, Settings-free first version.
package/DESIGN.md ADDED
@@ -0,0 +1,226 @@
1
+ # 小肥鱼记忆 · 设计草案 v0.2
2
+
3
+ > 目标:一个**上下文成本恒定**的轻量本地记忆插件。
4
+ > 记忆可以无限增长,但注入上下文的部分永远是固定的一小块。
5
+ >
6
+ > 作者:铁凝 × 小肥鱼 🐟
7
+
8
+ ---
9
+
10
+ ## v0.2 落地变更
11
+
12
+ - **自动捕获**:`session/event` 把 user/assistant 消息追加到 `journal/YYYY-MM-DD.jsonl`(L3 原始层,永不注入)。单条截断 4000 字符,子代理会话默认跳过。捕获零上下文成本。
13
+ - **睡梦机制**:`memory_dream` 回顾最近 N 天 journal,按线索词提炼候选 + 相似去重,生成 `dream/YYYY-MM-DD.md`;每天 23:30 定时任务驱动 Agent 复盘并落库,再 `memory_consolidate`。
14
+ - **可视化面板**:`GET /memory-fish/panel`(同源校验)+ `/memory-fish/api/state`、`/memory-fish/api/action`。展示预算条、条数卡片、画像编辑、记忆检索/置顶/遗忘、一键做梦/整理、最近睡梦报告。无构建步骤,纯 HTML。
15
+ - **代码已按模块拆分函数**:`saveMemoryCore`(工具/面板/做梦共用)、`buildDream`、`registerPanelRoutes`、`runPanelAction`、`trustedRequest`。
16
+
17
+ ## 0. 一句话设计
18
+
19
+ > **记忆的存储量是 O(∞),注入成本是 O(1),检索成本只在需要时发生。**
20
+
21
+ 现有的记忆插件(Claude Code 式、Markdown 索引式、知识图谱式)通病是:
22
+ 记忆越多 → 索引越长 → 每次会话注入越多 → 上下文越来越贵、越来越慢。
23
+
24
+ 我们要反过来:**记忆越多,系统越会"压缩",注入部分永远只有几百 token。**
25
+
26
+ ---
27
+
28
+ ## 1. 目标 / 非目标
29
+
30
+ ### 目标
31
+ - 跨会话记住铁凝的偏好、决策、结论
32
+ - 注入 token 有**硬预算**(默认 ~600 tokens),不随记忆数量增长
33
+ - 零服务端、零外部进程、零 embedding API、零额外 LLM 调用(可选增强除外)
34
+ - 记忆**人类可读**(Markdown),可 git 管理、可手工编辑、可一键删除
35
+ - 支持遗忘、纠错、覆盖(不是只增不减)
36
+
37
+ ### 非目标
38
+ - 不做知识图谱 / 向量数据库 / RAG 平台
39
+ - 不自动捕获每句对话(那会迅速变成垃圾场)
40
+ - 不做多设备同步
41
+ - 不替换系统提示里的其他上下文
42
+
43
+ ---
44
+
45
+ ## 2. 成本模型(先算账,再设计)
46
+
47
+ 一次会话的记忆注入成本 = `L0 + L1`,必须恒定。
48
+
49
+ | 层 | 内容 | 注入 | 预算 |
50
+ |---|---|---|---|
51
+ | **L0 画像** | 用户是谁、人设、沟通偏好、硬约束 | 每次会话 | ≤ 200 tokens |
52
+ | **L1 热索引** | 置顶记忆 + 最近 N 条的一行摘要 | 每次会话 | ≤ 400 tokens |
53
+ | **L2 温记忆** | 完整记忆正文 | 按需工具读取 | 0(常驻) |
54
+ | **L3 冷档案** | 归档、原始事件、被压缩的细节 | 只有 grep 命令能碰到 | 0 |
55
+
56
+ **换算**:中文标题 + 日期 + 标签 ≈ 25–30 tokens/行 → L1 预算 400 tokens ≈ **12–15 行**。
57
+ 所以"热索引"最多 15 条:置顶 5 条 + 最近 10 条。其余全部沉到 L2/L3。
58
+
59
+ L2/L3 不是删除,是"按需可取"——一次 `memory_search` 的成本只在真正需要时付。
60
+
61
+ > 关键点:注入放在 **session start**(稳定前缀),不随每轮消息变化。
62
+ > 稳定前缀能命中服务商的 prompt cache,实际成本远低于账面 token。
63
+
64
+ ---
65
+
66
+ ## 3. 四层架构
67
+
68
+ ```
69
+ ┌─────────────────────────────────────────────┐
70
+ │ L0 画像 (≤200 tok, 常驻) │
71
+ │ "铁凝:写代码/折腾电脑/关注新技术。 │
72
+ │ 自称小肥鱼。中文轻松语气。" │
73
+ ├─────────────────────────────────────────────┤
74
+ │ L1 热索引 (≤400 tok, 常驻) │
75
+ │ ★ 置顶:沟通偏好、项目约定(5 条) │
76
+ │ · 最近 10 条:标题 + 日期 + 2 个标签 │
77
+ │ → 冷区还有 37 条,memory_search 可查 │
78
+ ├─────────────────────────────────────────────┤
79
+ │ L2 温记忆 (磁盘, 按需) │
80
+ │ 每条 2–5 句,带来源、日期、重要度、失效时间 │
81
+ ├─────────────────────────────────────────────┤
82
+ │ L3 冷档案 (磁盘, 极少触碰) │
83
+ │ 被合并的旧版本、被衰减的条目、压缩前的原始记录 │
84
+ └─────────────────────────────────────────────┘
85
+ ```
86
+
87
+ **晋升 / 降级规则**
88
+ - 用户说"记住这个"或记忆被检索≥3 次 → 重要度↑,可晋升到 L1
89
+ - 30 天未被检索且重要度低 → 降级到 L3(归档,不删除)
90
+ - 同一主题出现新结论 → 旧条目标记 `superseded`,移出 L1,新旧之间保留链接
91
+
92
+ ---
93
+
94
+ ## 4. 防膨胀的六个机制
95
+
96
+ ### 4.1 写入门槛(Gate)
97
+ 不是所有对话都值得记。只在**结论时刻**写:
98
+ - 用户明确表达偏好/纠正(最高价值)
99
+ - 做出决策并说明理由
100
+ - 排障闭环(错误 → 根因 → 修复)
101
+ - 关键数字/ID/路径
102
+
103
+ 不写:临时状态、进行中的调试、仓库里能读到的、模型猜测。
104
+
105
+ ### 4.2 写入即去重(Dedupe)
106
+ 保存前做**字符 bigram Jaccard 相似度**(本地、零依赖):
107
+ - 相似度 > 0.75 → 判定同主题,**原地更新**(保留历史版本到 L3)
108
+ - 相似度 0.4–0.75 → 作为"相关记忆"链接,不新增
109
+ - 否则新增
110
+
111
+ > 这是控制增长的**第一道闸门**:重复才是膨胀的主因,而不是数量。
112
+
113
+ ### 4.3 定期压缩(Consolidation)
114
+ 触发条件(任一):L1 超过预算 / 新增 20 条 / 每周一次 / 用户手动 `/memory-consolidate`
115
+ 动作:
116
+ 1. 聚类(同标签或相似度 > 0.5)
117
+ 2. 每簇合并成一条"主题记忆"(保留要点 + 时间线)
118
+ 3. 合并前的原文全部进 L3 冷档案(可回溯)
119
+ 4. 重新生成 L1(只保留置顶 + 最近)
120
+
121
+ 压缩用**纯规则**(标题/标签/时间/相似度),不调 LLM —— 零额外 token。
122
+ 如果以后想要更聪明的合并,可以挂一个"可选:用便宜模型压缩"的开关,默认关闭。
123
+
124
+ ### 4.4 衰减(Decay)
125
+ 每条记忆维护 `score = importance × recency × (1 + hits)`
126
+ - `importance`:用户明说 5,决策 4,偏好 3,结论 2
127
+ - `recency`:指数衰减,半衰期 30 天
128
+ - `hits`:被检索次数
129
+
130
+ score 低于阈值的条目移出 L1 → L3。**只降级不删除**,除非用户明确"忘掉"。
131
+
132
+ ### 4.5 预算守卫(Budget Guard)
133
+ - 注入前统计 L0+L1 估算 token,超预算则自动执行压缩/裁剪
134
+ - 每次注入后记录:`injected_tokens`,写进统计文件
135
+ - 提供 `memory_stats` 工具:看记忆总数、L1 占多少 token、本月开销趋势
136
+
137
+ ### 4.6 冷热分离的检索
138
+ - 常驻只有 L1
139
+ - L2/L3 用 `memory_search`(BM25 + 字符 bigram,中文友好),**本地同步计算,<5ms**
140
+ - `memory_read` 按 id 取全文,支持截断
141
+ - 不做"每轮自动召回"(那会把上下文预算交给不可控的相关性判断);需要时我只花一次工具调用
142
+
143
+ ---
144
+
145
+ ## 5. 存储格式(人类可读 + 机器可查)
146
+
147
+ ```
148
+ memory/
149
+ ├── profile.md # L0,手写/我维护,很小
150
+ ├── index.json # L1 的机器索引 + 每条元数据(可重建)
151
+ ├── hot/ # 置顶与近期(软链或副本)
152
+ ├── memories/
153
+ │ └── 20261025-213821-prefer-pnpm.md
154
+ ├── archive/ # L3:压缩前原文、被替代版本
155
+ │ └── 20261025-213821-prefer-pnpm.md.1
156
+ └── stats.json # token 账本
157
+ ```
158
+
159
+ 单条记忆格式(Markdown + frontmatter):
160
+ ```markdown
161
+ ---
162
+ id: 20261025-213821-prefer-pnpm
163
+ created: 2026-10-25T21:38:21+08:00
164
+ importance: 4
165
+ hits: 0
166
+ supersedes: 20260910-120000-use-npm
167
+ tags: [工具链, 包管理]
168
+ status: active # active | superseded | archived
169
+ ---
170
+ # 用 pnpm,不用 npm
171
+
172
+ 这个项目统一用 pnpm(锁文件为 pnpm-lock.yaml)。
173
+ 理由:磁盘占用小、hoisted linker 与 DSH profile 兼容。
174
+ ```
175
+
176
+ **为什么不用 SQLite**:Markdown 可读、可 diff、可手工修、可 git;检索靠启动时构建内存索引(1 万条 ≈ 几 MB,毫秒级)。SQLite 作为可选加速层以后再加。
177
+
178
+ ---
179
+
180
+ ## 6. 工具面
181
+
182
+ | 工具 | 作用 | 成本 |
183
+ |---|---|---|
184
+ | `memory_save` | 保存/更新(自动去重),带门槛提示 | 一次调用 |
185
+ | `memory_search` | 本地关键词检索(L2+L3) | 毫秒级 |
186
+ | `memory_read` | 读全文(超长截断 + 给路径) | 一次调用 |
187
+ | `memory_list` | 最近 N 条(默认 8) | 一次调用 |
188
+ | `memory_pin` | 置顶/取消置顶(进 L1 头部) | 一次调用 |
189
+ | `memory_forget` | 遗忘(软删除 + tombstone,防复活) | 一次调用 |
190
+ | `memory_stats` | 预算与增长统计 | 一次调用 |
191
+ | `memory_consolidate` | 手动触发压缩(定期任务也会调) | 纯本地 |
192
+
193
+ ---
194
+
195
+ ## 7. 失败模式与对策
196
+
197
+ | 失败模式 | 对策 |
198
+ |---|---|
199
+ | 记忆越存越多,索引爆炸 | L1 硬预算 + 自动压缩 + 衰减 |
200
+ | 同一件事存了 5 遍 | 写入去重(相似度阈值) |
201
+ | 过期事实被当成现状 | 有效期字段 + supersede 链 + 状态可见 |
202
+ | 注入内容污染系统提示 | 注入块单独成节、可关闭、标注来源与日期 |
203
+ | 模型编造记忆 | 只允许工具写入;每条带来源会话 id |
204
+ | 删不掉 / 越删越乱 | 软删除 + tombstone + 冷档案可追溯 |
205
+ | 检索不准 | 关键词 + 标签 + 标题加权;不追求语义搜索 |
206
+
207
+ ---
208
+
209
+ ## 8. 路线图
210
+
211
+ - **M0 骨架(今晚)**:目录结构 + profile.md + index.json + `memory_save/search/read/list` + 注入 L0/L1 + 预算守卫
212
+ - **M1 防膨胀**:去重、pin、forget、stats、压缩(规则版)
213
+ - **M2 自动化**:定时压缩任务、衰减计算、冷热迁移
214
+ - **M3 体验**:Memory 面板(查看/编辑/统计)、导出/备份
215
+ - **M4 打磨**:测试(node:test)、错误处理、与 fooxe 插件共存/替代策略
216
+
217
+ ---
218
+
219
+ ## 9. 待铁凝拍板的决策
220
+
221
+ 1. **名字**:`dsh-memory-fish` / `小肥鱼记忆` / 其他?
222
+ 2. **自动捕获**:默认**不自动**(只在我判断值得记时写)还是允许"每轮对话后自动摘要"?
223
+ - 自动更省事,但增长快、噪音多;建议先手动,稳定后再加开关。
224
+ 3. **L1 预算**:400 tokens(≈13 行)够不够?还是 600?
225
+ 4. **是否要可视化面板**:M3 才有,先命令行/文件能不能接受?
226
+ 5. **和现有 `@fooxe/dsh-memory` 的关系**:先共存对比,还是直接替换?
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-memory-fish 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.
package/README.en.md ADDED
@@ -0,0 +1,121 @@
1
+ # dsh-memory-fish 🐟
2
+
3
+ ![banner](https://raw.githubusercontent.com/GitHub-yxm-hub/dsh-memory-fish/main/docs/banner.png)
4
+
5
+ > **Context-budgeted local memory for DeepSeek Harness.**
6
+ > Memory can grow forever; the prompt injection stays at a fixed ~600 tokens.
7
+ > It sleeps, it reviews, and it remembers what was true last month.
8
+
9
+ [中文说明](./README.md) · [Design](./DESIGN.md) · [Research](./RESEARCH.md) · [Changelog](./CHANGELOG.md) · [Security](./SECURITY.md)
10
+
11
+ ## Why
12
+
13
+ Most memory plugins inject an index that grows with your memory count — 500 memories, 500 lines in every prompt. It gets expensive and slow.
14
+
15
+ dsh-memory-fish splits *storage* from *injection*:
16
+
17
+ | Layer | Content | Injected | Budget |
18
+ |---|---|---|---|
19
+ | L0 profile | who you are, hard constraints | every session | ≤200 tok |
20
+ | L1 hot index | pinned + recent entries | every session | ≤400 tok |
21
+ | L2 warm memories | full text | on demand via tools | 0 |
22
+ | L3 cold archive | superseded/removed versions, raw journal | never | 0 |
23
+
24
+ **10 memories or 10,000 — the injected block stays the same size.**
25
+
26
+ ## Features
27
+
28
+ - **Zero dependencies, zero servers.** Plain Markdown + JSON under `~/.dsh/memory-fish/`.
29
+ - **Fixed token budget** (600 by default), enforced before injection.
30
+ - **Write-time dedupe + novelty gate.** Similarity ≥0.92 → no-op, 0.75–0.92 → in-place update, below → new entry.
31
+ - **Automatic salience scoring** (0–10): explicit "remember this", corrections, absolutes, novelty, pins.
32
+ - **Three-stage sleep** (`memory_dream`): SWS replay & compression → SHY salience-weighted decay → REM associative links.
33
+ - **Spaced repetition**: stability, nextReview, reviews; retrieval reinforces (×1.3 search, ×1.1 read); due items marked ⏰.
34
+ - **Temporal validity**: `validFrom`/`validTo` + version history; `memory_search({ asOf: "2026-09-15" })` answers "what did we use last month".
35
+ - **Auto-capture** of conversations into a raw journal (never injected, secrets redacted, 30-day retention).
36
+ - **Dream reminder (C1)** — no scheduled tasks, no session-event writes. When the last dream is older than `dreamAfterHours` (24h default) or memories are due for review, the injection block adds a two-line nudge. Zero cost when nothing is due; nothing to clean up on uninstall.
37
+ - **Optional visual panel**: a Settings section in DSH, plus a standalone page at `/memory-fish/panel`.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ # from npm (once published)
43
+ dsh plugin --profile web add dsh-memory-fish
44
+
45
+ # or from a local clone
46
+ dsh plugin --profile web add ./dsh-memory-fish
47
+ ```
48
+
49
+ Then restart DSH. Verify with a new chat: say *"remember: I prefer pnpm"* and look for a `memory_save` tool call.
50
+
51
+ Requirements: Node.js ≥ 22.19, DeepSeek Harness 0.2.x (tested on 0.2.1).
52
+
53
+ ## Tools
54
+
55
+ | Tool | Purpose |
56
+ |---|---|
57
+ | `memory_save` | Save/update a memory (dedupe, salience, versioning) |
58
+ | `memory_search` | Keyword search; `asOf` for historical versions; `includeArchived` for cold storage |
59
+ | `memory_read` | Read full text (>4000 chars truncated) and reinforce |
60
+ | `memory_list` | Recent memories |
61
+ | `memory_pin` | Pin/unpin (pinned never decays) |
62
+ | `memory_forget` | Soft delete + tombstone |
63
+ | `memory_profile_save` | Write the L0 profile |
64
+ | `memory_dream` | Run the three-stage sleep; returns phases, due-review list, candidates |
65
+ | `memory_stats` | Injection tokens, counts, salience avg, due count |
66
+ | `memory_consolidate` | Merge near-duplicates, archive weak memories (dry-run supported) |
67
+
68
+ ## Architecture
69
+
70
+ ```
71
+ index.mjs plugin entry: config, system-prompt injection, auto-capture, wiring
72
+ lib/config.mjs defaults + config normalization
73
+ lib/util.mjs tokens, bigrams/Jaccard, salience, spaced repetition, redaction
74
+ lib/store.mjs Markdown/JSON store, journal, archive, migration
75
+ lib/inject.mjs L0+L1 prompt block builder (budget-limited)
76
+ lib/core.mjs saveMemoryCore: dedupe / novelty gate / versioning
77
+ lib/dream.mjs three-stage sleep
78
+ lib/tools.mjs model-facing tools (mutations serialized by lock)
79
+ lib/panel.mjs local HTTP panel + API
80
+ lib/lock.mjs cross-process write lock
81
+ lib/log.mjs opt-in debug log
82
+ client/client.js Settings panel (browser half)
83
+ ```
84
+
85
+ ## Configuration
86
+
87
+ Pass config through the profile's `cordis.patch.yml`:
88
+
89
+ ```yaml
90
+ - id: memory-fish
91
+ config:
92
+ budgetTokens: 600
93
+ maxHotLines: 12
94
+ autoCapture: true
95
+ debug: false
96
+ ```
97
+
98
+ Key options: `dir`, `budgetTokens`, `profileTokens`, `maxHotLines`,
99
+ `archiveAfterDays`, `dedupeThreshold`, `noopThreshold`, `reviewEnabled`,
100
+ `autoCapture`, `captureMaxChars`, `journalKeepDays`, `dreamDays`,
101
+ `panelPath`, `apiBase`, `debug`.
102
+
103
+ ## Tests
104
+
105
+ ```bash
106
+ npm test # 46 assertions: capture, redaction, save/dedupe/NOOP, search,
107
+ # asOf history, sleep phases, panel routes, lock release, budget
108
+ ```
109
+
110
+ CI runs on Linux and Windows with Node 22 and 24.
111
+
112
+ ## Security
113
+
114
+ Local-first, no network. Journal writes redact API keys/tokens/passwords before
115
+ hitting disk. The HTTP panel is localhost-only with same-origin checks but **no
116
+ authentication** — run DSH on a trusted machine. See [SECURITY.md](./SECURITY.md).
117
+
118
+ ## License
119
+
120
+ MIT
121
+
package/README.md CHANGED
@@ -1,3 +1,170 @@
1
- # Temporary Holding Version
1
+ # 小肥鱼记忆 · dsh-memory-fish
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ![banner](https://raw.githubusercontent.com/GitHub-yxm-hub/dsh-memory-fish/main/docs/banner.png)
4
+
5
+ > 上下文成本恒定的本地记忆插件。记忆可以无限长,注入永远只有 ~600 tokens。
6
+ > 它会睡觉、会复习,也记得住"上个月我们用的什么方案"。
7
+
8
+ [English](./README.en.md) · [设计](./DESIGN.md) · [调研](./RESEARCH.md) · [更新日志](./CHANGELOG.md) · [安全](./SECURITY.md)
9
+
10
+ ## 它解决什么
11
+
12
+ 现有记忆插件的通病:记忆越多 → 索引越长 → 每次会话注入越多 → 又贵又慢。
13
+
14
+ 小肥鱼记忆把「存储」和「注入」拆开:
15
+
16
+ | 层 | 内容 | 注入 | 预算 |
17
+ |---|---|---|---|
18
+ | L0 画像 | 你是谁、偏好、硬约束 | 每次会话 | ≤200 tok |
19
+ | L1 热索引 | 置顶 + 最近 10–20 条标题 | 每次会话 | ≤400 tok |
20
+ | L2 温记忆 | 完整正文 | 按需工具读取 | 0 |
21
+ | L3 冷档案 | 压缩前原文、被替代版本 | 从不注入 | 0 |
22
+
23
+ 记住 10 条和记住 1000 条,注入成本一样。
24
+
25
+ ## 防膨胀机制
26
+
27
+ - **写入即去重**:字符 bigram Jaccard ≥ 0.75 判定同主题,原地更新,旧版本进冷档案
28
+ - **规则压缩**:`memory_consolidate` 合并近似条目(不调 LLM,零额外 token)
29
+ - **衰减归档**:60 天未更新、未置顶、低重要度 → 归档(可检索,不删除)
30
+ - **硬预算**:注入前按 token 预算裁剪,超了就少注入几行
31
+
32
+ ## v0.2 新功能
33
+
34
+ ### 自动捕获(默认开)
35
+ 每次会话的 user / assistant 消息自动追加到 `journal/YYYY-MM-DD.jsonl`(L3 原始层,**永不注入**,单条最多 4000 字符)。子代理会话默认跳过。捕获只是落盘,不产生任何上下文成本。
36
+
37
+ ### 睡梦机制
38
+ 每天 23:30 定时触发(也可手动 `memory_dream`):
39
+ 1. 回顾最近 N 天的全部对话 journal
40
+ 2. 按线索词(记住/我喜欢/统一用/以后都/约定…)提炼候选,相似候选自动去重
41
+ 3. 生成 `dream/YYYY-MM-DD.md` 报告
42
+ 4. Agent 逐条判断后 `memory_save` 值得留下的,再 `memory_consolidate`
43
+
44
+ ### 可视化面板
45
+ 宿主 webServer 上的本地页面:`/memory-fish/panel`
46
+ - 注入 token 预算条、活跃/归档/置顶/今日对话/候选线索卡片
47
+ - 画像编辑、记忆搜索、置顶/遗忘
48
+ - 一键「现在做梦」「整理记忆」、查看最近睡梦报告
49
+
50
+ 同源校验:拒绝跨站请求(Origin/Host/Sec-Fetch-Site),只允许 localhost / 本机 IP。
51
+
52
+ ## v0.4 生理学调优(P0/P1/P2)
53
+
54
+ ### 三阶段睡眠(P0)
55
+ `memory_dream` 现在按睡眠分期跑完整流程:
56
+ 1. **SWS 慢波**:重放 journal,合并相似记忆(原文进 archive)
57
+ 2. **SHY 稳态**:按显著性加权衰减,长期低分记忆降级归档
58
+ 3. **REM**:为共享标签/相似的记忆建立「相关链接」,不改内容
59
+
60
+ 报告写到 `dream/YYYY-MM-DD.md`,包含三阶段明细 + 待复习清单 + 候选记忆。
61
+
62
+ ### 睡梦提醒(C1:不依赖宿主调度器)
63
+ 插件**不创建任何定时任务**,也不写宿主的 session 事件日志。它只在注入块里按需提醒:
64
+
65
+ ```
66
+ ### 睡梦
67
+ - 距上次做梦 26 小时 · 新增对话 12 条 → 建议先 memory_dream 做三阶段整理
68
+ ```
69
+
70
+ 判断条件:`dreamReminder: true`(默认)且(距上次做梦 ≥ `dreamAfterHours`(默认 24)**或**有到期复习),并且确实有新对话或复习项。没有就完全不出现,零 token 成本。
71
+
72
+ 好处:新会话也有效、卸载后零残留、不依赖 `dsh-schedule` 的内部协议。
73
+
74
+ ### 间隔重复(P1)
75
+ 每条记忆维护 `stability`(能记多久)、`nextReview`(下次复习日)、`reviews`:
76
+ - 新记忆按重要度给初始稳定性(约 8–13 天)
77
+ - `memory_search` / `memory_read` 命中即加固(稳定性 ×1.3 / ×1.1),这就是提取练习效应
78
+ - 到期条目在热索引里标 `⏰`,睡眠报告里列成待复习清单
79
+
80
+ ### 显著性 + 新颖性门控(P2)
81
+ - 写入时自动打分(0–10):明说「记住」+4、纠正 +2、绝对化措辞 +1、新颖度 +0~2、置顶 +1
82
+ - 相似度 ≥0.92 → **NOOP**:完全重复不写,只记一次复习
83
+ - 0.75–0.92 → 原地更新;<0.75 → 新增
84
+
85
+ ## v0.5 时间有效性(P3)
86
+
87
+ 每条记忆带有效期:`validFrom` / `validTo`,同主题内容变化时:
88
+
89
+ - 旧版本封存进 `history`(含自己的 validFrom → validTo),当前版本换新有效期
90
+ - `memory_search` 默认只返回**当前有效**版本
91
+ - `memory_search({ asOf: "2026-09-15" })` 按时间点查当时有效的版本 —— 能回答"上个月我们用什么方案"
92
+ - 历史版本不参与复习加固,避免旧事实被强化
93
+
94
+ ## 工具
95
+
96
+ | 工具 | 作用 |
97
+ |---|---|
98
+ | `memory_save` | 保存/原地更新(自动去重、拒存密钥) |
99
+ | `memory_search` | 本地关键词检索(含归档,可选) |
100
+ | `memory_read` | 读全文(>4000 字符截断) |
101
+ | `memory_list` | 最近记忆 |
102
+ | `memory_pin` | 置顶/取消(置顶不参与衰减) |
103
+ | `memory_forget` | 软删除 + 墓碑 |
104
+ | `memory_profile_save` | 写 L0 画像 |
105
+ | `memory_dream` | 睡梦:回顾对话、提炼候选、写报告 |
106
+ | `memory_stats` | 查看注入 token / 条数 / 磁盘占用 / 捕获数 |
107
+ | `memory_consolidate` | 手动整理(dry-run 可选) |
108
+
109
+ ## 数据位置
110
+
111
+ ```
112
+ ~/.dsh/memory-fish/
113
+ ├── profile.md # L0
114
+ ├── index.json # 元数据索引(可重建)
115
+ ├── memories/ # L2:每条一个 Markdown
116
+ ├── archive/ # L3:旧版本、被替代、被遗忘的原文
117
+ ├── journal/ # 自动捕获的原始对话(按天 jsonl,永不注入)
118
+ ├── dream/ # 睡梦报告(按天 md)
119
+ └── stats.json # 计数
120
+ ```
121
+
122
+ 全部人类可读、可 git、可手改。索引丢失会从 `memories/*.md` 自动重建。
123
+
124
+ ## 安装
125
+
126
+ 注册进 DSH profile:
127
+
128
+ 1. 把本目录复制到 `<profile>/node_modules/dsh-memory-fish/`
129
+ 2. 在 `<profile>/package.json` 的 `dsh.profile.bundles` 加入 `"dsh-memory-fish"`,
130
+ 并在 `dependencies` 加入 `"dsh-memory-fish": "file:projects/memory-fish"`
131
+ 3. 重启 DSH(或等待 profile 热加载)
132
+
133
+ ## 代码结构
134
+
135
+ ```
136
+ index.mjs 插件入口:配置、注入、自动捕获、装配
137
+ lib/config.mjs 默认值 + 配置校验
138
+ lib/util.mjs token 估算、bigram/Jaccard、显著性、间隔重复、脱敏
139
+ lib/store.mjs Markdown/JSON 存储、journal、归档、迁移
140
+ lib/inject.mjs L0+L1 注入块(预算裁剪)
141
+ lib/core.mjs saveMemoryCore:去重 / 新颖性门控 / 版本
142
+ lib/dream.mjs 三阶段睡眠
143
+ lib/tools.mjs 模型工具(写操作加锁串行)
144
+ lib/panel.mjs 本地 HTTP 面板 + API
145
+ lib/lock.mjs 跨进程写锁
146
+ lib/log.mjs 可选调试日志
147
+ client/client.js 设置页面板(浏览器半)
148
+ ```
149
+
150
+ ## 测试
151
+
152
+ ```bash
153
+ npm test # 49 项断言:捕获、脱敏、保存/去重/NOOP、检索、asOf 历史、
154
+ # 睡眠三阶段、面板路由、锁释放、预算
155
+ ```
156
+
157
+ CI 在 Linux / Windows × Node 22 / 24 上跑。
158
+
159
+ ## 安全
160
+
161
+ 本地优先、无网络。journal 落盘前会脱敏密钥;HTTP 面板仅限 localhost 且有同源校验,但**没有鉴权**——请只在可信机器上运行。详见 [SECURITY.md](./SECURITY.md)。
162
+
163
+ ## 设计
164
+
165
+ 见 [DESIGN.md](./DESIGN.md)。
166
+
167
+ ## License
168
+
169
+ MIT
170
+