@lotargo/memory_plugin 1.6.2 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,9 +3,88 @@
3
3
  All notable changes to `@lotargo/memory_plugin` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [1.6.2] - 2026-08-12
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.6.4] - 2026-08-13
11
+
12
+ ### Added
13
+
14
+ - `memory_info` now reports whether the current Git identity is `Registry: linked`
15
+ or `Registry: unlinked`, enabling agents to register new repositories automatically.
16
+ - RAG documents now have explicit global/project scope associations. The default
17
+ project scope follows the current linked Git identity, while `scope: "all"`
18
+ retrieves only global knowledge plus the current project's knowledge. A source
19
+ shared by several scopes is stored once and associated with each scope.
20
+ - The native OpenCode plugin now exposes `batch_query_knowledge_base`, bringing it
21
+ to all 15 shared tools plus its two OpenCode-only helpers.
22
+
23
+ ### Changed
24
+
25
+ - OpenCode auto-injected memory now includes complete fact bodies for both global and
26
+ current-project stores without the previous ten-fact truncation and instructs the
27
+ agent to register an unlinked Git identity. Header-only initialization was removed
28
+ because it loses essential context.
29
+ - The bundled memory skill now defines full-body session recall, strict project
30
+ isolation, agent-resolved conflicts, automatic Git project registration, and
31
+ selective RAG curation for important web findings and current technical
32
+ documentation, including links from project facts to supporting sources.
33
+ - `recall` hides superseded facts by default and exposes history through
34
+ `includeSuperseded: true`. Filtered output keeps physical storage indices stable,
35
+ so a displayed number always targets the same fact in `forget`.
36
+
37
+ ### Fixed
38
+
39
+ - Re-ingesting an existing path or URL now preserves its document ID, scope
40
+ associations, and fact links while rebuilding chunks, vectors, FTS rows, and
41
+ structural graph edges for changed content.
42
+ - Snapshot export/import and hybrid cloud synchronization now preserve vectors,
43
+ retrieval policies, policy provenance, document scopes, knowledge links, and
44
+ structural graph metadata. The hybrid sync worker also drains tasks queued during
45
+ an active flush instead of leaving them pending.
46
+ - Relinking a Git project now moves its RAG scope associations and fact-to-document
47
+ links together with Notebook facts.
48
+ - Environment-specific setup flags now update only their requested integration.
49
+ Codex setup also synchronizes the skill to both `~/.codex/skills` and the shared
50
+ `~/.agents/skills` location.
51
+ - Shared documents no longer expose fact-link metadata belonging to another project.
52
+ Document deletion now defaults to unlinking only the current project (or global
53
+ outside Git); broader `global` / `all` removal must be requested explicitly.
54
+ - Updating or forgetting a linked Notebook fact now keeps its knowledge-graph
55
+ projection and cloud payload consistent. Line-range graph edges are included in
56
+ per-document cloud sync, and superseding legacy facts without IDs assigns distinct
57
+ IDs to the old and new versions.
58
+ - README tool counts now match the implementations: 15 MCP tools, 17 native
59
+ OpenCode tools, and 17 unique tool names across both surfaces. The OpenCode tool
60
+ list documents all shared and OpenCode-only helpers explicitly.
61
+
62
+ ## [1.6.3] - 2026-08-13
63
+
64
+ ### Added
65
+
66
+ - `memory_plugin doctor --codex` validates the configured executable and Node.js
67
+ version, performs direct MCP `initialize` / `tools/list`, and calls both
68
+ `memory_info` and `recall(scope="all")`.
69
+ - Codex regression coverage for Windows paths with spaces, legacy config migration,
70
+ duplicate removal, standalone MCP protocol smoke testing, two-repository project
71
+ isolation, global-only recall outside Git, and prompt idempotency.
72
+
73
+ ### Fixed
74
+
75
+ - Codex setup no longer launches the memory MCP through `npx` or a Windows `.cmd`
76
+ shim. It writes absolute paths for the active Node executable and packaged
77
+ `mcp-server/boot.js` entry point after validating Node.js >= 22.5.0.
78
+ - Existing plugin-owned `[mcp_servers.memory-agent]` sections, including the legacy
79
+ `opencode-memory-plugin` entry, are migrated in place without modifying unrelated
80
+ TOML sections or creating duplicates.
81
+ - Repeated prompt setup now collapses duplicate plugin-owned blocks while preserving
82
+ user-authored AGENTS.md / CLAUDE.md content.
83
+ - README tool counts match the v1.6.3 implementations: 15 MCP tools, 16 native
84
+ OpenCode tools, and 17 unique tool names across both surfaces. The OpenCode tool
85
+ list and the MCP-only status of `batch_query_knowledge_base` are documented explicitly.
86
+
87
+ ## [1.6.2] - 2026-08-12
9
88
 
