memkernel 0.2.0__tar.gz → 0.4.0__tar.gz

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. {memkernel-0.2.0 → memkernel-0.4.0}/PKG-INFO +116 -19
  2. {memkernel-0.2.0 → memkernel-0.4.0}/README.md +113 -18
  3. {memkernel-0.2.0 → memkernel-0.4.0}/pyproject.toml +3 -1
  4. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/__init__.py +1 -1
  5. memkernel-0.4.0/src/memkernel/adapters/__init__.py +3 -0
  6. memkernel-0.4.0/src/memkernel/adapters/rlmstudio.py +123 -0
  7. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/api/routes/admin.py +16 -1
  8. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/config.py +14 -0
  9. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/core/models.py +110 -0
  10. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/core/ports.py +45 -5
  11. memkernel-0.4.0/src/memkernel/core/services.py +1227 -0
  12. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/factory.py +37 -1
  13. memkernel-0.4.0/src/memkernel/lifecycle.py +221 -0
  14. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/mcp/server.py +41 -8
  15. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/mcp/tools/memory.py +31 -7
  16. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/retrieval/chunking.py +20 -0
  17. memkernel-0.4.0/src/memkernel/retrieval/reasoner.py +83 -0
  18. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/retrieval/search.py +31 -2
  19. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/storage/chroma.py +53 -19
  20. memkernel-0.4.0/src/memkernel/storage/documents.py +185 -0
  21. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/wiki/sync.py +4 -4
  22. memkernel-0.2.0/src/memkernel/core/services.py +0 -485
  23. {memkernel-0.2.0 → memkernel-0.4.0}/LICENSE +0 -0
  24. {memkernel-0.2.0 → memkernel-0.4.0}/NOTICE +0 -0
  25. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/api/__init__.py +0 -0
  26. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/api/app.py +0 -0
  27. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/api/routes/__init__.py +0 -0
  28. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/core/__init__.py +0 -0
  29. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/mcp/__init__.py +0 -0
  30. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/mcp/tools/__init__.py +0 -0
  31. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/mcp/tools/wiki.py +0 -0
  32. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/memory/__init__.py +0 -0
  33. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/memory/dedup.py +0 -0
  34. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/memory/lifecycle.py +0 -0
  35. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/memory/types.py +0 -0
  36. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/py.typed +0 -0
  37. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/retrieval/__init__.py +0 -0
  38. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/retrieval/embeddings.py +0 -0
  39. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/retrieval/sparse.py +0 -0
  40. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/storage/__init__.py +0 -0
  41. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/wiki/__init__.py +0 -0
  42. {memkernel-0.2.0 → memkernel-0.4.0}/src/memkernel/wiki/pages.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: memkernel
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Code-aware memory server for AI coding copilots
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -22,16 +22,23 @@ Requires-Dist: rank-bm25 (>=0.2)
22
22
  Requires-Dist: sentence-transformers (>=4.0,<6)
23
23
  Requires-Dist: tree-sitter (>=0.22,<1)
24
24
  Requires-Dist: tree-sitter-python (>=0.23)
25
+ Project-URL: Documentation, https://memkernel.dev
26
+ Project-URL: Homepage, https://memkernel.dev
25
27
  Project-URL: Repository, https://github.com/gosha70/memkernel
26
28
  Description-Content-Type: text/markdown
27
29
 
28
- # MemKernel
30
+ <h1>
31
+ <img src="https://raw.githubusercontent.com/gosha70/memkernel/main/docs/assets/memkernel-logo.png" alt="MemKernel logo: a glowing memory core on three stacked tiers" width="110" align="absmiddle">
32
+ MemKernel
33
+ </h1>
29
34
 
