@lotargo/memory_plugin 1.6.3 → 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
@@ -7,6 +7,58 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
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
+
10
62
  ## [1.6.3] - 2026-08-13
11
63
 
12
64
  ### Added
@@ -28,7 +80,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
28
80
  TOML sections or creating duplicates.
29
81
  - Repeated prompt setup now collapses duplicate plugin-owned blocks while preserving
30
82
  user-authored AGENTS.md / CLAUDE.md content.
31
- - README tool counts now match the implementations: 15 MCP tools, 16 native
83
+ - README tool counts match the v1.6.3 implementations: 15 MCP tools, 16 native
32
84
  OpenCode tools, and 17 unique tool names across both surfaces. The OpenCode tool
33
85
  list and the MCP-only status of `batch_query_knowledge_base` are documented explicitly.
34
86
 
package/README.md CHANGED
@@ -86,6 +86,10 @@ not `npx`. This avoids Windows stdio handshake failures caused by `.cmd` launche
86
86
  Running setup again safely migrates legacy `npx` entries and preserves unrelated
87
87
  sections in `~/.codex/config.toml`.
88
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
+
89
93
  To verify registration, the Node runtime, MCP initialization, tool discovery, and
90
94
  real `memory_info` / `recall(scope="all")` calls:
91
95
 
@@ -122,10 +126,13 @@ memory_plugin setup --mode only-cloud
122
126
  - **Fact Schema**: Every fact is formatted as `**Title** — body` with inline metadata badges (`[id]`, `[ttl]`, `[keep]`, `[tags]`, `[supersedes]`, `[inject]`).
123
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`.
124
128
 
125
- 2. **Layer 2: RAG Knowledge Base (Technical Documents & Codebases)**
129
+ 2. **Layer 2: RAG Knowledge Base (Technical Documents & Codebases)**
126
130
  - **Tools**: `ingest_document`, `query_knowledge_base`, `batch_query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base`
127
- - **Capabilities**: Ingests raw text files, Markdown, HTML, Web URLs, office documents (PDF, DOCX, XLSX, CSV), and codebases.
128
- - **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.
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.
129
136
 
130
137
  3. **Layer 3: Agent-Driven Knowledge Graph**
131
138
  - **Tools**: `link_knowledge` (plus `docId`, `startLine`, `endLine` in `remember`)
@@ -170,11 +177,11 @@ The package currently exposes **17 unique tool names** across its two integratio
170
177
  | Integration surface | Tool count | Composition |
171
178
  | :------------------ | ---------: | :---------- |
172
179
  | **MCP server** (Codex, Claude Code, Antigravity / Gemini CLI and other MCP clients) | **15** | 6 Notebook + 4 identity/graph + 5 RAG tools |
173
- | **Native OpenCode plugin** | **16** | 14 shared memory/identity/RAG tools + 2 OpenCode-only helpers |
180
+ | **Native OpenCode plugin** | **17** | 15 shared memory/identity/RAG tools + 2 OpenCode-only helpers |
174
181
 
175
- The two surfaces are intentionally counted separately. The OpenCode plugin adds
176
- `list-mcp-tools` and `mcp-reminder`, but currently does not register the MCP-only
177
- `batch_query_knowledge_base` tool.
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.
178
185
 
179
186
  ### MCP Server Tools (15)
180
187
 
@@ -183,7 +190,7 @@ The two surfaces are intentionally counted separately. The OpenCode plugin adds
183
190
  | Tool | Scope / Target | Key Parameters | Description |
184
191
  | :--- | :------------- | :------------- | :---------- |
185
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. |
186
- | `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. |
187
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"`). |
188
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. |
189
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. |
@@ -201,20 +208,20 @@ The two surfaces are intentionally counted separately. The OpenCode plugin adds
201
208
 
202
209
  | Tool | Key Parameters | Description |
203
210
  | :--- | :------------- | :---------- |
204
- | `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. |
205
- | `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. |
206
- | `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. |
207
- | `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`. |
208
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. |
209
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`. |
210
217
 
211
- ### Native OpenCode Plugin Tools (16)
218
+ ### Native OpenCode Plugin Tools (17)
212
219
 
213
220
  | Group | Count | Tools |
214
221
  | :---- | ----: | :---- |
215
222
  | **Memory Notebook** | 6 | `remember`, `recall`, `get_fact`, `forget`, `update_fact`, `memory_info` |
216
223
  | **Project Identity & Knowledge Graph** | 4 | `link_knowledge`, `link_project_memory`, `unlink_project_memory`, `relink_project_memory` |
217
- | **RAG Knowledge Base** | 4 | `ingest_document`, `query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base` |
224
+ | **RAG Knowledge Base** | 5 | `ingest_document`, `query_knowledge_base`, `batch_query_knowledge_base`, `manage_knowledge_base`, `reindex_knowledge_base` |
218
225
  | **OpenCode-only helpers** | 2 | `list-mcp-tools`, `mcp-reminder` |