10
89
  ### Added
11
90
 
package/README.md CHANGED
@@ -77,9 +77,29 @@ memory_plugin setup --opencode
77
77
  # Claude Code
78
78
  memory_plugin setup --claude
79
79
 
80
- # Codex
81
- memory_plugin setup --codex
82
- ```
80
+ # Codex
81
+ memory_plugin setup --codex
82
+ ```
83
+
84
+ Codex is configured with a direct executable chain (`node` → `mcp-server/boot.js`),
85
+ not `npx`. This avoids Windows stdio handshake failures caused by `.cmd` launchers.
86
+ Running setup again safely migrates legacy `npx` entries and preserves unrelated
87
+ sections in `~/.codex/config.toml`.
88
+
89
+ `memory_plugin setup --codex` updates only the Codex MCP registration, Codex prompt,
90
+ and the Codex-compatible skill locations (`~/.codex/skills` and the shared
91
+ `~/.agents/skills`). It does not modify Claude Code, OpenCode, or Antigravity files.
92
+
93
+ To verify registration, the Node runtime, MCP initialization, tool discovery, and
94
+ real `memory_info` / `recall(scope="all")` calls:
95
+
96
+ ```bash
97
+ memory_plugin doctor --codex
98
+ ```
99
+
100
+ The doctor checks the server process itself. If it passes but a fresh Codex Desktop
101
+ task still does not expose the tools, the remaining fault is in Desktop tool
102
+ exposure rather than the memory MCP launcher.
83
103
 
84
104
  `setup` also accepts `--gemini` (alias for Antigravity) and `--local` (registers the MCP server in the project-local `.agents/` directory). Without a specific flag, all detected environments are configured.
85
105
 
@@ -106,10 +126,13 @@ memory_plugin setup --mode only-cloud
106
126
  - **Fact Schema**: Every fact is formatted as `**Title** — body` with inline metadata badges (`[id]`, `[ttl]`, `[keep]`, `[tags]`, `[supersedes]`, `[inject]`).
107
127
  - **Project Identity**: Project stores are bound to a **Git-based project identity** — the normalized remote URL (`git:github.com/owner/repo`) or `git:local:<repo basename>` — never to a directory path. Memories follow the repository across machines, OSes, and subdirectories. Legacy path/basename stores can be linked and merged via `link_project_memory`.
108
128
 
109
- 2. **Layer 2: RAG Knowledge Base (Technical Documents & Codebases)**
110
- - **Tools**: `ingest_document`, `query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base`
111
- - **Capabilities**: Ingests raw text files, Markdown, HTML, Web URLs, office documents (PDF, DOCX, XLSX, CSV), and codebases.
112
- - **Engine Components**: 3-tier hierarchy chunking (Big / Medium / Small), SQLite FTS5 BM25 search, ONNX dense vector embeddings (`multilingual-e5-small`), Reciprocal Rank Fusion (RRF / RSF), cross-encoder reranking (optional), and GraphRAG Lite code symbol extraction.
129
+ 2. **Layer 2: RAG Knowledge Base (Technical Documents & Codebases)**
130
+ - **Tools**: `ingest_document`, `query_knowledge_base`, `batch_query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base`
131
+ - **Capabilities**: Ingests raw text files, Markdown, HTML, Web URLs, office documents (PDF, DOCX, XLSX, CSV), and codebases.
132
+ - **Curation Model**: Stores only project-relevant sources likely to be reused—for example important web findings and current library/framework documentation that may be newer than model training. Ingest the useful document or excerpt, then link it to the project memory it supports; do not archive everything the agent encounters.
133
+ - **Scope Isolation**: New documents default to the current linked Git project. `scope: "all"` searches global sources plus the current project's sources; outside a Git repository it searches global sources only. Use `scope: "global"` only for genuinely cross-project material.
134
+ - **Stable Shared Sources**: Ingesting the same path or URL in another scope reuses one document and adds a scope association. Re-ingesting updated content preserves the document ID and its fact links; removing one scope does not delete a document still used by another scope.
135
+ - **Engine Components**: 3-tier hierarchy chunking (Big / Medium / Small), SQLite FTS5 BM25 search, ONNX dense vector embeddings (`multilingual-e5-small`), Reciprocal Rank Fusion (RRF / RSF), cross-encoder reranking (optional), and GraphRAG Lite code symbol extraction.
113
136
 
