@chendpoc/pi-memory 0.2.0 → 0.2.1

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 (54) hide show
  1. package/README.md +320 -60
  2. package/dist/config/index.d.ts +1 -0
  3. package/dist/config/index.d.ts.map +1 -1
  4. package/dist/config/index.js +1 -0
  5. package/dist/config/index.js.map +1 -1
  6. package/dist/config/preflight.d.ts +8 -0
  7. package/dist/config/preflight.d.ts.map +1 -0
  8. package/dist/config/preflight.js +22 -0
  9. package/dist/config/preflight.js.map +1 -0
  10. package/dist/constants/env.d.ts +3 -0
  11. package/dist/constants/env.d.ts.map +1 -1
  12. package/dist/constants/env.js +3 -0
  13. package/dist/constants/env.js.map +1 -1
  14. package/dist/constants/preflight.d.ts +7 -0
  15. package/dist/constants/preflight.d.ts.map +1 -1
  16. package/dist/constants/preflight.js +7 -0
  17. package/dist/constants/preflight.js.map +1 -1
  18. package/dist/constants/timing.d.ts +2 -0
  19. package/dist/constants/timing.d.ts.map +1 -1
  20. package/dist/constants/timing.js +2 -0
  21. package/dist/constants/timing.js.map +1 -1
  22. package/dist/pi-extension.d.ts.map +1 -1
  23. package/dist/pi-extension.js +21 -2
  24. package/dist/pi-extension.js.map +1 -1
  25. package/dist/preflight/episodic.d.ts +7 -1
  26. package/dist/preflight/episodic.d.ts.map +1 -1
  27. package/dist/preflight/episodic.js +20 -4
  28. package/dist/preflight/episodic.js.map +1 -1
  29. package/dist/preflight/intentCache.d.ts +13 -0
  30. package/dist/preflight/intentCache.d.ts.map +1 -0
  31. package/dist/preflight/intentCache.js +37 -0
  32. package/dist/preflight/intentCache.js.map +1 -0
  33. package/dist/preflight/queryIntent.d.ts +14 -5
  34. package/dist/preflight/queryIntent.d.ts.map +1 -1
  35. package/dist/preflight/queryIntent.js +27 -11
  36. package/dist/preflight/queryIntent.js.map +1 -1
  37. package/dist/sidecar/warmup.d.ts +6 -0
  38. package/dist/sidecar/warmup.d.ts.map +1 -0
  39. package/dist/sidecar/warmup.js +39 -0
  40. package/dist/sidecar/warmup.js.map +1 -0
  41. package/doc/README-zh.md +365 -0
  42. package/doc/ROADMAP-zh.md +52 -0
  43. package/doc/ROADMAP.md +52 -0
  44. package/package.json +4 -1
  45. package/src/config/index.ts +1 -0
  46. package/src/config/preflight.ts +34 -0
  47. package/src/constants/env.ts +3 -0
  48. package/src/constants/preflight.ts +9 -0
  49. package/src/constants/timing.ts +2 -0
  50. package/src/pi-extension.ts +22 -2
  51. package/src/preflight/episodic.ts +28 -4
  52. package/src/preflight/intentCache.ts +44 -0
  53. package/src/preflight/queryIntent.ts +46 -12
  54. package/src/sidecar/warmup.ts +50 -0