219
226
 
220
227
  The OpenCode-only helpers have the following purpose:
@@ -283,10 +290,12 @@ Use **Up / Down** arrows to navigate, **ENTER** to select, and **BACKSPACE** to
283
290
 
284
291
  ### Built-in Agent Skill (`using-memory`)
285
292
 
286
- 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:
287
- 1. **Initialize Sessions**: Automatically invoke `recall(scope: "all")` at the start of every chat turn to load all saved preferences and project rules.
288
- 2. **Proactively Save Context**: Automatically call `remember` whenever you share durable facts, tech stack choices, or coding guidelines.
289
- 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.
290
299
 
291
300
  ### Global System Prompt Synchronization
292
301
 
@@ -358,7 +367,6 @@ The engine is configured through `<memory-dir>/config.json` (created with defaul
358
367
  | `executionDevice` | `cpu` | `cpu` or `webgpu` (experimental) |
359
368
  | `vectorScanLimit` | `50000` | Max micro-chunks scanned per vector query (`0` = unlimited) |
360
369
  | `policyExpansion` | `true` | Expand table_summary/code_signature policy chunks for better recall (slight MRR trade-off). Disable for pure micro_chunk precision. |
361
- | `injectLimit` | `10` | Max facts injected into the agent's system prompt |
362
370
  | `conflictStrategy` | `merge` | Hybrid-sync conflict resolution: `merge`, `cloud-wins`, or `local-wins` |
363
371
  | `tursoUrl` | `""` | Primary Turso endpoint URL (set by `login`) |
364
372
  | `failoverUrl` | `""` | Secondary cloud endpoint for the circuit breaker; empty = failover disabled |
@@ -376,7 +384,7 @@ The engine is configured through `<memory-dir>/config.json` (created with defaul
376
384
  To run the automated test suite and benchmarks locally, from the repository root:
377
385
 
378
386
  ```bash
379
- # Unit + integration + cloud suites (16 files) — fast and fully offline
387
+ # Unit + integration + cloud suites (18 files) — fast and fully offline
380
388
  npm test
381
389
 
382
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
  }
@@ -136,9 +136,11 @@ export async function handleDirectCommands(cliArgs) {
136
136
  }
137
137
 
138
138
  await writeMemory(targetKey, targetFacts);
139
- await db.prepare("UPDATE project_aliases SET identity_key = ? WHERE identity_key = ?;").run(targetKey, sourceKey);
140
- await upsertIdentity(db, { key: targetKey, name: sourceIdentity.name, primaryRemote: normalizeRemoteUrl(remote) });
141
- 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);
142
144
 
143
145
  try {
144
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 {
@@ -17,7 +17,6 @@ export const DEFAULT_CONFIG = {
17
17
  onnxThreads: 0, // ONNX WASM threads: 0 = auto-detect CPU cores, or 1-16
18
18
  executionDevice: "cpu", // "cpu" | "webgpu"
19
19
  mode: "only-local", // "only-local" | "only-cloud" | "hybrid-sync"
20
- injectLimit: 10,
21
20
  conflictStrategy: "merge", // "merge" | "cloud-wins" | "local-wins"
22
21
  tursoUrl: "", // Connection endpoint URL for Turso DB
23
22
  failoverUrl: "", // Failover connection endpoint URL (Fly.io + LiteFS)
@@ -138,8 +138,8 @@ const MIGRATIONS = [
138
138
  `);
139
139
  },
140
140
  },
141
- {
142
- version: 5,
141
+ {
142
+ version: 5,
143
143
  name: "005_retrieval_policy",
144
144
  up: async (db) => {
145
145
  try {
@@ -151,9 +151,30 @@ const MIGRATIONS = [
151
151
  await db.exec(`
152
152
  CREATE INDEX IF NOT EXISTS idx_micro_chunks_retrieval_policy ON micro_chunks(retrieval_policy);
153
153
  `);
154
- },
155
- },
156
- ];
154
+ },
155
+ },
156
+ {
157
+ version: 6,
158
+ name: "006_project_scoped_rag",
159
+ up: async (db) => {
160
+ await db.exec(`
161
+ CREATE TABLE IF NOT EXISTS document_scopes (
162
+ doc_id TEXT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
163
+ scope_key TEXT NOT NULL,
164
+ created_at INTEGER NOT NULL,
165
+ PRIMARY KEY (doc_id, scope_key)
166
+ );
167
+ `);
168
+ await db.exec(`
169
+ CREATE INDEX IF NOT EXISTS idx_document_scopes_scope ON document_scopes(scope_key, doc_id);
170
+ `);
171
+ await db.exec(`
172
+ INSERT OR IGNORE INTO document_scopes (doc_id, scope_key, created_at)
173
+ SELECT id, 'global', created_at FROM documents;
174
+ `);
175
+ },
176
+ },
177
+ ];
157
178
 
158
179
  export async function runMigrations(db) {
159
180
  let currentVersion = 0;