114
137
  3. **Layer 3: Agent-Driven Knowledge Graph**
115
138
  - **Tools**: `link_knowledge` (plus `docId`, `startLine`, `endLine` in `remember`)
@@ -147,22 +170,33 @@ Cloud authentication tokens are never written to `config.json`. They are stored
147
170
 
148
171
  ---
149
172
 
150
- ## Available MCP Tools
151
-
152
- The MCP server registers **14 MCP tools** accessible across all connected AI environments, plus **2 OpenCode-plugin helper tools** available only inside OpenCode:
153
-
154
- ### 1. Memory Notebook Tools (Layer 1)
173
+ ## Available Tools
174
+
175
+ The package currently exposes **17 unique tool names** across its two integration surfaces:
176
+
177
+ | Integration surface | Tool count | Composition |
178
+ | :------------------ | ---------: | :---------- |
179
+ | **MCP server** (Codex, Claude Code, Antigravity / Gemini CLI and other MCP clients) | **15** | 6 Notebook + 4 identity/graph + 5 RAG tools |
180
+ | **Native OpenCode plugin** | **17** | 15 shared memory/identity/RAG tools + 2 OpenCode-only helpers |
181
+
182
+ The two surfaces are intentionally counted separately. The OpenCode plugin exposes
183
+ all 15 shared tools, including `batch_query_knowledge_base`, and adds the native
184
+ `list-mcp-tools` and `mcp-reminder` helpers.
185
+
186
+ ### MCP Server Tools (15)
187
+
188
+ #### 1. Memory Notebook Tools (Layer 1)
155
189
 
156
190
  | Tool | Scope / Target | Key Parameters | Description |
157
191
  | :--- | :------------- | :------------- | :---------- |
158
192
  | `remember` | `project` / `global` | `fact`, `title`, `scope`, `docId`, `startLine`, `endLine`, `relationType`, `ttl`, `keep`, `tags`, `supersedes` | Save a durable fact or preference. Supports optional title, document linking, TTL, keep protection, tags, and version superseding. |
159
- | `recall` | `all`, `project`, `global`, `list_projects` | `scope`, `project`, `query`, `tags`, `since`, `until`, `mode`, `offset`, `limit` | Display saved facts with metadata badges and linked docs. Supports cross-project lookup via `project: '<path>'` and header-only mode (`mode: "headers"`). |
193
+ | `recall` | `all`, `project`, `global`, `list_projects` | `scope`, `project`, `query`, `tags`, `since`, `until`, `mode`, `offset`, `limit`, `includeSuperseded` | Display saved facts with metadata badges and linked docs. Superseded facts are hidden by default; opt into history with `includeSuperseded: true`. Filtered results retain stable storage indices for safe `forget` operations. |
160
194
  | `get_fact` | `all`, `project`, `global` | `id`, `scope` | Retrieve full text, raw line, and metadata of a single fact by its metadata ID (e.g. `"8f3a2c"`). |
161
195
  | `update_fact` | `project` / `global` | `id`, `newText`, `title`, `scope` | Rewrite a fact (and optionally its `**Title**`) while preserving its original creation date, metadata, and knowledge links. |
162
196
  | `forget` | `project` / `global` | `id` / `range` / `query`, `scope`, `force` | Remove a fact by index number, ID, range (e.g. `"3-30"`), or query. Requires `force: true` for protected (`[KEEP]`) facts. |
163
197
  | `memory_info` | - | - | Show storage paths, fact counts, RAG statistics, git identity bindings, and package version. |
164
198
 
165
- ### 2. Project Identity Tools
199
+ #### 2. Project Identity Tools
166
200
 
167
201
  | Tool | Key Parameters | Description |
168
202
  | :--- | :------------- | :---------- |
@@ -170,23 +204,32 @@ The MCP server registers **14 MCP tools** accessible across all connected AI env
170
204
  | `unlink_project_memory` | `directory`, `purge` | Remove a path alias binding for a directory. Optionally purge the project identity entry if `purge: true`. |
171
205
  | `relink_project_memory` | `directory`, `remote` | Switch a project's primary identity to a new remote URL and merge all stored facts into the target store with fact-text deduplication. |
172
206
 
173
- ### 3. RAG Knowledge Base & Graph Tools (Layers 2 & 3)
207
+ #### 3. RAG Knowledge Base & Graph Tools (Layers 2 & 3)
174
208
 
175
209
  | Tool | Key Parameters | Description |
