pi-memory-sessions
Pi extension: keyword recall over your past pi sessions, stored in a self-owned SQLite index with CJK-aware search
Package details
Install pi-memory-sessions from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-memory-sessions- Package
pi-memory-sessions- Version
1.0.0- Published
- Oct 22, 2026
- Downloads
- 912/mo · 912/wk
- Author
- blueuu
- License
- MIT
- Types
- extension
- Size
- 80.8 KB
- Dependencies
- 0 dependencies · 5 peers
Pi manifest JSON
{
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-memory-sessions
中文说明 · English
Keyword recall over your past pi sessions — works for Chinese, English, and mixed content.
Every conversation pi runs is captured into a SQLite index that this extension owns. Delete pi's sessions/*.jsonl and recall still works. Ask "what did we decide about X last month?" and the agent can actually answer, because it can search.
Zero runtime dependencies: node:sqlite + FTS5, both built into Node.
you ▸ /recall 缓存索引
命中 8 条(2.4ms)
[2026-10-15 14:02 · 3 周前 · ~/code/api · "cache sharding"]
#122 assistant msg_id=5541 score=1.00
…按会话分片,索引写在 WAL 之后…【缓存】索引…
What this is / is not
| Is | Automatic, lossless capture of session transcripts, searchable by keyword |
| Is not | A note-taking memory (MEMORY.md). Nothing is curated, summarized, or injected into the system prompt |
| Is not | Semantic/vector search. Keywords only (why) |
If you want the agent to remember a preference, that is a curated-notes problem. This solves the other half: finding what actually happened.
Requirements
- pi ≥ 1.0
- Node ≥ 22.19 — for
node:sqlite - SQLite ≥ 3.43 — for FTS5
contentless_delete(bundled with the Node versions above)
The extension refuses to start with an actionable message if either is missing.
Install
pi install npm:pi-memory-sessions
Or straight from the repository:
pi install github:877007021/pi-memory-sessions
Or load a checkout directly:
pi --extension ./index.ts
Or copy the directory into ~/.pi/agent/extensions/ (auto-discovered, no settings change).
Usage
Model-facing tools (registered with promptSnippet + promptGuidelines, so they appear in the system prompt and get used without being told):
| Tool | Purpose |
|---|---|
session_search |
Search past sessions by keyword |
session_read |
Read a contiguous window around a hit |
User-facing command:
/recall <keyword> search
/recall stats store size, message and session counts
/recall import import existing sessions/*.jsonl (batched, does not block the UI)
/recall importis how you get history from before the extension was installed. It is safe to run repeatedly — writes are idempotent.
How it works
message_end ─┐
session_start │
session_compact ├─→ render markdown ─→ SQLite (WAL) ─→ dual-channel FTS5
session_shutdown ─┘ ↑
session_search / session_read / /recall
Capture happens at message_end, which fires for every finalized message before pi persists it. Successful tool results are dropped: they are ~90% of transcript volume and the lowest-signal content in it.
session_search is a three-stage pipeline:
- Tokenize — CJK unigrams for recall, CJK bigrams for precision, Latin tokens in both channels.
- Recall — take the three rarest unigrams as anchors, then
FTS5 chars:("a" "b" "c") ORDER BY rowid DESC LIMIT K, which lets FTS5 stop early. - Score — bigram coverage, with recency as the tiebreak.
Each stage exists because the naive alternative was measured and failed:
| Naive approach | Measured failure |
|---|---|
| AND over all bigrams | 0 hits for any natural-language Chinese query of 5+ chars |
| OR over all bigrams | candidates swamped by high-frequency characters |
| IDF weighting | cross-word bigrams (的缓, 式是) are rare but meaningless, so they win the ranking |
ORDER BY bm25 |
must score every match: 36 ms and grows linearly |
LIKE '%x%' |
full scan, 87 ms and grows linearly |
Query cost is O(candidate window), not O(store size).
Performance
Measured on 29.5k real messages / 95 MB index:
| Operation | Time |
|---|---|
| Write one message | 0.53 ms |
| Search (K=200 candidates) | 5–15 ms |
| Read 21 messages of context | 0.06 ms |
| Second pi process writing concurrently | 1.2 ms |
Real install, 7,370 messages / 41 sessions: search p50 0.1–0.4 ms.
Security
This extension stores transcripts verbatim. There is no redaction.
That is a deliberate choice: masking breaks searchability (you cannot search for OPENAI_API_KEY or a variable name if it was replaced), and partial masking is worse than none because it implies the store is clean when it is not.
Consequences you must accept:
~/.pi/memory/sessions.dbcontains everything you or your tools ever printed, including secrets that passed through a conversation.- Treat it as a sensitive file: do not commit it, do not sync it to a public location, encrypt backups.
- pi's own
sessions/*.jsonlalready stores the same content; this index is no broader than what pi already keeps, but it is in one file and it outlivessessions/deletion.
PI_MEMORY_DB relocates the store if you want it on an encrypted volume.
Data
~/.pi/memory/sessions.db
| Stored | Dropped |
|---|---|
| user messages | system prompt |
| assistant text | thinking blocks |
| tool calls (name + arguments) | successful tool results |
| failed tool results (first 500 chars) | image payloads |
| compaction summaries |
Why compaction summaries matter: pi compresses old context into a structured summary (Goal / Decisions / Next Steps) when the window fills. In a real store, 28 summaries averaged 14,746 chars — a single summary carries roughly 40 messages' worth of signal, and it is already the distilled version of what pi judged important.
Paths and environment variables
| Variable | Default | Effect |
|---|---|---|
PI_MEMORY_DB |
~/.pi/memory/sessions.db |
index location |
PI_MEMORY_SESSIONS_DIR |
inferred from pi | where /recall import reads *.jsonl from |
PI_CODING_AGENT_SESSION_DIR |
— | pi's own session dir; followed automatically |
PI_CODING_AGENT_DIR |
~/.pi/agent |
pi's config dir; sessions are read from <dir>/sessions |
Containers and non-persistent homes. Both defaults live under $HOME. If $HOME is a container layer (rebuilt on every deploy), the index is thrown away with it, and a sessions dir that pi redirected elsewhere is never found — the symptom is /recall import reporting 0 文件 with no error. Give it explicit paths:
PI_MEMORY_DB=/data/agent/pi-memory/sessions.db
PI_MEMORY_SESSIONS_DIR=/data/agent/sessions
PI_CODING_AGENT_DIR / PI_CODING_AGENT_SESSION_DIR are read too, so a pi that is already configured for a custom layout needs no extra settings here.
Known limitations
- No traditional↔simplified normalization — a traditional query will not match simplified content.
- No dictionary segmentation — exact-phrase matching is weaker than a real tokenizer; rare-character anchors plus bigram scoring compensate.
- Sessions before install are not captured — use
/recall import, and check that it reports a non-zero file count (see Paths). - A dropped write is not retried — the source of truth is still pi's session file, so re-run
/recall import. A write failure never breaks a turn. - No vector search — see below.
- No
forgetyet — deleting rows requiressqlite3by hand.
Why keywords and not vectors
Two reasons, both practical: it keeps the dependency count at zero and the query at single-digit milliseconds. Semantic search would help when you remember the concept but not the words — that is a real gap, and it is the next thing to add (a vec table keyed by msg.id, no schema change to existing tables).
Development
node --test test/unit.test.ts # 47 tests, no pi, no model, no installs
node --no-warnings bench/smoke.ts # loads index.ts against a mock ExtensionAPI
node --no-warnings bench/replay.ts # replays real ~/.pi/agent/sessions and reports latency
Design notes, measurements, and the assumptions that testing disproved are in DESIGN.md (written in Chinese).
Releasing
Publishing goes through npm Trusted Publishing (OIDC), so there is no npm token in this repository and no OTP step.
npm version patch # or minor / major
git push --follow-tags
The v* tag triggers .github/workflows/publish.yml, which checks that the tag
matches package.json, runs the test suite and the capability check, then
publishes with a provenance signature.
The npm package must already exist before Trusted Publishing can be configured — that first version has to be published once with a token or OTP.
License
MIT