30
35
  [![CI](https://github.com/gosha70/memkernel/actions/workflows/ci.yml/badge.svg)](https://github.com/gosha70/memkernel/actions/workflows/ci.yml)
31
36
  [![PyPI](https://img.shields.io/pypi/v/memkernel)](https://pypi.org/project/memkernel/)
32
37
  [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](pyproject.toml)
33
38
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)
34
39
 
40
+ **Site:** [memkernel.dev](https://memkernel.dev) · **Package:** [pypi.org/project/memkernel](https://pypi.org/project/memkernel/)
41
+
35
42
  Code-aware memory server for AI coding copilots.
36
43
 
37
44
  MemKernel gives Claude Code (and any MCP-compatible copilot) persistent memory across sessions. When Claude compacts context or a session ends, project decisions, conventions, and code understanding are normally lost. MemKernel stores them in a local vector database and retrieves them on demand.
@@ -44,23 +51,33 @@ MemKernel gives Claude Code (and any MCP-compatible copilot) persistent memory a
44
51
 
45
52
  ## Requirements
46
53
 
47
- - Python 3.11 – 3.13
54
+ - Python 3.11 – 3.13 (the package declares `Requires-Python >=3.11,<3.14`; pip reports "from versions: none" when the installing interpreter is outside that range)
48
55
  - [Poetry](https://python-poetry.org) (for development installs)
49
56
 
50
57
  ---
51
58
 
52
59
  ## Install
53
60
 
61
+ MemKernel is a Python package on PyPI. Its `memkernel` command is the MCP server. There is no npm package: an `npx` install someone suggests is a hallucination.
62
+
63
+ **Recommended: an isolated install with [pipx](https://pipx.pypa.io)**, which puts the `memkernel` command on your PATH without touching any other Python environment:
64
+
54
65
  ```bash
55
- pip install memkernel
66
+ pipx install memkernel
67
+ # if pipx's default interpreter is outside 3.11–3.13:
68
+ pipx install --python python3.11 memkernel
56
69
  ```
57
70
 
58
- Or with Poetry:
71
+ **As a library inside a project**, install it into that project's virtual environment:
59
72
 
60
73
  ```bash
61
- poetry add memkernel
74
+ pip install memkernel # or: poetry add memkernel
62
75
  ```
63
76
 
77
+ Avoid a plain `pip install memkernel` into a shared or system interpreter (Homebrew's `python3`, for example). MemKernel pins `chromadb` 1.x and recent `cffi`, so pip will replace older copies and can break unrelated tools that need them.
78
+
79
+ Verify the install with `pipx list` or `which memkernel`. Running `memkernel` with no arguments starts the MCP server on stdio and waits for a client; `memkernel --version` and `memkernel --help` print and exit.
80
+
64
81
  ---
65
82
 
66
83
  ## Add to Claude Code
@@ -73,17 +90,29 @@ Add MemKernel to `.claude/settings.json` in your project:
73
90
  {
74
91
  "mcpServers": {
75
92
  "memkernel": {
76
- "command": "memkernel",
93
+ "command": "/Users/you/.local/bin/memkernel",
77
94
  "args": [],
78
95
  "env": {
79
- "MEMKERNEL_PROJECT_ID": "my-project"
96
+ "MEMKERNEL_PROJECT_ID": "my-project",
97
+ "MEMKERNEL_CHROMA_PERSIST_PATH": "/Users/you/.memkernel/my-project"
80
98
  }
81
99
  }
82
100
  }
83
101
  }
84
102
  ```
85
103
 
86
- Replace `my-project` with a short identifier for the project — memories are isolated per project.
104
+ Replace `my-project` with a short identifier for the project — memories are isolated per project. Use the absolute path of the command (`which memkernel` prints it) because Claude Code does not necessarily see the PATH changes your shell profile makes, and an absolute `MEMKERNEL_CHROMA_PERSIST_PATH` keeps the store outside the repository. On first server initialization, MemKernel downloads the embedding model (about 90 MB) if it is not already cached: `build_service()` loads the sentence-transformers model when the MCP server starts, before any tool call, so the first connection can take a minute. Later starts and every tool call are fast.
105
+
106
+ <details>
107
+ <summary>Troubleshooting the install</summary>
108
+
109
+ - **`pipx install memkernel` says "No matching distribution found"** — pipx used an interpreter outside 3.11–3.13. Pass one explicitly: `pipx install --python /opt/homebrew/opt/python@3.11/bin/python3.11 memkernel`.
110
+ - **`bash: /some/old/path/memkernel: No such file or directory`** right after reinstalling — the shell cached the old location. Run `hash -r` (bash) or `rehash` (zsh), then `which memkernel`.
111
+ - **`memkernel --help` seems to hang** on versions before 0.4.0 — the command had no flags and started the server; press Ctrl-C. From 0.4.0 on, `--help` and `--version` print and exit.
112
+ - **The first MCP connection is slow or times out** — the server is downloading the embedding model at startup (see above). Wait for it once, or pre-warm it with `python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"` in the same environment.
113
+ - **A global pip install broke another tool** (for example `langchain-chroma` wanting `chromadb < 0.6`) — uninstall memkernel from that interpreter, reinstall the versions the other tool needs, and install memkernel with pipx instead.
114
+
115
+ </details>
87
116
 
88
117
  ### 2. Add memory instructions to CLAUDE.md
89
118
 
@@ -99,7 +128,21 @@ Copy the hook scripts from [`templates/hooks/`](templates/hooks/) to `.claude/ho
99
128
  | `pre-compact.py` | `PreCompact` | Retains a checkpoint episode, runs a consolidation pass |
100
129
  | `post-compact.py` | `PostCompact` | Re-injects the checkpoint, long-term context and promotion candidates |
101
130
 
102
- See the Prerequisites section inside each script for the Python environment requirement.
131
+ See the Prerequisites section inside each script for the Python environment requirement. Each script is a thin wrapper over [`MemoryLifecycle`](#embedding-memkernel-in-your-own-harness), so an in-process harness gets the same behaviour without the scripts.
132
+
133
+ ---
134
+
135
+ ## Add to Kiro
136
+
137
+ Kiro (and the Kiro CLI, formerly Amazon Q Developer CLI) connects to MemKernel as a stdio MCP server. [`templates/kiro/`](templates/kiro/) holds the three files to copy into a workspace:
138
+
139
+ | Template | Destination | Role |
140
+ |----------|-------------|------|
141
+ | `settings/mcp.json` | `.kiro/settings/mcp.json` | Server entry with `env` and `autoApprove` |
142
+ | `steering/memory.md` | `.kiro/steering/memory.md` | Memory instructions, always included |
143
+ | `hooks/memkernel.json` | `.kiro/hooks/memkernel.json` | `SessionStart` recall, `Stop` retain, manual checkpoint |
144
+
145
+ [`templates/kiro/README.md`](templates/kiro/README.md) has the setup steps, the Kiro CLI custom-agent variant and the caveats (absolute persist path, hook format by Kiro version).
103
146
 
104
147
  ---
105
148
 
@@ -108,12 +151,12 @@ See the Prerequisites section inside each script for the Python environment requ
108
151
  | Tool | What it does |
109
152
  |------|-------------|
110
153
  | `retain(content, type, session_id?, checkpoint?, source?, importance?, tags?, supersedes?)` | Store a memory. Returns `ref_id`. Idempotent — identical content + type returns the same `ref_id`. `supersedes` hides the memory it replaces. |
111
- | `recall(query, type?, tier?, session_id?, checkpoint_only?, top_k?)` | Search by natural language or identifier. Returns lightweight `ArtifactRef` list ranked by relevance + recency + importance. Records an access (reinforces retention). |
154
+ | `recall(query, type?, tier?, session_id?, checkpoint_only?, top_k?, include_needs_review?)` | Search by natural language or identifier. Returns lightweight `ArtifactRef` list ranked by relevance + recency + importance. Records an access (reinforces retention). Code memories flagged `needs_review` are hidden unless `include_needs_review=true`. |
112
155
  | `get(ref_id)` | Fetch full content of a specific memory. Records an access. |
113
156
  | `forget(ref_id)` | Delete a memory from all indexes. |
114
157
  | `promote(ref_id, type, content?, importance?, tags?)` | Turn a short-term/working memory into a long-term one (you supply the distilled text). Keeps provenance via `promoted_from`. |
115
- | `consolidate(session_id?, limit?)` | Purge expired short-term memories and list the ones most worth promoting (ranked by importance × how often they were recalled). |
116
- | `forget_expired()` | Delete every short-term/working memory whose retention window lapsed. |
158
+ | `consolidate(session_id?, limit?, synthesize?, revalidate?)` | Purge expired short-term memories and list the ones most worth promoting (ranked by importance × how often they were recalled). `synthesize=true` also asks an external reasoner to draft long-term statements (`suggested`); `revalidate=true` first checks every code memory against its source file (`revalidated`). The report always carries a `health` summary. |
159
+ | `ask(ref_id, question, mode?)` | Answer a question about one memory without loading it — for a `document` the full body is consulted. `mode`: `direct` \| `rag` \| `rlm` \| `auto`, passed to the reasoner unchanged; the built-in reasoner answers in `rag` mode with the best-matching chunks. Returns `answer`, `mode`, `citations`, `usage`. |
117
160
  | `wiki_export(wiki_dir?)` | Compile long-term memories without a wiki page into draft markdown pages (`index.md`, `log.md` updated). |
118
161
  | `wiki_import(wiki_dir?)` | Index curated wiki pages as long-term memories (idempotent; changed pages supersede). |
119
162
 
@@ -125,7 +168,8 @@ See the Prerequisites section inside each script for the Python environment requ
125
168
  | `convention` | long-term | Naming rules, code style, project patterns | 0.9 | Never |
126
169
  | `fact` | long-term | Durable project facts: build commands, environments, contracts | 0.7 | Never |
127
170
  | `procedure` | long-term | How-to that worked: workflows, recipes, verified skills | 0.8 | Never |
128
- | `code` | long-term | Function/class snapshots, API contracts | 0.6 | Never |
171
+ | `code` | long-term | Function/class snapshots, API contracts; identified by `(source, content)`, a changed file supersedes the old snapshot, a drifted source flags `needs_review` | 0.6 | Never (superseded on re-retain) |
172
+ | `document` | long-term | Large artefacts (specs, transcripts, files): the body is stored and chunk-indexed, `get` returns a summary, `ask` answers from the indexed chunks (local reasoner) or the body (RLM Studio) | 0.6 | Never |
129
173
  | `episode` | short-term | Session events, compaction checkpoints | 0.4 | 30 days, extended by every recall |
130
174
  | `observation` | short-term | Findings, tool outputs, things noticed | 0.3 | 7 days, extended by every recall |
131
175
  | `task` | working | The current goal, plan state, open questions | 0.5 | 24 hours |
@@ -138,7 +182,9 @@ Retention: `ttl = base_ttl × (1 + access_boost × access_count)`, measured from
138
182
  2. **While working** — `retain(type="task")` when the plan changes; `retain(type="observation")` for findings; durable knowledge as `decision` / `convention` / `fact` / `procedure`.
139
183
  3. **Before compaction** — `retain(type="episode", checkpoint=true)` (the `pre-compact.py` hook) and `consolidate()`.
140
184
  4. **After compaction / end of phase** — `promote(ref_id, type, content)` for the candidates worth keeping.
141
- 5. **Curate** — `wiki_export` → review the drafts → `wiki_import`.
185
+ 5. **Large artefacts** — `retain(type="document", content=<body>, source=<name>)` then `ask(ref_id, question)`; never `get` a document to read it.
186
+ 7. **Code snapshots** — `retain(type="code", content=<file>, source=<path>)`; retain the file again after editing it (the new snapshot supersedes the old one). The session-start hook revalidates snapshots against their files and lists the ones that need re-retaining.
187
+ 6. **Curate** — `wiki_export` → review the drafts → `wiki_import`.
142
188
 
143
189
  ### Quick examples
144
190
 
@@ -164,6 +210,10 @@ retain(content="Use arq, not Celery", type="decision", supersedes="<old decision
164
210
  get(ref_id="<ref_id from recall>")
165
211
 
166
212
  forget(ref_id="<ref_id>")
213
+
214
+ retain(content=<the whole spec>, type="document", source="specs/auth/spec.md")
215
+
216
+ ask(ref_id="<document ref_id>", question="Which endpoints need rate limiting?", mode="auto")
167
217
  ```
168
218
 
169
219
  ---
@@ -185,6 +235,14 @@ All settings are read from environment variables with the `MEMKERNEL_` prefix, o
185
235
  | `MEMKERNEL_PREVIEW_LENGTH` | `200` | Max characters in the `preview` field of `ArtifactRef` |
186
236
  | `MEMKERNEL_CHUNK_SIZE` | `1500` | Max characters per AST chunk (code memories) |
187
237
  | `MEMKERNEL_CHUNK_OVERLAP` | `200` | Character overlap between token-based fallback chunks |
238
+ | `MEMKERNEL_DOCUMENT_STORE` | `local` | Where `document` bodies live (`local` = the project's own ChromaDB collection) |
239
+ | `MEMKERNEL_DOCUMENT_SUMMARY_CHARS` | `1000` | Characters of the body kept in a `document` memory's summary record |
240
+ | `MEMKERNEL_REASONER` | `local` | `ReasonerPort` behind `ask`: `local` (hybrid search over chunks, no LLM) or `rlmstudio` (falls back to `local` when `rlmstudio` is not importable) |
241
+ | `MEMKERNEL_ASK_TOP_K` | `5` | Chunks the local reasoner returns per `ask` |
242
+ | `MEMKERNEL_PROJECT_ROOT` | `.` | Directory that relative `source` paths of `code` memories resolve under; nothing outside it is ever read. Set it whenever the server or a hook is not started from the project directory |
243
+ | `MEMKERNEL_HEALTH_DUPLICATE_THRESHOLD` | `0.95` | Cosine similarity in [0, 1] from which two long-term memories count as near-duplicates in the health pass |
244
+ | `MEMKERNEL_HEALTH_DUPLICATE_LIMIT` | `500` | Most recent long-term memories the near-duplicate check covers |
245
+ | `MEMKERNEL_HEALTH_REPORT_LIMIT` | `20` | Items listed per category in a health report |
188
246
  | `MEMKERNEL_EPISODE_TTL_DAYS` | `30` | Base TTL for `episode` memories (short-term) |
189
247
  | `MEMKERNEL_OBSERVATION_TTL_DAYS` | `7` | Base TTL for `observation` memories (short-term) |
190
248
  | `MEMKERNEL_TASK_TTL_HOURS` | `24` | Base TTL for `task` memories (working) |
@@ -237,6 +295,35 @@ report = svc.consolidate() # {"purged": n, "candidates": [...]}
237
295
 
238
296
  `build_service()` is the supported entry point; the MCP server, the admin API and the hook templates all use it. Integration notes for `code-copilot-team` and `haa` are in [`docs/integration/harness-integration.md`](docs/integration/harness-integration.md).
239
297
 
298
+ ### The session protocol as a component
299
+
300
+ `MemoryLifecycle` packages what the hook scripts do, so a harness calls four methods instead of re-implementing the protocol. Every method returns markdown ready for injection and does nothing but talk to the service:
301
+
302
+ ```python
303
+ from memkernel.factory import build_service
304
+ from memkernel.lifecycle import MemoryLifecycle
305
+
306
+ memory = MemoryLifecycle(build_service())
307
+ system_context = memory.on_session_start() # checkpoint, long-term context, promotion candidates, wiki pointer
308
+ ...
309
+ memory.on_pre_compact(summary) # checkpoint episode + consolidation, before the context is compacted
310
+ recovered = memory.on_post_compact() # what to re-inject after compaction
311
+ memory.on_session_end(summary) # final checkpoint + consolidation
312
+ ```
313
+
314
+ ### Documents and `ask`
315
+
316
+ `retain(type="document")` keeps large artefacts out of the context window: the body goes to the document store (built-in: the project's own ChromaDB collection), its chunks are indexed for hybrid search, and the memkernel record is a summary. `ask(ref_id, question, mode)` answers through a `ReasonerPort`:
317
+
318
+ - `MEMKERNEL_REASONER=local` (default) — no LLM: the question is matched against the document's chunks with dense + BM25 + RRF and the best chunks come back as the answer, `mode="rag"`.
319
+ - `MEMKERNEL_REASONER=rlmstudio` — delegates to [rlm-studio](https://github.com/gosha70/rlm-studio)'s `interact(content, question, mode=mode)`, so `direct`, `rag`, `rlm` and `auto` select how the document is read (ADR-009). `rlm-studio` is not a memkernel dependency and is not on PyPI yet; install it from git into the same environment:
320
+
321
+ ```bash
322
+ pip install "rlm-studio @ git+https://github.com/gosha70/rlm-studio"
323
+ ```
324
+
325
+ When it is not importable, `build_service()` logs a warning and uses the local reasoner. With an external reasoner, `consolidate(synthesize=True)` also drafts long-term statements from the session's short-term memories.
326
+
240
327
  ## LLM wiki bridge
241
328
 
242
329
  Long-term memory has a human-readable twin: a markdown wiki in the [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern (`index.md`, `log.md`, typed pages with cited sources — the same schema as `code-copilot-team/knowledge/wiki`).
@@ -246,6 +333,12 @@ Long-term memory has a human-readable twin: a markdown wiki in the [LLM Wiki](ht
246
333
 
247
334
  Set `MEMKERNEL_WIKI_DIR` (for example `knowledge/wiki`) or pass `wiki_dir` to the tools. Rationale and the proposed standalone `ai-wiki` repo: [`docs/adr/ADR-008-llm-wiki-as-semantic-layer.md`](docs/adr/ADR-008-llm-wiki-as-semantic-layer.md).
248
335
 
336
+ ### Revalidation and store health
337
+
338
+ A `code` memory is a snapshot of a file. `MemoryService.revalidate()` hashes each live snapshot's whole source file and compares it with the stored content hash: a changed file flags the memory `needs_review` (`changed`), a vanished file flags it `missing`, a matching file clears the flag. Flagged memories leave default `recall` until the file is retained again (which supersedes the stale snapshot) or they are asked for with `include_needs_review=true`. Revalidation runs from `consolidate(revalidate=true)`, at every session start through `MemoryLifecycle.on_session_start()` (a `### Needs review` section lists what to re-retain), and from `POST /revalidate`. Sources resolve under `MEMKERNEL_PROJECT_ROOT` (default: the current directory); set it whenever the server or a hook is not started from the project directory. Nothing outside that root is ever read. 0.4 hashes whole files; per-chunk invalidation is a follow-up.
339
+
340
+ `MemoryService.health()` audits the store without changing it: orphaned chunk and document records, dangling `supersedes` / `superseded_by` / `promoted_from` / `parent_ref` links, supersessions from 0.2/0.3 that lack the durable marker, `code` memories whose source file is gone, memories flagged for review, expired short-term memories not yet purged and, in full mode only, near-duplicate pairs among the most recent long-term memories (cosine ≥ `MEMKERNEL_HEALTH_DUPLICATE_THRESHOLD`). The quick report (counts only, no embeddings, no content loads) is part of every `consolidate()` report and of the `memory://status` resource; the full report is `GET /health/store`. The pass never fixes anything: a fix mode is a later feature.
341
+
249
342
  ## Admin API
250
343
 
251
344
  A FastAPI admin server provides health and stats endpoints (independent of MCP):
@@ -259,6 +352,8 @@ uvicorn memkernel.api.app:app --host 127.0.0.1 --port 8766
259
352
  | `GET /health` | Server health and memory count |
260
353
  | `GET /stats` | Total memory count, per-type and per-tier breakdown, growth rate, last recall latency |
261
354
  | `GET /collections` | List ChromaDB collections |
355
+ | `GET /health/store` | Full store health report (orphans, dangling links, stale sources, near-duplicates, …) |
356
+ | `POST /revalidate` | Check every code memory against its source file; flag changed or missing ones |
262
357
 
263
358
  ---
264
359
 
@@ -281,7 +376,7 @@ Releases are published to PyPI by [`.github/workflows/release.yml`](.github/work
281
376
 
282
377
  1. Set `version` in `pyproject.toml` and move the `[Unreleased]` entries in [`CHANGELOG.md`](CHANGELOG.md) under the new version.
283
378
  2. Merge to `main` with CI green.
284
- 3. Tag and push: `git tag -a v0.2.0 -m "memkernel 0.2.0" && git push origin v0.2.0`.
379
+ 3. Tag and push: `git tag -a v0.4.0 -m "memkernel 0.4.0" && git push origin v0.4.0`.
285
380
 
286
381
  The workflow refuses a tag that does not match the `pyproject.toml` version, builds the sdist and wheel, smoke-installs the wheel, and uploads through the `pypi` environment.
287
382
 
@@ -293,12 +388,14 @@ The workflow refuses a tag that does not match the `pyproject.toml` version, bui
293
388
  src/memkernel/
294
389
  ├── mcp/ # MCP server + tool definitions (memory tools, wiki tools)
295
390
  ├── core/ # Domain models (tiers, types), MemoryService, ports (interfaces)
296
- ├── storage/ # ChromaDB adapter
297
- ├── retrieval/ # AST chunking, embeddings, hybrid search (dense + BM25 + RRF + rescoring)
391
+ ├── adapters/ # Optional external systems (RLM Studio reasoner), imported lazily
392
+ ├── storage/ # ChromaDB adapter, local document store
393
+ ├── retrieval/ # AST/token chunking, embeddings, hybrid search (dense + BM25 + RRF + rescoring), local reasoner
298
394
  ├── memory/ # Lifecycle: TTL/decay policies, retention, deduplication
299
395
  ├── wiki/ # LLM-wiki bridge: page model, export, import
300
396
  ├── api/ # FastAPI admin endpoints
301
397
  ├── factory.py # build_service() — the supported embedding entry point
398
+ ├── lifecycle.py # MemoryLifecycle — the session protocol as a component
302
399
  └── config.py # Pydantic settings
303
400
  ```
304
401
 
@@ -315,7 +412,7 @@ MemKernel is the memory component of a small family of projects by the same auth
315
412
  | Project | Role | Relationship |
316
413
  |---|---|---|
317
414
  | [ai-wiki](https://github.com/gosha70/ai-wiki) | LLM-maintained markdown wiki (ingest / promote / query / lint) | the human-readable form of long-term memory; `wiki_export` / `wiki_import` speak its page schema (ADR-008) |
318
- | [rlm-studio](https://github.com/gosha70/rlm-studio) | Recursive Language Models workbench: Direct / RAG / RLM / auto over large documents | the document layer; planned `document` memory type and `ask()` tool (ADR-009) |
415
+ | [rlm-studio](https://github.com/gosha70/rlm-studio) | Recursive Language Models workbench: Direct / RAG / RLM / auto over large documents | the document layer behind `document` memories and the `ask()` tool (`MEMKERNEL_REASONER=rlmstudio`, ADR-009) |
319
416
  | [code-copilot-team](https://github.com/gosha70/code-copilot-team) | Enforceable harness configuration for Claude Code, Pi and other copilots | consumes MemKernel through MCP and hooks |
320
417
  | [haa](https://github.com/gosha70/haa) | Home-built AI coding harness, future component assembler | consumes MemKernel in-process through `build_service()` |
321
418
 
@@ -1,10 +1,15 @@
1
- # MemKernel
1
+ <h1>
2
+ <img src="https://raw.githubusercontent.com/gosha70/memkernel/main/docs/assets/memkernel-logo.png" alt="MemKernel logo: a glowing memory core on three stacked tiers" width="110" align="absmiddle">
3
+ MemKernel
4
+ </h1>
2
5
 
3
6
  [![CI](https://github.com/gosha70/memkernel/actions/workflows/ci.yml/badge.svg)](https://github.com/gosha70/memkernel/actions/workflows/ci.yml)
4
7
  [![PyPI](https://img.shields.io/pypi/v/memkernel)](https://pypi.org/project/memkernel/)
5
8
  [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](pyproject.toml)
6
9
  [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)
7
10
 
11
+ **Site:** [memkernel.dev](https://memkernel.dev) · **Package:** [pypi.org/project/memkernel](https://pypi.org/project/memkernel/)
12
+
8
13
  Code-aware memory server for AI coding copilots.
9
14
 
10
15
  MemKernel gives Claude Code (and any MCP-compatible copilot) persistent memory across sessions. When Claude compacts context or a session ends, project decisions, conventions, and code understanding are normally lost. MemKernel stores them in a local vector database and retrieves them on demand.
@@ -17,23 +22,33 @@ MemKernel gives Claude Code (and any MCP-compatible copilot) persistent memory a
17
22
 
18
23
  ## Requirements
19
24
 
20
- - Python 3.11 – 3.13
25
+ - Python 3.11 – 3.13 (the package declares `Requires-Python >=3.11,<3.14`; pip reports "from versions: none" when the installing interpreter is outside that range)
21
26
  - [Poetry](https://python-poetry.org) (for development installs)
22
27
 
23
28
  ---
24
29
 
25
30
  ## Install
26
31
 
32
+ MemKernel is a Python package on PyPI. Its `memkernel` command is the MCP server. There is no npm package: an `npx` install someone suggests is a hallucination.
33
+
34
+ **Recommended: an isolated install with [pipx](https://pipx.pypa.io)**, which puts the `memkernel` command on your PATH without touching any other Python environment:
35
+
27
36
  ```bash
28
- pip install memkernel
37
+ pipx install memkernel
38
+ # if pipx's default interpreter is outside 3.11–3.13:
39
+ pipx install --python python3.11 memkernel
29
40
  ```
30
41
 
31
- Or with Poetry:
42
+ **As a library inside a project**, install it into that project's virtual environment:
32
43
 
33
44
  ```bash
34
- poetry add memkernel
45
+ pip install memkernel # or: poetry add memkernel
35
46
  ```
36
47
 
48
+ Avoid a plain `pip install memkernel` into a shared or system interpreter (Homebrew's `python3`, for example). MemKernel pins `chromadb` 1.x and recent `cffi`, so pip will replace older copies and can break unrelated tools that need them.
49
+
50
+ Verify the install with `pipx list` or `which memkernel`. Running `memkernel` with no arguments starts the MCP server on stdio and waits for a client; `memkernel --version` and `memkernel --help` print and exit.
51
+
37
52
  ---
38
53
 
39
54
  ## Add to Claude Code
@@ -46,17 +61,29 @@ Add MemKernel to `.claude/settings.json` in your project:
46
61
  {
47
62
  "mcpServers": {
48
63
  "memkernel": {
49
- "command": "memkernel",
64
+ "command": "/Users/you/.local/bin/memkernel",
50
65
  "args": [],
51
66
  "env": {
52
- "MEMKERNEL_PROJECT_ID": "my-project"
67
+ "MEMKERNEL_PROJECT_ID": "my-project",
68
+ "MEMKERNEL_CHROMA_PERSIST_PATH": "/Users/you/.memkernel/my-project"
53
69
  }
54
70
  }
55
71
  }
56
72
  }
57
73
  ```
58
74
 
59
- Replace `my-project` with a short identifier for the project — memories are isolated per project.
75
+ Replace `my-project` with a short identifier for the project — memories are isolated per project. Use the absolute path of the command (`which memkernel` prints it) because Claude Code does not necessarily see the PATH changes your shell profile makes, and an absolute `MEMKERNEL_CHROMA_PERSIST_PATH` keeps the store outside the repository. On first server initialization, MemKernel downloads the embedding model (about 90 MB) if it is not already cached: `build_service()` loads the sentence-transformers model when the MCP server starts, before any tool call, so the first connection can take a minute. Later starts and every tool call are fast.
76
+
77
+ <details>
78
+ <summary>Troubleshooting the install</summary>
79
+
80
+ - **`pipx install memkernel` says "No matching distribution found"** — pipx used an interpreter outside 3.11–3.13. Pass one explicitly: `pipx install --python /opt/homebrew/opt/python@3.11/bin/python3.11 memkernel`.
81
+ - **`bash: /some/old/path/memkernel: No such file or directory`** right after reinstalling — the shell cached the old location. Run `hash -r` (bash) or `rehash` (zsh), then `which memkernel`.
82
+ - **`memkernel --help` seems to hang** on versions before 0.4.0 — the command had no flags and started the server; press Ctrl-C. From 0.4.0 on, `--help` and `--version` print and exit.
83
+ - **The first MCP connection is slow or times out** — the server is downloading the embedding model at startup (see above). Wait for it once, or pre-warm it with `python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"` in the same environment.
84
+ - **A global pip install broke another tool** (for example `langchain-chroma` wanting `chromadb < 0.6`) — uninstall memkernel from that interpreter, reinstall the versions the other tool needs, and install memkernel with pipx instead.
85
+
86
+ </details>
60
87
 
61
88
  ### 2. Add memory instructions to CLAUDE.md
62
89
 
@@ -72,7 +99,21 @@ Copy the hook scripts from [`templates/hooks/`](templates/hooks/) to `.claude/ho
72
99
  | `pre-compact.py` | `PreCompact` | Retains a checkpoint episode, runs a consolidation pass |
73
100
  | `post-compact.py` | `PostCompact` | Re-injects the checkpoint, long-term context and promotion candidates |
74
101
 
75
- See the Prerequisites section inside each script for the Python environment requirement.
102
+ See the Prerequisites section inside each script for the Python environment requirement. Each script is a thin wrapper over [`MemoryLifecycle`](#embedding-memkernel-in-your-own-harness), so an in-process harness gets the same behaviour without the scripts.
103
+
104
+ ---
105
+
106
+ ## Add to Kiro
107
+
108
+ Kiro (and the Kiro CLI, formerly Amazon Q Developer CLI) connects to MemKernel as a stdio MCP server. [`templates/kiro/`](templates/kiro/) holds the three files to copy into a workspace:
109
+
110
+ | Template | Destination | Role |
111
+ |----------|-------------|------|
112
+ | `settings/mcp.json` | `.kiro/settings/mcp.json` | Server entry with `env` and `autoApprove` |
113
+ | `steering/memory.md` | `.kiro/steering/memory.md` | Memory instructions, always included |
114
+ | `hooks/memkernel.json` | `.kiro/hooks/memkernel.json` | `SessionStart` recall, `Stop` retain, manual checkpoint |
115
+
116
+ [`templates/kiro/README.md`](templates/kiro/README.md) has the setup steps, the Kiro CLI custom-agent variant and the caveats (absolute persist path, hook format by Kiro version).
76
117
 
77
118
  ---
78
119
 
@@ -81,12 +122,12 @@ See the Prerequisites section inside each script for the Python environment requ
81
122
  | Tool | What it does |
82
123
  |------|-------------|
83
124
  | `retain(content, type, session_id?, checkpoint?, source?, importance?, tags?, supersedes?)` | Store a memory. Returns `ref_id`. Idempotent — identical content + type returns the same `ref_id`. `supersedes` hides the memory it replaces. |
84
- | `recall(query, type?, tier?, session_id?, checkpoint_only?, top_k?)` | Search by natural language or identifier. Returns lightweight `ArtifactRef` list ranked by relevance + recency + importance. Records an access (reinforces retention). |
125
+ | `recall(query, type?, tier?, session_id?, checkpoint_only?, top_k?, include_needs_review?)` | Search by natural language or identifier. Returns lightweight `ArtifactRef` list ranked by relevance + recency + importance. Records an access (reinforces retention). Code memories flagged `needs_review` are hidden unless `include_needs_review=true`. |
85
126
  | `get(ref_id)` | Fetch full content of a specific memory. Records an access. |
86
127
  | `forget(ref_id)` | Delete a memory from all indexes. |
87
128
  | `promote(ref_id, type, content?, importance?, tags?)` | Turn a short-term/working memory into a long-term one (you supply the distilled text). Keeps provenance via `promoted_from`. |
88
- | `consolidate(session_id?, limit?)` | Purge expired short-term memories and list the ones most worth promoting (ranked by importance × how often they were recalled). |
89
- | `forget_expired()` | Delete every short-term/working memory whose retention window lapsed. |
129
+ | `consolidate(session_id?, limit?, synthesize?, revalidate?)` | Purge expired short-term memories and list the ones most worth promoting (ranked by importance × how often they were recalled). `synthesize=true` also asks an external reasoner to draft long-term statements (`suggested`); `revalidate=true` first checks every code memory against its source file (`revalidated`). The report always carries a `health` summary. |
130
+ | `ask(ref_id, question, mode?)` | Answer a question about one memory without loading it — for a `document` the full body is consulted. `mode`: `direct` \| `rag` \| `rlm` \| `auto`, passed to the reasoner unchanged; the built-in reasoner answers in `rag` mode with the best-matching chunks. Returns `answer`, `mode`, `citations`, `usage`. |
90
131
  | `wiki_export(wiki_dir?)` | Compile long-term memories without a wiki page into draft markdown pages (`index.md`, `log.md` updated). |
91
132
  | `wiki_import(wiki_dir?)` | Index curated wiki pages as long-term memories (idempotent; changed pages supersede). |
92
133
 
@@ -98,7 +139,8 @@ See the Prerequisites section inside each script for the Python environment requ
98
139
  | `convention` | long-term | Naming rules, code style, project patterns | 0.9 | Never |
99
140
  | `fact` | long-term | Durable project facts: build commands, environments, contracts | 0.7 | Never |
100
141
  | `procedure` | long-term | How-to that worked: workflows, recipes, verified skills | 0.8 | Never |
101
- | `code` | long-term | Function/class snapshots, API contracts | 0.6 | Never |
142
+ | `code` | long-term | Function/class snapshots, API contracts; identified by `(source, content)`, a changed file supersedes the old snapshot, a drifted source flags `needs_review` | 0.6 | Never (superseded on re-retain) |
143
+ | `document` | long-term | Large artefacts (specs, transcripts, files): the body is stored and chunk-indexed, `get` returns a summary, `ask` answers from the indexed chunks (local reasoner) or the body (RLM Studio) | 0.6 | Never |
102
144
  | `episode` | short-term | Session events, compaction checkpoints | 0.4 | 30 days, extended by every recall |
103
145
  | `observation` | short-term | Findings, tool outputs, things noticed | 0.3 | 7 days, extended by every recall |
104
146
  | `task` | working | The current goal, plan state, open questions | 0.5 | 24 hours |
@@ -111,7 +153,9 @@ Retention: `ttl = base_ttl × (1 + access_boost × access_count)`, measured from
111
153
  2. **While working** — `retain(type="task")` when the plan changes; `retain(type="observation")` for findings; durable knowledge as `decision` / `convention` / `fact` / `procedure`.
112
154
  3. **Before compaction** — `retain(type="episode", checkpoint=true)` (the `pre-compact.py` hook) and `consolidate()`.
113
155
  4. **After compaction / end of phase** — `promote(ref_id, type, content)` for the candidates worth keeping.
114
- 5. **Curate** — `wiki_export` → review the drafts → `wiki_import`.
156
+ 5. **Large artefacts** — `retain(type="document", content=<body>, source=<name>)` then `ask(ref_id, question)`; never `get` a document to read it.
157
+ 7. **Code snapshots** — `retain(type="code", content=<file>, source=<path>)`; retain the file again after editing it (the new snapshot supersedes the old one). The session-start hook revalidates snapshots against their files and lists the ones that need re-retaining.
158
+ 6. **Curate** — `wiki_export` → review the drafts → `wiki_import`.
115
159
 
116
160
  ### Quick examples
117
161
 
@@ -137,6 +181,10 @@ retain(content="Use arq, not Celery", type="decision", supersedes="<old decision
137
181
  get(ref_id="<ref_id from recall>")
138
182
 
139
183
  forget(ref_id="<ref_id>")
184
+
185
+ retain(content=<the whole spec>, type="document", source="specs/auth/spec.md")
186
+
187
+ ask(ref_id="<document ref_id>", question="Which endpoints need rate limiting?", mode="auto")
140
188
  ```
141
189
 
142
190
  ---
@@ -158,6 +206,14 @@ All settings are read from environment variables with the `MEMKERNEL_` prefix, o
158
206
  | `MEMKERNEL_PREVIEW_LENGTH` | `200` | Max characters in the `preview` field of `ArtifactRef` |
159
207
  | `MEMKERNEL_CHUNK_SIZE` | `1500` | Max characters per AST chunk (code memories) |
160
208
  | `MEMKERNEL_CHUNK_OVERLAP` | `200` | Character overlap between token-based fallback chunks |
209
+ | `MEMKERNEL_DOCUMENT_STORE` | `local` | Where `document` bodies live (`local` = the project's own ChromaDB collection) |
210
+ | `MEMKERNEL_DOCUMENT_SUMMARY_CHARS` | `1000` | Characters of the body kept in a `document` memory's summary record |
211
+ | `MEMKERNEL_REASONER` | `local` | `ReasonerPort` behind `ask`: `local` (hybrid search over chunks, no LLM) or `rlmstudio` (falls back to `local` when `rlmstudio` is not importable) |
212
+ | `MEMKERNEL_ASK_TOP_K` | `5` | Chunks the local reasoner returns per `ask` |
213
+ | `MEMKERNEL_PROJECT_ROOT` | `.` | Directory that relative `source` paths of `code` memories resolve under; nothing outside it is ever read. Set it whenever the server or a hook is not started from the project directory |
214
+ | `MEMKERNEL_HEALTH_DUPLICATE_THRESHOLD` | `0.95` | Cosine similarity in [0, 1] from which two long-term memories count as near-duplicates in the health pass |
215
+ | `MEMKERNEL_HEALTH_DUPLICATE_LIMIT` | `500` | Most recent long-term memories the near-duplicate check covers |
216
+ | `MEMKERNEL_HEALTH_REPORT_LIMIT` | `20` | Items listed per category in a health report |
161
217
  | `MEMKERNEL_EPISODE_TTL_DAYS` | `30` | Base TTL for `episode` memories (short-term) |
162
218
  | `MEMKERNEL_OBSERVATION_TTL_DAYS` | `7` | Base TTL for `observation` memories (short-term) |
163
219
  | `MEMKERNEL_TASK_TTL_HOURS` | `24` | Base TTL for `task` memories (working) |
@@ -210,6 +266,35 @@ report = svc.consolidate() # {"purged": n, "candidates": [...]}
210
266
 
211
267
  `build_service()` is the supported entry point; the MCP server, the admin API and the hook templates all use it. Integration notes for `code-copilot-team` and `haa` are in [`docs/integration/harness-integration.md`](docs/integration/harness-integration.md).
212
268
 
269
+ ### The session protocol as a component
270
+
271
+ `MemoryLifecycle` packages what the hook scripts do, so a harness calls four methods instead of re-implementing the protocol. Every method returns markdown ready for injection and does nothing but talk to the service:
272
+
273
+ ```python
274
+ from memkernel.factory import build_service
275
+ from memkernel.lifecycle import MemoryLifecycle
276
+
277
+ memory = MemoryLifecycle(build_service())
278
+ system_context = memory.on_session_start() # checkpoint, long-term context, promotion candidates, wiki pointer
279
+ ...
280
+ memory.on_pre_compact(summary) # checkpoint episode + consolidation, before the context is compacted
281
+ recovered = memory.on_post_compact() # what to re-inject after compaction
282
+ memory.on_session_end(summary) # final checkpoint + consolidation
283
+ ```
284
+
285
+ ### Documents and `ask`
286
+
287
+ `retain(type="document")` keeps large artefacts out of the context window: the body goes to the document store (built-in: the project's own ChromaDB collection), its chunks are indexed for hybrid search, and the memkernel record is a summary. `ask(ref_id, question, mode)` answers through a `ReasonerPort`:
288
+
289
+ - `MEMKERNEL_REASONER=local` (default) — no LLM: the question is matched against the document's chunks with dense + BM25 + RRF and the best chunks come back as the answer, `mode="rag"`.
290
+ - `MEMKERNEL_REASONER=rlmstudio` — delegates to [rlm-studio](https://github.com/gosha70/rlm-studio)'s `interact(content, question, mode=mode)`, so `direct`, `rag`, `rlm` and `auto` select how the document is read (ADR-009). `rlm-studio` is not a memkernel dependency and is not on PyPI yet; install it from git into the same environment:
291
+
292
+ ```bash
293
+ pip install "rlm-studio @ git+https://github.com/gosha70/rlm-studio"
294
+ ```
295
+
296
+ When it is not importable, `build_service()` logs a warning and uses the local reasoner. With an external reasoner, `consolidate(synthesize=True)` also drafts long-term statements from the session's short-term memories.
297
+
213
298
  ## LLM wiki bridge
214
299
 
215
300
  Long-term memory has a human-readable twin: a markdown wiki in the [LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) pattern (`index.md`, `log.md`, typed pages with cited sources — the same schema as `code-copilot-team/knowledge/wiki`).
@@ -219,6 +304,12 @@ Long-term memory has a human-readable twin: a markdown wiki in the [LLM Wiki](ht
219
304
 
220
305
  Set `MEMKERNEL_WIKI_DIR` (for example `knowledge/wiki`) or pass `wiki_dir` to the tools. Rationale and the proposed standalone `ai-wiki` repo: [`docs/adr/ADR-008-llm-wiki-as-semantic-layer.md`](docs/adr/ADR-008-llm-wiki-as-semantic-layer.md).
221
306
 
307
+ ### Revalidation and store health
308
+
309
+ A `code` memory is a snapshot of a file. `MemoryService.revalidate()` hashes each live snapshot's whole source file and compares it with the stored content hash: a changed file flags the memory `needs_review` (`changed`), a vanished file flags it `missing`, a matching file clears the flag. Flagged memories leave default `recall` until the file is retained again (which supersedes the stale snapshot) or they are asked for with `include_needs_review=true`. Revalidation runs from `consolidate(revalidate=true)`, at every session start through `MemoryLifecycle.on_session_start()` (a `### Needs review` section lists what to re-retain), and from `POST /revalidate`. Sources resolve under `MEMKERNEL_PROJECT_ROOT` (default: the current directory); set it whenever the server or a hook is not started from the project directory. Nothing outside that root is ever read. 0.4 hashes whole files; per-chunk invalidation is a follow-up.
310
+
311
+ `MemoryService.health()` audits the store without changing it: orphaned chunk and document records, dangling `supersedes` / `superseded_by` / `promoted_from` / `parent_ref` links, supersessions from 0.2/0.3 that lack the durable marker, `code` memories whose source file is gone, memories flagged for review, expired short-term memories not yet purged and, in full mode only, near-duplicate pairs among the most recent long-term memories (cosine ≥ `MEMKERNEL_HEALTH_DUPLICATE_THRESHOLD`). The quick report (counts only, no embeddings, no content loads) is part of every `consolidate()` report and of the `memory://status` resource; the full report is `GET /health/store`. The pass never fixes anything: a fix mode is a later feature.
312
+
222
313
  ## Admin API
223
314
 
224
315
  A FastAPI admin server provides health and stats endpoints (independent of MCP):
@@ -232,6 +323,8 @@ uvicorn memkernel.api.app:app --host 127.0.0.1 --port 8766
232
323
  | `GET /health` | Server health and memory count |
233
324
  | `GET /stats` | Total memory count, per-type and per-tier breakdown, growth rate, last recall latency |
234
325
  | `GET /collections` | List ChromaDB collections |
326
+ | `GET /health/store` | Full store health report (orphans, dangling links, stale sources, near-duplicates, …) |
327
+ | `POST /revalidate` | Check every code memory against its source file; flag changed or missing ones |
235
328
 
236
329
  ---
237
330
 
@@ -254,7 +347,7 @@ Releases are published to PyPI by [`.github/workflows/release.yml`](.github/work
254
347
 
255
348
  1. Set `version` in `pyproject.toml` and move the `[Unreleased]` entries in [`CHANGELOG.md`](CHANGELOG.md) under the new version.
256
349
  2. Merge to `main` with CI green.
257
- 3. Tag and push: `git tag -a v0.2.0 -m "memkernel 0.2.0" && git push origin v0.2.0`.
350
+ 3. Tag and push: `git tag -a v0.4.0 -m "memkernel 0.4.0" && git push origin v0.4.0`.
258
351
 
259
352
  The workflow refuses a tag that does not match the `pyproject.toml` version, builds the sdist and wheel, smoke-installs the wheel, and uploads through the `pypi` environment.
260
353
 
@@ -266,12 +359,14 @@ The workflow refuses a tag that does not match the `pyproject.toml` version, bui
266
359
  src/memkernel/
267
360
  ├── mcp/ # MCP server + tool definitions (memory tools, wiki tools)
268
361
  ├── core/ # Domain models (tiers, types), MemoryService, ports (interfaces)
269
- ├── storage/ # ChromaDB adapter
270
- ├── retrieval/ # AST chunking, embeddings, hybrid search (dense + BM25 + RRF + rescoring)
362
+ ├── adapters/ # Optional external systems (RLM Studio reasoner), imported lazily
363
+ ├── storage/ # ChromaDB adapter, local document store
364
+ ├── retrieval/ # AST/token chunking, embeddings, hybrid search (dense + BM25 + RRF + rescoring), local reasoner
271
365
  ├── memory/ # Lifecycle: TTL/decay policies, retention, deduplication
272
366
  ├── wiki/ # LLM-wiki bridge: page model, export, import
273
367
  ├── api/ # FastAPI admin endpoints
274
368
  ├── factory.py # build_service() — the supported embedding entry point
369
+ ├── lifecycle.py # MemoryLifecycle — the session protocol as a component
275
370
  └── config.py # Pydantic settings
276
371
  ```
277
372
 
@@ -288,7 +383,7 @@ MemKernel is the memory component of a small family of projects by the same auth
288
383
  | Project | Role | Relationship |
289
384
  |---|---|---|
290
385
  | [ai-wiki](https://github.com/gosha70/ai-wiki) | LLM-maintained markdown wiki (ingest / promote / query / lint) | the human-readable form of long-term memory; `wiki_export` / `wiki_import` speak its page schema (ADR-008) |
291
- | [rlm-studio](https://github.com/gosha70/rlm-studio) | Recursive Language Models workbench: Direct / RAG / RLM / auto over large documents | the document layer; planned `document` memory type and `ask()` tool (ADR-009) |
386
+ | [rlm-studio](https://github.com/gosha70/rlm-studio) | Recursive Language Models workbench: Direct / RAG / RLM / auto over large documents | the document layer behind `document` memories and the `ask()` tool (`MEMKERNEL_REASONER=rlmstudio`, ADR-009) |
292
387
  | [code-copilot-team](https://github.com/gosha70/code-copilot-team) | Enforceable harness configuration for Claude Code, Pi and other copilots | consumes MemKernel through MCP and hooks |
293
388
  | [haa](https://github.com/gosha70/haa) | Home-built AI coding harness, future component assembler | consumes MemKernel in-process through `build_service()` |
294
389
 
@@ -1,11 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "memkernel"
3
- version = "0.2.0"
3
+ version = "0.4.0"
4
4
  description = "Code-aware memory server for AI coding copilots"
5
5
  authors = ["EGOGE"]
6
6
  license = "Apache-2.0"
7
7
  readme = "README.md"
8
8
  repository = "https://github.com/gosha70/memkernel"
9
+ homepage = "https://memkernel.dev"
10
+ documentation = "https://memkernel.dev"
9
11
  classifiers = [
10
12
  "Programming Language :: Python :: 3",
11
13
  "Programming Language :: Python :: 3.11",
@@ -2,4 +2,4 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
  """MemKernel — Code-aware memory server for AI coding copilots."""
4
4
 
5
- __version__ = "0.2.0"
5
+ __version__ = "0.4.0"
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2026 EGOGE
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Adapters to optional external systems. Each imports its package lazily and degrades cleanly."""