176
210
  | :--- | :------------- | :---------- |
177
- | `ingest_document` | `content`, `type`, `title`, `path`, `generateEmbeddings` | Ingest local files, URLs, or raw text into the 3-tier index (Big/Medium/Small) with ONNX vector embeddings and GraphRAG symbol extraction. |
178
- | `query_knowledge_base` | `query`, `limit`, `instruction`, `generateEmbeddings` | Perform hybrid search (RSF/RRF BM25 + dense vector similarity) to retrieve candidate document sections with defined code symbols. |
179
- | `batch_query_knowledge_base` | `queries` (array), `limit`, `instruction`, `generateEmbeddings` | Execute multiple queries in a single batch call. More efficient than separate `query_knowledge_base` calls — all embeddings computed in one ONNX pass, queries run in parallel. Ideal for comparisons and multi-topic analysis. |
180
- | `manage_knowledge_base` | `action`, `docId`, `snapshotPath` | Inspect DB stats (`stats`), list documents (`list`), read full raw document (`read_document`), delete document (`delete`), or export/import snapshots (`export_snapshot` / `import_snapshot`). |
211
+ | `ingest_document` | `content`, `type`, `title`, `path`, `scope`, `generateEmbeddings` | Ingest local files, URLs, or raw text into the 3-tier index. Defaults to the current linked Git project; use `scope: "global"` for intentionally shared knowledge. |
212
+ | `query_knowledge_base` | `query`, `scope`, `limit`, `instruction`, `generateEmbeddings` | Perform hybrid search (RSF/RRF BM25 + dense vectors). `all` means global plus the current project, while `project` and `global` restrict retrieval explicitly. |
213
+ | `batch_query_knowledge_base` | `queries` (array), `scope`, `limit`, `instruction`, `generateEmbeddings` | Execute multiple scoped queries in one call. All embeddings are computed in one ONNX pass, making this the preferred API for comparisons and multi-part research. |
214
+ | `manage_knowledge_base` | `action`, `scope`, `docId`, `snapshotPath` | Inspect, list, read, or unlink documents within the selected scope, or export/import complete snapshots. Delete defaults to the current project (global outside Git); broader removal requires explicit `global` or `all`. |
181
215
  | `reindex_knowledge_base` | `model`, `dimension` | Re-embed all stored vectors with the active (or specified) embedding model and vector dimension. Use after switching the embedding model or vector dimension so previously indexed documents remain retrievable. Preserves documents, FTS index, graph edges, and fact links. |
182
216
  | `link_knowledge` | `action`, `factText`, `docId`, `scope`, `startLine`, `endLine`, `relationType` | Create, list, or retrieve semantic graph links connecting Notebook facts to Knowledge Base documents, sections, or line ranges. Actions: `link`, `list_links`, `get_doc_links`. |
183
217
 
184
- ### 4. Agent & OpenCode Helpers (OpenCode plugin only, not exposed by the MCP server)
185
-
186
- | Tool | Key Parameters | Description |
187
- | :--- | :------------- | :---------- |
188
- | `list-mcp-tools` | - | Discover all connected MCP servers and their available tool definitions. |
189
- | `mcp-reminder` | `task` | Recommends the appropriate MCP tool or server for a specific developer task. |
218
+ ### Native OpenCode Plugin Tools (17)
219
+
220
+ | Group | Count | Tools |
221
+ | :---- | ----: | :---- |
222
+ | **Memory Notebook** | 6 | `remember`, `recall`, `get_fact`, `forget`, `update_fact`, `memory_info` |
223
+ | **Project Identity & Knowledge Graph** | 4 | `link_knowledge`, `link_project_memory`, `unlink_project_memory`, `relink_project_memory` |
224
+ | **RAG Knowledge Base** | 5 | `ingest_document`, `query_knowledge_base`, `batch_query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base` |
225
+ | **OpenCode-only helpers** | 2 | `list-mcp-tools`, `mcp-reminder` |
226
+
227
+ The OpenCode-only helpers have the following purpose:
228
+
229
+ | Tool | Key Parameters | Description |
230
+ | :--- | :------------- | :---------- |
231
+ | `list-mcp-tools` | - | Discover connected MCP servers and their intended use cases. |
232
+ | `mcp-reminder` | `task` | Recommend the appropriate MCP tool or server for a developer task. |
190
233
 
191
234
  ---
192
235
 
@@ -216,7 +259,8 @@ Both binaries accept the same commands. `memory_plugin` with **no** command star
216
259
  | **`identity`** | `--dir <path>` | Inspects Git project identity key, primary remote, name, and toplevel path for a directory. |
