pi-qdrant-memory
Semantic, cross-session retrieval over durable conversation knowledge for pi.dev, backed by Qdrant.
Package details
Install pi-qdrant-memory from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-qdrant-memory- Package
pi-qdrant-memory- Version
0.3.3- Published
- Sep 14, 2026
- Downloads
- 722/mo · 722/wk
- Author
- jh0nnyrosa
- License
- MIT
- Types
- extension
- Size
- 191.9 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-qdrant-memory
Semantic, cross-session/cross-project retrieval over durable conversation knowledge for pi.dev, backed by Qdrant.
Complements the ecosystem: codegraph = current code structure (exact/graph); pi-blackhole = durable decision
capture (lexical recall); this extension = semantic retrieval over that durable knowledge.
memory_save(agent tool) — persist a durable decision/constraint/preference.memory_search(agent tool) — semantic search of prior durable knowledge.code_memory(agent tool, opt-in) — semantic search of indexed code-structure summaries (enabled viacodeKnowledge: "on"; see Code memory)./qdrant-*command set —qdrant-status,qdrant-settings,qdrant-remember,qdrant-search,qdrant-clear,qdrant-help, plusqdrant-index-codewhen code memory is on. Each is a unique single-token pi command (no subcommand parsing):/qdrant-status,/qdrant-settings <key> <value>, …- Mode-aware: with pi-blackhole installed it ingests blackhole's durable artifacts; without it, it captures
pi's own compaction summary as a
session_summary.
All writes are idempotent (deterministic content-hash point ids). Unreachable Qdrant/embeddings degrade gracefully — tools report the problem and never crash the session.
Install
pi install npm:pi-qdrant-memory # or: pi install git:github.com/<you>/pi-qdrant-memory
Quickstart (no package publish): drop src/index.ts into .pi/extensions/.
Runtime is in-process; no background resources are started by the factory (all lifecycle work happens on
session_start / compaction events / session_shutdown).
Prerequisites
- Node >= 22.19 (native TypeScript type-stripping).
- A running Qdrant server — default
http://localhost:6333(REST). - An OpenAI-compatible
/embeddingsendpoint — default a local llama.cpp OpenAI-format server runningnomic-embed-text(768-dim). Ollama or any hosted OpenAI-compatible API also work (setembeddingApiKey).
Config
The global config file lives at
~/.pi/agent/pi-qdrant-memory/pi-qdrant-memory-config.json (honors
PI_CODING_AGENT_DIR). It holds all eleven keys and is the layer that applies where
no project override exists. A copyable template of the shipped defaults ships at
pi-qdrant-memory-config.example.json (repo root, listed in package.json files) —
copy it onto the path above to materialize the file. The template equals the built-in
DEFAULTS in src/config.ts, which remains the real layer-4 default: a missing global
file is not an error, the built-in defaults apply (zero-config run).
Storage layout — ~/.pi/agent/pi-qdrant-memory/ holds the global config file and a
projects/ directory with one <projectId>.json override per project (~100 B each).
Precedence, per field, highest first:
env → project override → global file → DEFAULTS (see Project overrides).
The nine keys below always live in the global file and are edited with /qdrant-settings;
the two allowlisted keys' defaults are edited by hand using the template.
| Key | Default | Meaning |
|---|---|---|
qdrantUrl |
http://localhost:6333 |
Qdrant REST base URL |
qdrantApiKey |
null |
Optional Qdrant API key |
embeddingBaseURL |
http://localhost:8080/v1 |
OpenAI-compatible /embeddings endpoint |
embeddingModel |
nomic-embed-text |
Embedding model id |
embeddingApiKey |
null |
Optional key for hosted embedding APIs |
expectedDimension |
768 |
Embedding dimension (positive integer); dimension drift recreates the collection |
scoreThreshold |
0.18 |
Search score threshold, 0–1 (per-model; nomic ≈ 0.15–0.2) |
maxResults |
10 |
Default memory_search limit |
mode |
auto |
auto detect | blackhole force Mode 1 | own force Mode 2 |
codeKnowledge |
off |
on enables structural code summaries + the code_memory tool (next session) |
codeScoreThreshold |
0.55 |
Score threshold for code_memory searches, 0–1 (per-model; see Code memory) |
Env overrides (highest precedence, above the project and file layers):
PI_QDRANT_URL, PI_QDRANT_API_KEY, PI_QDRANT_EMBEDDING_BASE_URL, PI_QDRANT_EMBEDDING_MODEL,
PI_QDRANT_EMBEDDING_API_KEY, PI_QDRANT_EXPECTED_DIMENSION, PI_QDRANT_SCORE_THRESHOLD,
PI_QDRANT_MAX_RESULTS, PI_QDRANT_MODE, PI_QDRANT_CODE_KNOWLEDGE, PI_QDRANT_CODE_SCORE_THRESHOLD.
The global file stores API keys and is written with owner-only permissions (0600);
a previously loosened file is tightened on the next save.
Project overrides
Two config keys are repo-shaped and can be overridden per project:
codeKnowledge (should this repo be indexed at all) and codeScoreThreshold (how strict
code retrieval is in this repo). An override lives at
<agentDir>/pi-qdrant-memory/projects/<projectId>.json — a partial config holding only
those keys, keyed by the same projectId as the collection.
- Env still wins:
PI_QDRANT_CODE_KNOWLEDGE/PI_QDRANT_CODE_SCORE_THRESHOLDmask a project override. /qdrant-settings codeKnowledge onand/qdrant-settings codeScoreThreshold 0.6write this project's store;/qdrant-settings <key> defaultclears the override.- A moved or renamed repo orphans its override exactly as it orphans its collection (both are keyed by the git-root realpath hash).
scoreThresholdandmaxResultsare not project-overridable on purpose:scoreThresholdis purely model-calibrated and has no repo-shaped half, andmaxResultsis a user preference about result volume, not a repo property.
Commands
/qdrant-status — connection health + active mode + collection point count.
/qdrant-settings <key> <value> — persist a config field. codeKnowledge and codeScoreThreshold
apply to this project (an override); every other key (mode, embeddingBaseURL, embeddingModel,
expectedDimension, scoreThreshold, maxResults, …) writes the global config file.
<key> default clears an allowlisted override; for the other keys default is an ordinary value.
Bare /qdrant-settings prints usage.
/qdrant-remember <text> — manual durable save.
/qdrant-search <query> — manual semantic search.
/qdrant-index-code — re-index code summaries now (only when codeKnowledge: on).
/qdrant-clear — reset the current project's collection.
/qdrant-help — this list.
Statusline
While a session is active the extension shows a footer status entry:
🧠 Memory (N): <mode> (<collection>) — N is the number of memories stored
in the project collection, refreshed at session start and after every
successful save/clear. When Qdrant is unreachable the count is omitted.
Agent tools
memory_save(text, type?)— persist a durable decision/constraint/preference. Type defaults todecision.memory_search(query, type?, limit?)— semantic search of prior durable knowledge (limit capped bymaxResults).code_memory(query, limit?)(opt-in) — semantic search of indexed code-structure summaries.
The tools carry always-on prompt guidance (via promptSnippet/promptGuidelines): the model is nudged to call
memory_save when a decision/constraint/preference settles (with concise, self-contained statements, without
re-recording what auto-capture covers) and to call memory_search when resuming prior work or before re-deciding.
When code memory is on, code_memory likewise carries guidance to reach for it on "how/where does X work"
questions and to open the returned file:line pointers.
No companion skill is needed for the core loop — the guidance ships with the tools.
Code memory (opt-in)
With codeKnowledge: "on", the extension scans the repo at session_start (fire-and-forget) and
indexes structural summaries of top-level definitions — exported functions/classes/types,
Python defs, per-file anchors — as code points in the same collection. Zero dependencies: the
extractor is built in; no external indexer is used.
Freshness: payloads carry
file_path+file_sha; unchanged files are skipped, changed files are deleted-and-replaced, vanished files are cleaned up./qdrant-index-codeforces a resync.Retrieval: the
code_memorytool (registered only while enabled) searches code summaries atcodeScoreThreshold;memory_searchnever returns code hits.Thresholds are per-model, and this one is load-bearing. The shipped
0.55was calibrated againstnomic-embed-textso that relevant code queries (≈0.6) clear it while unrelated ones (≈0.5) do not — a narrow band, because embedding models compress cosine similarity. On a model that scores lower overall, code search can silently return nothing. To find where your model actually sits, temporarily lowercodeScoreThreshold(e.g. to0.3) and read thescore=values incode_memoryoutput, then set it just above the band where unrelated matches stop appearing. Note this only applies to code search:memory_searchusesscoreThresholdand never returns code hits.Mid-session flips take effect at the next session start (the settings output reminds you); only indexing can be run immediately via
/qdrant-index-code.
Modes
- Mode 1 (pi-blackhole present, coexistence): reads pi-blackhole's pending durable artifacts
(
<agent-dir>/pi-blackhole/*-pending.json) and ingests them atsession_start(catch-up) andsession_shutdown. It never claims thesession_before_compacthook. - Mode 2 (pi-blackhole absent): claims
session_before_compactand captures pi's own compaction summary (from thesession_compactevent) as asession_summarypoint — fire-and-forget so capture can never stall compaction./qdrant-rememberis the manual safety net.
An early-session auto snapshot (spec §3.3 safety net) is not yet wired: it needs mid-session content
distillation access this extension does not currently have, so Mode 2's safety nets are the compaction
capture and /qdrant-remember.
Data model (summary)
One Qdrant collection per project, named pi-mem-<16 hex of sha256(git root)>; single unnamed vector,
Cosine, on_disk, HNSW. Points carry { type, text, project_id, session_id?, source_entry_id?, ts, source_kind } plus code-provenance fields on code points (file_path, file_sha, symbol,
start_line, end_line); keyword payload indexes on source_kind and file_path.
Deterministic point ids (sha256(normalized text | source_kind | context)) make every write idempotent.
Development
Zero runtime npm dependencies (Node global fetch + crypto only). TypeScript is erasable-syntax only and
runs directly via Node's type stripping — no build step.
npm test # node --test over test/**/*.test.ts
npm run typecheck # tsc --noEmit (needs devDependencies installed)
Opt-in end-to-end smoke test (requires real Qdrant on :6333 and an OpenAI-compatible embeddings server):
QDRANT_MEMORY_SMOKE=1 npm run test:smoke. Server endpoints are overridable via QDRANT_MEMORY_URL,
QDRANT_MEMORY_EMBED_URL, QDRANT_MEMORY_EMBED_MODEL, and QDRANT_MEMORY_EMBED_DIM.