@@ -0,0 +1,365 @@
1
+ # @chendpoc/pi-memory
2
+
3
+ <p align="center">
4
+ <a href="../README.md">English</a> |
5
+ <a href="README-zh.md">简体中文</a>
6
+ </p>
7
+
8
+ Pi coding agent 的跨 session 情景记忆扩展。
9
+
10
+ `pi-memory` 给 Pi 增加一层本地、可审计的跨 session 记忆。它把长期事实保存在 **`MEMORY.md` 这个 Ground Truth** 里,把 `memory.vec.sqlite` 作为派生向量索引,并在主模型回答前通过 Preflight 注入相关的私有上下文。
11
+
12
+ ## 这个 Package 做什么
13
+
14
+ Pi 已经有长 session compaction,它解决的是“当前对话太长了怎么办”;但它不解决“新开 session 后忘记用户偏好、项目约定、历史决策和未完成 TODO”的问题。
15
+
16
+ `pi-memory` 补的是这条链路:
17
+
18
+ ```text
19
+ 长期事实 -> MEMORY.md -> 派生向量索引 -> 每轮 Preflight 召回
20
+ ```
21
+
22
+ 它提供:
23
+
24
+ - 通过 `/remember` 写入显式记忆。
25
+ - 从 Pi compaction 自动导出长期事实。
26
+ - 用 Shutdown Queue 补捞短 session 或漏处理 session。
27
+ - 在主模型运行前做每轮私有记忆召回。
28
+ - 用 Markdown 保存人类可编辑的事实,向量搜索只是可重建索引。
29
+ - 离线 maintenance 负责 consolidate、去重、overflow 处理和 queue drain。
30
+
31
+ ## 安装与启用
32
+
33
+ 要求:
34
+
35
+ - Node.js `>=24 <25`
36
+ - pnpm
37
+ - Pi 提供的 extension runtime packages
38
+
39
+ 在 Pi extension 环境中安装包:
40
+
41
+ ```bash
42
+ pnpm add @chendpoc/pi-memory
43
+ ```
44
+
45
+ 从本仓库本地开发:
46
+
47
+ ```bash
48
+ pnpm install
49
+ pnpm build
50
+ pnpm typecheck
51
+ pnpm test
52
+ ```
53
+
54
+ 通过 Pi 的 extension 加载机制启用。本包声明:
55
+
56
+ ```json
57
+ {
58
+ "pi": {
59
+ "extensions": ["./src/pi-extension.ts"]
60
+ }
61
+ }
62
+ ```
63
+
64
+ 需要时可显式初始化记忆:
65
+
66
+ ```bash
67
+ pi-memory init
68
+ ```
69
+
70
+ 初始化不会覆盖非空 `MEMORY.md`。
71
+
72
+ ## 为什么选择 `pi-memory`
73
+
74
+ ### Agent 使用前后差异
75
+
76
+ | 场景 | 未使用 `pi-memory` | 使用 `pi-memory` 后 |
77
+ | --- | --- | --- |
78
+ | 新 session 问“继续上次计划” | Agent 只能追问上下文,或基于当前仓库猜测。 | Preflight 召回匹配的 `MEMORY.md` 事实并注入私有参考上下文。 |
79
+ | 用户说“记住这个 repo 用 Vitest” | 事实可能只留在当前 session 摘要里。 | `/remember` 写入 `[user]` 条目,consolidate 必须保留。 |
80
+ | 长 session 触发压缩 | Compaction 只帮助当前 session 续聊,不必然产生跨 session 事实。 | 一次 dual-purpose summary 同时保留当前上下文并导出长期事实。 |
81
+ | 启动 subagent | 可能重复父 session 的召回和记忆写入。 | Subagent 只拿 Memory Cap,只写新增 Compact Delta。 |
82
+ | 向量 sidecar 不可用 | 如果是硬依赖,会直接影响对话。 | Preflight 静默回退到 Markdown 或空注入,主模型照常运行。 |
83
+ | 记忆持续增长 | 文件容易变成无边界流水账。 | `MEMORY.md` 150 行上限,`auto-*.md` 溢出,consolidate 合并去重。 |
84
+
85
+ ### 核心优势
86
+
87
+ - **Markdown Ground Truth**:`MEMORY.md` 和 `auto-*.md` 可以直接打开、审查、编辑、grep、复制或纳入版本控制。
88
+ - **索引不是隐藏事实源**:`memory.vec.sqlite` 可以删除,并从 Markdown 重新构建。
89
+ - **Preflight 召回**:记忆在主模型回答前注入,而不是依赖模型主动调用搜索工具。
90
+ - **热路径有预算**:默认 Preflight 预算是 **800ms**,QueryIntent、sidecar query 和 fallback 都有边界。
91
+ - **用户显式记忆受保护**:`/remember` 写入 `[user]` 条目,consolidate 不能删除或改写。
92
+ - **Sidecar 隔离**:embedding、向量扫描、MMR、stats 和 reindex 在扩展进程外运行;写入仍由 `MemoryStore` 负责。
93
+ - **Subagent 策略**:root session 跑 Memory Cap + Episodic Preflight;subagent 默认只拿 Memory Cap。
94
+ - **优雅降级**:sidecar 召回为空、超时或不可用时,当前 turn 仍然继续。
95
+
96
+ ### 对比
97
+
98
+ `pi-memory` 不试图成为所有 memory 系统的集合。它的价值是一个 Pi-native 闭环:Markdown ground truth、Preflight 注入、sidecar 检索、compaction export 和离线 maintenance。
99
+
100
+ | 系统 | 优势 | 与 `@chendpoc/pi-memory` 的差异 |
101
+ | --- | --- | --- |
102
+ | Cursor Rules / OpenCode `AGENTS.md` | 静态项目指令,注入行为可预测。 | 主要是用户手写规则,没有自动长期事实提取,也没有每轮 Episodic Preflight。 |
103
+ | Claude Code Auto Memory | Agent 可以写本地记忆文件。 | 也是文件记忆,但没有 sidecar 向量召回,也没有 Pi compact/shutdown 集成。 |
104
+ | `pi-hermes-memory` | 功能丰富,有 FTS5、失败记忆、纠正学习、安全扫描。 | 自动化更重;但没有 `<private_memory>` Preflight 闭环,也没有 sidecar 派生向量索引。 |
105
+ | OpenClaw memory-core | 成熟的文件+索引设计,有 dreaming、混合搜索、本地 embedding。 | OpenClaw 是更大的 memory 平台;`pi-memory` 更窄,聚焦 Pi extension。 |
106
+ | Mem0 / Zep | 托管 memory API,有混合搜索、图和时序建模。 | 检索基础设施更强,但更偏外部服务/数据库,不以 Markdown 事实源为第一原则。 |
107
+ | Letta | 上下文工程,git-backed memory repo 和 sleep-time compute。 | 自主记忆管理更强,但心智模型比 Pi extension lifecycle 更重。 |
108
+ | Cognee | 知识引擎,图/向量/关系存储和多种检索模式。 | 更适合知识图谱;对轻量编码 agent 偏好/约定来说偏重。 |
109
+
110
+ 其他系统更强的地方:
111
+
112
+ - `pi-hermes-memory`:失败记忆、纠正检测、工具怪癖、安全扫描。
113
+ - OpenClaw:dreaming 阶段、memory wiki、FTS/vector 混合搜索、本地 embedding provider。
114
+ - Zep/Cognee:时序图推理和多跳图检索。
115
+ - Mem0:托管多租户 memory API。
116
+ - Letta:自主 context repository 和 sleep-time memory work。
117
+
118
+ ## 工作方式
119
+
120
+ ### 架构
121
+
122
+ ```text
123
+ Pi 扩展进程
124
+ |- session_start
125
+ | |- 初始化 MEMORY.md
126
+ | |- 启动/warm sidecar
127
+ | |- 重建派生向量索引
128
+ | `- 预加载 Memory Cap
129
+ |
130
+ |- before_agent_start / context
131
+ | `- Preflight recall -> <private_memory> 注入
132
+ |
133
+ |- /remember
134
+ | `- 追加 [user] Memory Entry
135
+ |
136
+ |- session_before_compact / session_compact
137
+ | `- dual-purpose summary -> Memory Export ingest
138
+ |
139
+ |- session_shutdown
140
+ | `- 只追加 shutdown 元数据
141
+ |
142
+ `- consolidate scheduler
143
+ `- 合并/去重 -> 重写 Ground Truth -> reindex
144
+
145
+ 通过 UDS JSONL 通信的 Sidecar 进程
146
+ |- ping
147
+ |- stats
148
+ |- query: embed -> cosine scan -> MMR
149
+ `- reindex: upsert chunks into memory.vec.sqlite
150
+ ```
151
+
152
+ ### 读取路径
153
+
154
+ Root session:
155
+
156
+ ```text
157
+ 来自 Ground Truth 的 Memory Cap
158
+ + 当前用户消息的 Episodic Preflight
159
+ -> 合并为 <private_memory>
160
+ ```
161
+
162
+ Subagent session:
163
+
164
+ ```text
165
+ 只使用 Memory Cap
166
+ -> 默认不跑 episodic QueryIntent / sidecar query
167
+ ```
168
+
169
+ 回退链:
170
+
171
+ ```text
172
+ Sidecar 结果
173
+ -> 空结果、错误或超时:回退到 MEMORY.md
174
+ -> 仍为空:不注入
175
+ ```
176
+
177
+ ### 写入路径
178
+
179
+ | 路径 | 触发 | LLM? | 是否阻塞 | 目的 |
180
+ | --- | --- | --- | --- | --- |
181
+ | `/remember` | 用户命令 | 否 | 是 | 显式长期记忆 |
182
+ | Compaction | `session_before_compact` + `session_compact` | 一次摘要调用 | 摘要阻塞,入库后台执行 | 当前 session 续聊 + 导出长期事实 |
183
+ | Shutdown Queue | `session_shutdown` + `pi-memory maintenance` | 仅离线且无 compaction summary 时 | shutdown 时不阻塞 | 补捞短 session 或漏处理事实 |
184
+ | Consolidate | overflow >= 12、7 天或每日 cron | 可选 | 离线或后台 | 去重、合并、清理过期 TODO |
185
+
186
+ ## 数据和 MEMORY.md 格式
187
+
188
+ 所有产物都放在同一个 memory agent directory 下。
189
+
190
+ 解析顺序:
191
+
192
+ 1. `--agent-dir` CLI flag
193
+ 2. `PI_MEMORY_AGENT_DIR`
194
+ 3. 默认 `~/.pi/pi-memory-data`
195
+
196
+ | 文件 | 作用 |
197
+ | --- | --- |
198
+ | `MEMORY.md` | 事实源文件 |
199
+ | `auto-*.md` | 150 行上限后的溢出文件 |
200
+ | `.memory_gc` | 上次 consolidate 时间 |
201
+ | `.memory_compactions.json` | compaction 幂等状态 |
202
+ | `.memory_shutdown_queue.jsonl` | append-only shutdown 元数据 |
203
+ | `.memory_shutdown_processed.json` | drain 幂等状态 |
204
+ | `memory.vec.sqlite` | 派生向量索引 |
205
+ | `memory.sock` | Sidecar UDS socket |
206
+
207
+ 标准模板:[`templates/MEMORY.md.example`](../templates/MEMORY.md.example)
208
+
209
+ ```markdown
210
+ # Memory
211
+
212
+ ## Preferences
213
+
214
+ ## Conventions
215
+
216
+ ## Findings
217
+
218
+ ## Todos
219
+ ```
220
+
221
+ 每条记忆是一个 Markdown bullet:
222
+
223
+ ```markdown
224
+ - [user] Prefer pnpm over npm <!-- id:abc123 user ts:2026-07-04T09:00:00.000+08:00 -->
225
+ - Project tests use Vitest <!-- id:def456 ts:2026-07-04T09:05:00.000+08:00 -->
226
+ ```
227
+
228
+ 规则:
229
+
230
+ - `/remember` 写入 `[user]` 条目。
231
+ - Consolidate 不能删除或改写 `[user]` 条目。
232
+ - `MEMORY.md` 上限为 150 行。
233
+ - 溢出条目写入 `auto-*.md`,并在 `MEMORY.md` 留指针。
234
+ - 向量 chunk 从条目派生;默认超过 `PI_MEMORY_CHUNK_MAX_CHARS=512` 的长条目会拆分。
235
+
236
+ ## 配置
237
+
238
+ 可选 env 文件按以下顺序加载:
239
+
240
+ 1. `PI_MEMORY_ENV_FILE`
241
+ 2. 项目 `.env`
242
+ 3. 项目 `.env.local`
243
+ 4. `~/.pi/agent/pi-memory.env`
244
+
245
+ 常用变量:
246
+
247
+ | 变量 | 默认 | 用途 |
248
+ | --- | --- | --- |
249
+ | `PI_MEMORY_AGENT_DIR` | `~/.pi/pi-memory-data` | 记忆数据根目录 |
250
+ | `PI_MEMORY_EMBEDDER` | `hash` | 支持 `hash`、`ollama`、`openai` |
251
+ | `PI_MEMORY_HELPER_MODEL` | `deepseek/deepseek-v4-flash` | QueryIntent 和 consolidate 使用的辅助模型 |
252
+ | `PI_MEMORY_PREFLIGHT_BUDGET_MS` | `800` | Preflight 共享预算,限制在 250-1500ms |
253
+ | `PI_MEMORY_INTENT_RETRIES` | `0` | 首次尝试后的 helper LLM 重试次数 |
254
+ | `PI_MEMORY_WARM_SIDECAR` | `1` | 在 `session_start` warm sidecar |
255
+ | `PI_MEMORY_INTENT_CACHE` | `1` | session 级 QueryIntent 缓存 |
256
+ | `PI_MEMORY_REINDEX_DEBOUNCE_MS` | `500` | 写入后的 sidecar reindex debounce |
257
+ | `PI_MEMORY_TOP_K` | `3` | 向量召回条数 |
258
+ | `PI_MEMORY_MMR_LAMBDA` | `0.8` | MMR 相关性与多样性权重 |
259
+ | `PI_MEMORY_MIN_RELEVANCE` | `0.4` | 最小 cosine similarity |
260
+ | `PI_MEMORY_CHUNK_MAX_CHARS` | `512` | 索引长条目拆分阈值;`0` 表示关闭 |
261
+ | `PI_MEMORY_DEBUG` | 未设置 | `1` 打印 debug timing logs |
262
+
263
+ 完整列表见 [`.env.example`](../.env.example)。
264
+
265
+ ### Embedding Provider
266
+
267
+ | Embedder | 适用场景 | 说明 |
268
+ | --- | --- | --- |
269
+ | `hash` | 零配置本地开发 | 离线、确定性、语义质量较低 |
270
+ | `ollama` | 本地语义 embedding | 使用 `PI_MEMORY_OLLAMA_BASE_URL` 和 `PI_MEMORY_OLLAMA_EMBED_MODEL` |
271
+ | `openai` | 更高质量云端 embedding | 需要 `PI_MEMORY_OPENAI_API_KEY` 或 `OPENAI_API_KEY` |
272
+
273
+ Vector Index 会保存 embedding provider、model 和 dimension 元数据。配置变化时会清空旧 chunks 并重建。
274
+
275
+ ## 命令
276
+
277
+ Pi 内部:
278
+
279
+ ```text
280
+ /remember [section] <content>
281
+ /memory-status [refresh|expand|collapse|hide]
282
+ ```
283
+
284
+ CLI:
285
+
286
+ ```bash
287
+ pi-memory init
288
+ pi-memory status
289
+ pi-memory maintenance --cron --verbose
290
+ pi-memory consolidate --force --verbose
291
+ pi-memory drain-shutdown-queue --verbose
292
+ ```
293
+
294
+ `maintenance` 是推荐的调度入口:
295
+
296
+ ```text
297
+ consolidate -> drain-shutdown-queue
298
+ ```
299
+
300
+ 调度模板:
301
+
302
+ - [`templates/com.pi.memory.consolidate.plist.example`](../templates/com.pi.memory.consolidate.plist.example)
303
+ - [`templates/crontab.example`](../templates/crontab.example)
304
+ - [`templates/consolidate.cmd.example`](../templates/consolidate.cmd.example)
305
+ - [`templates/schtasks.example.txt`](../templates/schtasks.example.txt)
306
+
307
+ ## 诊断
308
+
309
+ 使用 `/memory-status` 或 `pi-memory status` 检查:
310
+
311
+ - memory agent 目录
312
+ - `MEMORY.md` 行数
313
+ - 条目数
314
+ - overflow 文件数
315
+ - 上次 consolidate 时间
316
+ - sidecar socket 状态
317
+ - vector index generation 和 chunk 数
318
+ - 当前配置的 embedder
319
+ - 索引 embedder 是否与当前配置不一致
320
+
321
+ 设置 `PI_MEMORY_DEBUG=1` 可打印 Preflight timing 日志:
322
+
323
+ ```json
324
+ {
325
+ "phase": "preflight",
326
+ "event": "recall",
327
+ "intent_ms": 0,
328
+ "intent_skipped": true,
329
+ "intent_cache_hit": false,
330
+ "sidecar_ms": 42,
331
+ "cache_hit": true,
332
+ "total_ms": 45,
333
+ "fallback": false,
334
+ "results": 3
335
+ }
336
+ ```
337
+
338
+ ## 非目标
339
+
340
+ - 不替代 Pi compaction。
341
+ - 不替代 session search;历史对话搜索应使用专门的 session-search 扩展。
342
+ - 不在本包内维护图数据库。
343
+ - 不让 sidecar 成为事实源。
344
+ - 不把完整聊天记录当作 memory 存储。
345
+ - 不给每轮用户消息增加多秒级 reflection。
346
+
347
+ ## 开发
348
+
349
+ ```bash
350
+ pnpm typecheck
351
+ pnpm test
352
+ pnpm build
353
+ ```
354
+
355
+ sidecar IPC 测试会打开 Unix domain socket。如果在受限沙盒中因为 `listen EPERM` 失败,请在正常本地 shell 中运行。
356
+
357
+ ## 文档
358
+
359
+ - [English README](../README.md)
360
+ - [路线图](./ROADMAP-zh.md)
361
+ - [UBIQUITOUS_LANGUAGE.md](../UBIQUITOUS_LANGUAGE.md) - 领域术语表
362
+
363
+ ## 许可证
364
+
365
+ MIT
@@ -0,0 +1,52 @@
1
+ # @chendpoc/pi-memory 路线图
2
+
3
+ <p align="center">
4
+ <a href="ROADMAP.md">English</a> |
5
+ <a href="ROADMAP-zh.md">简体中文</a>
6
+ </p>
7
+
8
+ 本文档记录 `@chendpoc/pi-memory` 的产品方向。README 保持聚焦,只讲定位、安装和当前架构。
9
+
10
+ ## 当前基础
11
+
12
+ - 带 overflow 的 `MEMORY.md` Ground Truth。
13
+ - `/remember` 和 `/memory-status`。
14
+ - 基于 UDS JSONL 的 Sidecar。
15
+ - `memory.vec.sqlite` 向量索引。
16
+ - QueryIntent + raw-query fallback。
17
+ - 800ms 共享 Preflight 预算。
18
+ - sidecar warm、intent cache、query cache。
19
+ - dual-purpose compaction summary。
20
+ - Shutdown Queue + `maintenance`。
21
+ - Consolidate + reindex。
22
+ - Subagent Memory Cap + Compact Delta。
23
+
24
+ ## P0:信任与安全
25
+
26
+ - 记忆写入前做 secret/token redaction。
27
+ - 对 LLM 生成的 Memory Export 增加 prompt-injection 防护。
28
+ - 针对用户明确纠正做 correction detector。
29
+ - 为跳过写入和 fallback 原因提供更好的诊断。
30
+
31
+ ## P1:召回质量
32
+
33
+ - 对 `MEMORY.md` 条目做 lexical + vector 混合召回。
34
+ - 为常见 coding-agent 问题建立 recall eval fixtures。
35
+ - debug metrics 拆分 intent、embed、scan、MMR、render。
36
+ - 延迟预算允许时,在 MMR 后增加可选 reranker。
37
+ - 改进 Ollama 之外的本地 embedding provider。
38
+
39
+ ## P2:记忆生命周期
40
+
41
+ - 增加 failure 和 tool-quirk 类别。
42
+ - 晋升前提供人类可审查的 memory draft 或 diary。
43
+ - 基于使用信号的晋升和裁剪。
44
+ - rewrite 前提供更安全的 consolidate preview。
45
+ - 显式处理 stale fact,而不是只做简单 TODO 清理。
46
+
47
+ ## P3:产品表面
48
+
49
+ - 更好的 TUI status panel。
50
+ - memory edit/review 命令。
51
+ - 不同 Pi 安装之间的 import/export。
52
+ - 常见 Pi 配置的文档 recipes。
package/doc/ROADMAP.md ADDED
@@ -0,0 +1,52 @@
1
+ # @chendpoc/pi-memory Roadmap
2
+
3
+ <p align="center">
4
+ <a href="ROADMAP.md">English</a> |
5
+ <a href="ROADMAP-zh.md">简体中文</a>
6
+ </p>
7
+
8
+ This roadmap tracks product direction for `@chendpoc/pi-memory`. The README stays focused on positioning, installation, and the current architecture.
9
+
10
+ ## Current Foundation
11
+
12
+ - `MEMORY.md` Ground Truth with overflow.
13
+ - `/remember` and `/memory-status`.
14
+ - Sidecar over UDS JSONL.
15
+ - `memory.vec.sqlite` vector index.
16
+ - QueryIntent with raw-query fallback.
17
+ - 800ms shared Preflight budget.
18
+ - Warm sidecar, intent cache, query cache.
19
+ - Dual-purpose compaction summary.
20
+ - Shutdown Queue + `maintenance`.
21
+ - Consolidate + reindex.
22
+ - Subagent Memory Cap + Compact Delta.
23
+
24
+ ## P0: Trust And Safety
25
+
26
+ - Secret and token redaction before memory writes.
27
+ - Prompt-injection guardrails for LLM-generated Memory Export.
28
+ - Correction detector for explicit user corrections.
29
+ - Better diagnostics for skipped writes and fallback reasons.
30
+
31
+ ## P1: Recall Quality
32
+
33
+ - Hybrid lexical + vector recall for `MEMORY.md` entries.
34
+ - Recall eval fixtures for common coding-agent questions.
35
+ - Debug metrics split by intent, embed, scan, MMR, render.
36
+ - Optional reranker after MMR when latency budget allows.
37
+ - Local embedding provider improvements beyond Ollama.
38
+
39
+ ## P2: Memory Lifecycle
40
+
41
+ - Failure and tool-quirk categories.
42
+ - Human-reviewable memory draft or diary before promotion.
43
+ - Usage-weighted promotion and pruning.
44
+ - Safer consolidate previews before rewrite.
45
+ - Explicit stale fact handling instead of simple TODO pruning.
46
+
47
+ ## P3: Product Surface
48
+
49
+ - Better TUI status panel.
50
+ - Memory edit/review commands.
51
+ - Import/export between Pi installations.
52
+ - Documentation recipes for common Pi setups.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chendpoc/pi-memory",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Cross-session episodic memory for Pi agent — MEMORY.md ground truth, JSONL sidecar retrieval, and Preflight injection",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,6 +45,9 @@
45
45
  "scripts/platform-fs.mjs",