217
260
  | **`migrate_titles`** | `--key <key>` | Auto-generates `**Title**` prefixes for legacy facts without titles. |
218
261
  | **`enable-prompt`** | - | Injects memory agent instructions into client agent files (`AGENTS.md`, `CLAUDE.md`). |
219
- | **`disable-prompt`** | - | Removes memory agent instructions from client agent files. |
262
+ | **`disable-prompt`** | - | Removes memory agent instructions from client agent files. |
263
+ | **`doctor`** | `--codex` | Validates Codex config, direct Node runtime, MCP initialize/tools/list, and live `memory_info` + `recall` calls. |
220
264
  | **`login`** | `--api-token`, `--from-env`, `--db-url <URL> --db-token`, `$TURSO_API_TOKEN`, `$TURSO_DB_TOKEN` | Authenticates with Turso Cloud via API token, direct DB token, or environment variables. Token values are read from the environment or a hidden stdin prompt. |
221
265
  | **`logout`** | `--api-key` | Signs out of Turso Cloud or removes stored API key while retaining DB session. |
222
266
  | **`auth-status`** | - | Displays authentication source, endpoint URL, username, organization, database, and sync mode. |
@@ -246,10 +290,12 @@ Use **Up / Down** arrows to navigate, **ENTER** to select, and **BACKSPACE** to
246
290
 
247
291
  ### Built-in Agent Skill (`using-memory`)
248
292
 
249
- The plugin bundles a pre-configured Agent Skill located at [`skills/using-memory/SKILL.md`](./skills/using-memory/SKILL.md). When installed in supported environments (such as Antigravity / Gemini CLI or OpenCode), coding agents automatically read this skill to:
250
- 1. **Initialize Sessions**: Automatically invoke `recall(scope: "all")` at the start of every chat turn to load all saved preferences and project rules.
251
- 2. **Proactively Save Context**: Automatically call `remember` whenever you share durable facts, tech stack choices, or coding guidelines.
252
- 3. **Architect Knowledge Graphs**: Use `link_knowledge` or `remember(docId, startLine, endLine)` to connect memories directly to technical documentation.
293
+ The plugin bundles a pre-configured Agent Skill located at [`skills/using-memory/SKILL.md`](./skills/using-memory/SKILL.md). When installed in supported environments (such as Antigravity / Gemini CLI or OpenCode), coding agents automatically read this skill to:
294
+ 1. **Initialize Sessions**: Invoke full-body `recall(scope: "all")` first, loading global memory plus only the current Git project's memory (or global-only outside Git).
295
+ 2. **Register Git Projects**: Inspect `memory_info` after recall and automatically call `link_project_memory` when the current Git identity reports `Registry: unlinked`.
296
+ 3. **Proactively Save Context**: Automatically call `remember` whenever you share durable facts, tech stack choices, or coding guidelines.
297
+ 4. **Curate and Use RAG Deliberately**: Preserve important web findings and current technical documentation only when they are likely to matter again. Store project-specific sources in the current project scope, reserve global scope for reusable cross-project material, query with concept-dense searches, and avoid low-value bulk ingestion.
298
+ 5. **Architect Knowledge Graphs**: Use `link_knowledge` or `remember(docId, startLine, endLine)` to connect concise project memories to their supporting technical sources.
253
299
 
254
300
  ### Global System Prompt Synchronization
255
301
 
@@ -295,7 +341,7 @@ During `ingest_document`, code symbols are extracted from code blocks using fast
295
341
  | Platform | Status | Configuration Mechanism |
296
342
  | :--- | :--- | :--- |
297
343
  | **Antigravity / Gemini CLI** | Supported | MCP Server (`~/.gemini/config/mcp_config.json` & `.agents/mcp_config.json`) |
298
- | **OpenCode** | Native | Native plugin + MCP Server (`~/.config/opencode/opencode.json`) |
344
+ | **OpenCode** | Native | Native plugin with 16 tools (`~/.config/opencode/opencode.json`) |
299
345
  | **Claude Code** | Supported | MCP Server (`~/.claude.json`) |
300
346
  | **Codex** | Supported | MCP Server (`~/.codex/config.toml`) |
301
347
  | **Google Jules** | Supported | MCP Server via global install + setup (`memory_plugin setup`) |
@@ -321,7 +367,6 @@ The engine is configured through `<memory-dir>/config.json` (created with defaul
321
367
  | `executionDevice` | `cpu` | `cpu` or `webgpu` (experimental) |
322
368
  | `vectorScanLimit` | `50000` | Max micro-chunks scanned per vector query (`0` = unlimited) |
323
369
  | `policyExpansion` | `true` | Expand table_summary/code_signature policy chunks for better recall (slight MRR trade-off). Disable for pure micro_chunk precision. |
324
- | `injectLimit` | `10` | Max facts injected into the agent's system prompt |
325
370
  | `conflictStrategy` | `merge` | Hybrid-sync conflict resolution: `merge`, `cloud-wins`, or `local-wins` |
326
371
  | `tursoUrl` | `""` | Primary Turso endpoint URL (set by `login`) |
327
372
  | `failoverUrl` | `""` | Secondary cloud endpoint for the circuit breaker; empty = failover disabled |
@@ -339,7 +384,7 @@ The engine is configured through `<memory-dir>/config.json` (created with defaul
339
384
  To run the automated test suite and benchmarks locally, from the repository root:
340
385
 
341
386
  ```bash
342
- # Unit + integration + cloud suites (12 files) — fast and fully offline
387
+ # Unit + integration + cloud suites (18 files) — fast and fully offline
343
388
  npm test
344
389
 
345
390
  # End-to-end smoke test with REAL ONNX embeddings — run before a release
@@ -72,8 +72,10 @@ export async function exportSnapshot({ customDb = null, customBlobDir = BLOBS_DI
72
72
  const documents = await db.prepare("SELECT * FROM documents").all();
73
73
  const sections = await db.prepare("SELECT * FROM sections").all();
74
74
  const mediumChunks = await db.prepare("SELECT * FROM medium_chunks").all();
75
- const rawMicroChunks = await db.prepare("SELECT * FROM micro_chunks").all();
76
- const graphEdges = await db.prepare("SELECT * FROM graph_edges").all();
75
+ const rawMicroChunks = await db.prepare("SELECT * FROM micro_chunks").all();
76
+ const graphEdges = await db.prepare("SELECT * FROM graph_edges").all();
77
+ const knowledgeLinks = await db.prepare("SELECT * FROM knowledge_links").all();
78
+ const documentScopes = await db.prepare("SELECT * FROM document_scopes").all();
77
79
 
78
80
  const microChunks = rawMicroChunks.map((mc) => {
79
81
  let vecBase64 = "";
@@ -99,14 +101,16 @@ export async function exportSnapshot({ customDb = null, customBlobDir = BLOBS_DI
99
101
  }
100
102
 
101
103
  const snapshot = {
102
- version: 2,
104
+ version: 3,
103
105
  created_at: new Date().toISOString(),
104
106
  documents,
105
107
  sections,
106
108
  medium_chunks: mediumChunks,
107
109
  micro_chunks: microChunks,
108
- graph_edges: graphEdges,
109
- blobs,
110
+ graph_edges: graphEdges,
111
+ knowledge_links: knowledgeLinks,
112
+ document_scopes: documentScopes,
113
+ blobs,
110
114
  };
111
115
 
112
116
  const jsonStr = JSON.stringify(snapshot, null, 2);
@@ -187,14 +191,16 @@ export async function importSnapshot({ customDb = null, customBlobDir = BLOBS_DI
187
191
  token_count=excluded.token_count
188
192
  `);
189
193
 
190
- const insertChunk = db.prepare(`
191
- INSERT INTO micro_chunks (id, section_id, doc_id, content, vector, token_count, medium_id)
192
- VALUES (?, ?, ?, ?, ?, ?, ?)
193
- ON CONFLICT(id) DO UPDATE SET
194
- content=excluded.content,
195
- vector=excluded.vector,
196
- token_count=excluded.token_count,
197
- medium_id=excluded.medium_id
194
+ const insertChunk = db.prepare(`
195
+ INSERT INTO micro_chunks (id, section_id, doc_id, content, vector, token_count, medium_id, retrieval_policy, policy_source_id)
196
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
197
+ ON CONFLICT(id) DO UPDATE SET
198
+ content=excluded.content,
199
+ vector=excluded.vector,
200
+ token_count=excluded.token_count,
201
+ medium_id=excluded.medium_id,
202
+ retrieval_policy=excluded.retrieval_policy,
203
+ policy_source_id=excluded.policy_source_id
198
204
  `);
199
205
 
200
206
  const insertFts = db.prepare(`
@@ -204,11 +210,32 @@ export async function importSnapshot({ customDb = null, customBlobDir = BLOBS_DI
204
210
 
205
211
  const deleteFts = db.prepare("DELETE FROM micro_chunks_fts WHERE id = ?");
206
212
 
207
- const insertEdge = db.prepare(`
208
- INSERT INTO graph_edges (source_id, target_id, relation_type)
209
- VALUES (?, ?, ?)
210
- ON CONFLICT(source_id, target_id, relation_type) DO NOTHING
211
- `);
213
+ const insertEdge = db.prepare(`
214
+ INSERT INTO graph_edges (source_id, target_id, relation_type, metadata_json, created_at)
215
+ VALUES (?, ?, ?, ?, ?)
216
+ ON CONFLICT(source_id, target_id, relation_type) DO UPDATE SET
217
+ metadata_json=excluded.metadata_json,
218
+ created_at=excluded.created_at
219
+ `);
220
+ const insertScope = db.prepare(`
221
+ INSERT OR IGNORE INTO document_scopes (doc_id, scope_key, created_at)
222
+ VALUES (?, ?, ?)
223
+ `);
224
+ const insertLink = db.prepare(`
225
+ INSERT INTO knowledge_links
226
+ (id, fact_key, fact_text, doc_id, section_id, start_line, end_line, relation_type, metadata_json, created_at)
227
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
228
+ ON CONFLICT(id) DO UPDATE SET
229
+ fact_key=excluded.fact_key,
230
+ fact_text=excluded.fact_text,
231
+ doc_id=excluded.doc_id,
232
+ section_id=excluded.section_id,
233
+ start_line=excluded.start_line,
234
+ end_line=excluded.end_line,
235
+ relation_type=excluded.relation_type,
236
+ metadata_json=excluded.metadata_json,
237
+ created_at=excluded.created_at
238
+ `);
212
239
 
213
240
  await db.exec("BEGIN IMMEDIATE;");
214
241
  try {
@@ -228,11 +255,23 @@ export async function importSnapshot({ customDb = null, customBlobDir = BLOBS_DI
228
255
  }
229
256
  }
230
257
 
231
- if (Array.isArray(snapshot.sections)) {
258
+ if (Array.isArray(snapshot.sections)) {
232
259
  for (const s of snapshot.sections) {
233
260
  await insertSection.run(s.id, s.doc_id, s.heading, s.breadcrumbs, s.content, s.token_count);
234
261
  }
235
- }
262
+ }
263
+
264
+ if (Array.isArray(snapshot.document_scopes) && snapshot.document_scopes.length > 0) {
265
+ for (const scope of snapshot.document_scopes) {
266
+ await insertScope.run(scope.doc_id, scope.scope_key || "global", scope.created_at || Date.now());
267
+ }
268
+ } else if (Array.isArray(snapshot.documents)) {
269
+ // v1/v2 snapshots predate project-scoped RAG and retain their historical
270
+ // globally-visible behavior after import.
271
+ for (const d of snapshot.documents) {
272
+ await insertScope.run(d.id, "global", d.created_at || Date.now());
273
+ }
274
+ }
236
275
 
237
276
  if (Array.isArray(snapshot.medium_chunks)) {
238
277
  for (const m of snapshot.medium_chunks) {
@@ -246,7 +285,17 @@ export async function importSnapshot({ customDb = null, customBlobDir = BLOBS_DI
246
285
  if (mc.vector) {
247
286
  vecBuf = Buffer.from(mc.vector, "base64");
248
287
  }
249
- await insertChunk.run(mc.id, mc.section_id, mc.doc_id, mc.content, vecBuf, mc.token_count, mc.medium_id || null);
288
+ await insertChunk.run(
289
+ mc.id,
290
+ mc.section_id,
291
+ mc.doc_id,
292
+ mc.content,
293
+ vecBuf,
294
+ mc.token_count,
295
+ mc.medium_id || null,
296
+ mc.retrieval_policy || "micro_chunk",
297
+ mc.policy_source_id || null
298
+ );
250
299
 
251
300
  try {
252
301
  await deleteFts.run(mc.id);
@@ -255,10 +304,26 @@ export async function importSnapshot({ customDb = null, customBlobDir = BLOBS_DI
255
304
  }
256
305
  }
257
306
 
258
- if (Array.isArray(snapshot.graph_edges)) {
259
- for (const e of snapshot.graph_edges) {
260
- await insertEdge.run(e.source_id, e.target_id, e.relation_type);
261
- }
307
+ if (Array.isArray(snapshot.graph_edges)) {
308
+ for (const e of snapshot.graph_edges) {
309
+ await insertEdge.run(e.source_id, e.target_id, e.relation_type, e.metadata_json || null, e.created_at || null);
310
+ }
311
+ }
312
+ if (Array.isArray(snapshot.knowledge_links)) {
313
+ for (const link of snapshot.knowledge_links) {
314
+ await insertLink.run(
315
+ link.id,
316
+ link.fact_key,
317
+ link.fact_text,
318
+ link.doc_id,
319
+ link.section_id || null,
320
+ link.start_line || null,
321
+ link.end_line || null,
322
+ link.relation_type || "LINKS_TO",
323
+ link.metadata_json || null,
324
+ link.created_at || Date.now()
325
+ );
326
+ }
262
327
  }
263
328
  await db.exec("COMMIT;");
264
329
  } catch (err) {
@@ -271,7 +336,9 @@ export async function importSnapshot({ customDb = null, customBlobDir = BLOBS_DI
271
336
  sections: snapshot.sections ? snapshot.sections.length : 0,
272
337
  medium_chunks: snapshot.medium_chunks ? snapshot.medium_chunks.length : 0,
273
338
  micro_chunks: snapshot.micro_chunks ? snapshot.micro_chunks.length : 0,
274
- graph_edges: snapshot.graph_edges ? snapshot.graph_edges.length : 0,
339
+ graph_edges: snapshot.graph_edges ? snapshot.graph_edges.length : 0,
340
+ knowledge_links: snapshot.knowledge_links ? snapshot.knowledge_links.length : 0,
341
+ document_scopes: snapshot.document_scopes ? snapshot.document_scopes.length : 0,
275
342
  blobs: blobCount,
276
343
  };
277
344
  }
@@ -11,7 +11,16 @@ import {
11
11
  } from "../memory.js";
12
12
  import { factBody } from "../fact_format.js";
13
13
 
14
- export async function handleDirectCommands(cliArgs) {
14
+ export async function handleDirectCommands(cliArgs) {
15
+ if (cliArgs[0] === "doctor" && cliArgs.includes("--codex")) {
16
+ const { runCodexDoctor } = await import("../codex_diagnostics.js");
17
+ console.log("\nCodex memory-agent diagnostics\n");
18
+ const result = await runCodexDoctor();
19
+ console.log("");
20
+ if (!result.ok) process.exitCode = 1;
21
+ return true;
22
+ }
23
+
15
24
  if (cliArgs[0] === "link") {
16
25
  const dirIdx = cliArgs.indexOf("--dir");
17
26
  const dir = dirIdx >= 0 && cliArgs[dirIdx + 1] ? cliArgs[dirIdx + 1] : process.cwd();
@@ -127,9 +136,11 @@ export async function handleDirectCommands(cliArgs) {
127
136
  }
128
137
 
129
138
  await writeMemory(targetKey, targetFacts);
130
- await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
131
- await upsertIdentity(db, { key: targetKey, name: sourceIdentity.name, primaryRemote: normalizeRemoteUrl(remote) });
132
- await removeIdentity(db, sourceKey);
139
+ await upsertIdentity(db, { key: targetKey, name: sourceIdentity.name, primaryRemote: normalizeRemoteUrl(remote) });
140
+ await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
141
+ const { moveKnowledgeScope } = await import("../graph/knowledge_linker.js");
142
+ await moveKnowledgeScope(db, sourceKey, targetKey);
143
+ await removeIdentity(db, sourceKey);
133
144
 
134
145
  try {
135
146
  const sourceFp = storeFilePath(sourceKey);
@@ -405,8 +405,10 @@ export async function handleStorageAction(value, config, stats) {
405
405
  }
406
406
 
407
407
  await writeMemory(targetKey, targetFacts);
408
- await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
409
408
  await upsertIdentity(db, { key: targetKey, name: identity.name, primaryRemote: normalizeRemoteUrl(targetRemote) });
409
+ await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
410
+ const { moveKnowledgeScope } = await import("../../graph/knowledge_linker.js");
411
+ await moveKnowledgeScope(db, sourceKey, targetKey);
410
412
  await removeIdentity(db, sourceKey);
411
413
 
412
414
  try {
package/mcp-server/cli.js CHANGED
@@ -23,7 +23,8 @@ Usage:
23
23
  memory-cli auth-status
24
24
  memory-cli link|unlink|relink|identity [--dir <path>] [--remote <url>]
25
25
  memory-cli migrate_titles [--key <key>]
26
- memory-cli enable-prompt | disable-prompt
26
+ memory-cli enable-prompt | disable-prompt
27
+ memory-cli doctor --codex
27
28
 
28
29
  Options:
29
30
  -h, --help Show this help text`);