46
46
  "scripts/postinstall.mjs",
47
47
  "README.md",
48
+ "doc/README-zh.md",
49
+ "doc/ROADMAP.md",
50
+ "doc/ROADMAP-zh.md",
48
51
  "UBIQUITOUS_LANGUAGE.md"
49
52
  ],
50
53
  "engines": {
@@ -2,5 +2,6 @@ export { KNOWN_EMBED_DIMS, readPiMemoryEnv, resolveEmbedDim, type EmbedderProvid
2
2
  export { readRetrievalConfig, type RetrievalConfig } from "./retrieval.js";
3
3
  export { readChunkingConfig, type ChunkingConfig } from "./chunking.js";
4
4
  export { resolvePreflightBudget, type PreflightBudget } from "./preflightBudget.js";
5
+ export { readPreflightRuntimeConfig, type PreflightRuntimeConfig } from "./preflight.js";
5
6
  export { resolveAgentDirFromEnv, resolveMemoryAgentDir, type ResolveMemoryAgentDirOptions } from "./agentDir.js";
6
7
  export { loadEnv } from "./loadEnv.js";
@@ -0,0 +1,34 @@
1
+ import { ENV_KEYS } from "../constants/env.js";
2
+ import {
3
+ DEFAULT_INTENT_CACHE_ENABLED,
4
+ DEFAULT_INTENT_RETRIES,
5
+ DEFAULT_WARM_SIDECAR_ENABLED,
6
+ MAX_INTENT_RETRIES,
7
+ } from "../constants/preflight.js";
8
+
9
+ export type PreflightRuntimeConfig = {
10
+ intentRetries: number;
11
+ warmSidecar: boolean;
12
+ intentCache: boolean;
13
+ };
14
+
15
+ function parseOnOff(value: string | undefined, defaultOn: boolean): boolean {
16
+ if (value === undefined || value.trim() === "") return defaultOn;
17
+ const normalized = value.trim().toLowerCase();
18
+ if (normalized === "0" || normalized === "false" || normalized === "no") return false;
19
+ return true;
20
+ }
21
+
22
+ /** Runtime toggles for preflight latency optimizations. */
23
+ export function readPreflightRuntimeConfig(
24
+ env: NodeJS.ProcessEnv = process.env,
25
+ ): PreflightRuntimeConfig {
26
+ const retries = Number.parseInt(env[ENV_KEYS.INTENT_RETRIES] ?? String(DEFAULT_INTENT_RETRIES), 10);
27
+ return {
28
+ intentRetries: Number.isFinite(retries)
29
+ ? Math.min(Math.max(0, retries), MAX_INTENT_RETRIES)
30
+ : DEFAULT_INTENT_RETRIES,
31
+ warmSidecar: parseOnOff(env[ENV_KEYS.WARM_SIDECAR], DEFAULT_WARM_SIDECAR_ENABLED),
32
+ intentCache: parseOnOff(env[ENV_KEYS.INTENT_CACHE], DEFAULT_INTENT_CACHE_ENABLED),
33
+ };
34
+ }
@@ -25,6 +25,9 @@ export const ENV_KEYS = {
25
25
  MMR_LAMBDA: "PI_MEMORY_MMR_LAMBDA",
26
26
  MIN_RELEVANCE: "PI_MEMORY_MIN_RELEVANCE",
27
27
  CHUNK_MAX_CHARS: "PI_MEMORY_CHUNK_MAX_CHARS",
28
+ INTENT_RETRIES: "PI_MEMORY_INTENT_RETRIES",
29
+ WARM_SIDECAR: "PI_MEMORY_WARM_SIDECAR",
30
+ INTENT_CACHE: "PI_MEMORY_INTENT_CACHE",
28
31
  AGENT_DIR: "PI_MEMORY_AGENT_DIR",
29
32
  ENV_FILE: "PI_MEMORY_ENV_FILE",
30
33
  DEBUG: "PI_MEMORY_DEBUG",
@@ -11,3 +11,12 @@ export const MEMORY_CUE_RE =
11
11
 
12
12
  export const PREFLIGHT_SKIP_MIN_LENGTH = 12;
13
13
  export const PREFLIGHT_EXTRACT_MIN_LENGTH = 24;
14
+
15
+ /** Helper LLM retries after the first attempt (0 = single try). */
16
+ export const DEFAULT_INTENT_RETRIES = 0;
17
+ export const MAX_INTENT_RETRIES = 3;
18
+
19
+ /** Sidecar warm start + intent LRU defaults. */
20
+ export const DEFAULT_WARM_SIDECAR_ENABLED = true;
21
+ export const DEFAULT_INTENT_CACHE_ENABLED = true;
22
+ export const INTENT_CACHE_MAX_ENTRIES = 128;
@@ -39,6 +39,8 @@ export const SIDECAR_FORCE_KILL_DELAY_MS = 5_000;
39
39
  export const SIDECAR_PING_TIMEOUT_MS = 1_000;
40
40
  export const SIDECAR_QUERY_TIMEOUT_MS = 3_000;
41
41
  export const SIDECAR_REINDEX_TIMEOUT_MS = 10_000;
42
+ /** Warm start dummy query (session_start; not counted against Preflight budget). */
43
+ export const SIDECAR_WARMUP_QUERY_TIMEOUT_MS = 500;
42
44
 
43
45
  /** proper-lockfile on MEMORY.md. */
44
46
  export const MEMORY_LOCK_RETRIES = 5;
@@ -4,10 +4,12 @@ import findLastIndex from "lodash/findLastIndex.js";
4
4
 
5
5
  import { createLlmClient, type LlmClient } from "./adapters/llm/index.js";
6
6
  import { loadEnv, readPiMemoryEnv, resolveMemoryAgentDir } from "./config/index.js";
7
+ import { readPreflightRuntimeConfig } from "./config/preflight.js";
7
8
  import { createConsolidateScheduler, startConsolidateInterval, type ConsolidateScheduler } from "./consolidate/scheduler.js";
8
9
  import { registerCommands } from "./commands/index.js";
9
10
  import { registerCompactHandlers } from "./compact/register.js";
10
11
  import { runEpisodicPreflight } from "./preflight/episodic.js";
12
+ import { queryIntentCache } from "./preflight/intentCache.js";
11
13
  import { mergePrivateMemoryBlocks, renderMemoryCapPrivateMemory } from "./preflight/render.js";
12
14
  import { isSubagentSession } from "./preflight/session.js";
13
15
  import { injectPrivateMemoryContext } from "./preflight/strip.js";
@@ -15,6 +17,7 @@ import { enqueueShutdownMetadata, readParentSession } from "./shutdown/enqueue.j
15
17
  import { resolveSidecarPaths } from "./sidecar/paths.js";
16
18
  import { formatTimestamp } from "./utils/time.js";
17
19
  import { createReindexScheduler, type ReindexScheduler } from "./sidecar/reindexBridge.js";
20
+ import { warmSidecar } from "./sidecar/warmup.js";
18
21
  import { ensureSidecarRunning, stopSidecar } from "./sidecar/sidecarManager.js";
19
22
  import { MemoryStore } from "./store/memoryStore.js";
20
23
 
@@ -35,6 +38,7 @@ let sessionMemoryCap: string | null = null;
35
38
  let turnPreflight: TurnPreflight | null = null;
36
39
  let isFirstTurn = false;
37
40
  let isSubagent = false;
41
+ let sessionId: string | null = null;
38
42
 
39
43
  function getUserMessageText(message: AgentMessage): string | null {
40
44
  if (message.role !== "user") return null;
@@ -70,6 +74,14 @@ async function bootstrapSidecar(): Promise<void> {
70
74
  dbPath: sidecarPaths.dbPath,
71
75
  });
72
76
 
77
+ if (readPreflightRuntimeConfig().warmSidecar) {
78
+ try {
79
+ await warmSidecar(sidecarPaths.socketPath);
80
+ } catch {
81
+ // warm is best-effort
82
+ }
83
+ }
84
+
73
85
  reindexScheduler ??= createReindexScheduler({
74
86
  sidecar: sidecarPaths,
75
87
  agentDir: memoryStore.agentDir,
@@ -117,6 +129,7 @@ export default function piMemoryExtension(pi: ExtensionAPI): void {
117
129
  isSubagent = isSubagentSession(ctx);
118
130
  isFirstTurn = true;
119
131
  turnPreflight = null;
132
+ sessionId = ctx.sessionManager.getSessionFile() ?? null;
120
133
 
121
134
  await refreshLlm(ctx, pi);
122
135
 
@@ -141,6 +154,10 @@ export default function piMemoryExtension(pi: ExtensionAPI): void {
141
154
  }).catch(() => {});
142
155
  }
143
156
 
157
+ if (sessionId) {
158
+ queryIntentCache.clearSession(sessionId);
159
+ }
160
+
144
161
  stopConsolidateInterval?.();
145
162
  stopConsolidateInterval = null;
146
163
  consolidateScheduler = null;
@@ -152,6 +169,7 @@ export default function piMemoryExtension(pi: ExtensionAPI): void {
152
169
  turnPreflight = null;
153
170
  isFirstTurn = false;
154
171
  isSubagent = false;
172
+ sessionId = null;
155
173
 
156
174
  try {
157
175
  await stopSidecar();
@@ -194,7 +212,8 @@ export default function piMemoryExtension(pi: ExtensionAPI): void {
194
212
  agentDir: memoryStore.agentDir,
195
213
  store: memoryStore,
196
214
  llm: llmClient,
197
- force: forceHelper,
215
+ forceEpisodic: forceHelper,
216
+ sessionId: sessionId ?? undefined,
198
217
  budgetMs: env.preflightBudgetMs,
199
218
  signal: ctx.signal,
200
219
  onProgress: workingUi?.update,
@@ -237,7 +256,8 @@ export default function piMemoryExtension(pi: ExtensionAPI): void {
237
256
  agentDir: memoryStore.agentDir,
238
257
  store: memoryStore,
239
258
  llm: llmClient,
240
- force: userTurnCount === 1,
259
+ forceEpisodic: userTurnCount === 1,
260
+ sessionId: sessionId ?? undefined,
241
261
  budgetMs: env.preflightBudgetMs,
242
262
  signal: ctx.signal,
243
263
  });