@lotargo/memory_plugin 1.6.3 → 1.6.5
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 +64 -1
- package/README.md +28 -20
- package/mcp-server/admin/snapshot.js +93 -26
- package/mcp-server/cli/direct_commands.js +5 -3
- package/mcp-server/cli/handlers/storage_actions.js +3 -1
- package/mcp-server/config/config_manager.js +0 -1
- package/mcp-server/db/migrations.js +26 -5
- package/mcp-server/db/sync_queue.js +118 -44
- package/mcp-server/graph/knowledge_linker.js +126 -19
- package/mcp-server/ingest/exporter.js +38 -9
- package/mcp-server/ingest/pipeline.js +146 -48
- package/mcp-server/memory.js +13 -3
- package/mcp-server/prompt_manager.js +10 -7
- package/mcp-server/retrieval/retriever.js +62 -38
- package/mcp-server/setup.js +18 -12
- package/mcp-server/tools/core/memory_core.js +465 -393
- package/mcp-server/tools/identity_tools.js +25 -6
- package/mcp-server/tools/memory_tools.js +138 -123
- package/mcp-server/tools/rag_tools.js +313 -249
- package/opencode-plugin/index.js +558 -440
- package/package.json +5 -5
- package/skills/using-memory/SKILL.md +152 -117
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,69 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.6.5] - 2026-08-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added explicit `directory` (and `project` alias) parameter support to Notebook/Memory tools (`remember`, `recall`, `get_fact`, `forget`, `update_fact`, `memory_info`, `link_knowledge`) and RAG tools (`ingest_document`, `query_knowledge_base`, `batch_query_knowledge_base`, `manage_knowledge_base`) across both the MCP server and native OpenCode plugin.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Fixed cross-directory workspace routing and isolation so operations targeting external project paths work reliably without directory mismatch or leaking npm process `INIT_CWD`.
|
|
19
|
+
- Fixed `batch_query_knowledge_base` query execution and result formatting in the MCP server.
|
|
20
|
+
|
|
21
|
+
## [1.6.4] - 2026-08-13
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- `memory_info` now reports whether the current Git identity is `Registry: linked`
|
|
26
|
+
or `Registry: unlinked`, enabling agents to register new repositories automatically.
|
|
27
|
+
- RAG documents now have explicit global/project scope associations. The default
|
|
28
|
+
project scope follows the current linked Git identity, while `scope: "all"`
|
|
29
|
+
retrieves only global knowledge plus the current project's knowledge. A source
|
|
30
|
+
shared by several scopes is stored once and associated with each scope.
|
|
31
|
+
- The native OpenCode plugin now exposes `batch_query_knowledge_base`, bringing it
|
|
32
|
+
to all 15 shared tools plus its two OpenCode-only helpers.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- OpenCode auto-injected memory now includes complete fact bodies for both global and
|
|
37
|
+
current-project stores without the previous ten-fact truncation and instructs the
|
|
38
|
+
agent to register an unlinked Git identity. Header-only initialization was removed
|
|
39
|
+
because it loses essential context.
|
|
40
|
+
- The bundled memory skill now defines full-body session recall, strict project
|
|
41
|
+
isolation, agent-resolved conflicts, automatic Git project registration, and
|
|
42
|
+
selective RAG curation for important web findings and current technical
|
|
43
|
+
documentation, including links from project facts to supporting sources.
|
|
44
|
+
- `recall` hides superseded facts by default and exposes history through
|
|
45
|
+
`includeSuperseded: true`. Filtered output keeps physical storage indices stable,
|
|
46
|
+
so a displayed number always targets the same fact in `forget`.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- Re-ingesting an existing path or URL now preserves its document ID, scope
|
|
51
|
+
associations, and fact links while rebuilding chunks, vectors, FTS rows, and
|
|
52
|
+
structural graph edges for changed content.
|
|
53
|
+
- Snapshot export/import and hybrid cloud synchronization now preserve vectors,
|
|
54
|
+
retrieval policies, policy provenance, document scopes, knowledge links, and
|
|
55
|
+
structural graph metadata. The hybrid sync worker also drains tasks queued during
|
|
56
|
+
an active flush instead of leaving them pending.
|
|
57
|
+
- Relinking a Git project now moves its RAG scope associations and fact-to-document
|
|
58
|
+
links together with Notebook facts.
|
|
59
|
+
- Environment-specific setup flags now update only their requested integration.
|
|
60
|
+
Codex setup also synchronizes the skill to both `~/.codex/skills` and the shared
|
|
61
|
+
`~/.agents/skills` location.
|
|
62
|
+
- Shared documents no longer expose fact-link metadata belonging to another project.
|
|
63
|
+
Document deletion now defaults to unlinking only the current project (or global
|
|
64
|
+
outside Git); broader `global` / `all` removal must be requested explicitly.
|
|
65
|
+
- Updating or forgetting a linked Notebook fact now keeps its knowledge-graph
|
|
66
|
+
projection and cloud payload consistent. Line-range graph edges are included in
|
|
67
|
+
per-document cloud sync, and superseding legacy facts without IDs assigns distinct
|
|
68
|
+
IDs to the old and new versions.
|
|
69
|
+
- README tool counts now match the implementations: 15 MCP tools, 17 native
|
|
70
|
+
OpenCode tools, and 17 unique tool names across both surfaces. The OpenCode tool
|
|
71
|
+
list documents all shared and OpenCode-only helpers explicitly.
|
|
72
|
+
|
|
10
73
|
## [1.6.3] - 2026-08-13
|
|
11
74
|
|
|
12
75
|
### Added
|
|
@@ -28,7 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
28
91
|
TOML sections or creating duplicates.
|
|
29
92
|
- Repeated prompt setup now collapses duplicate plugin-owned blocks while preserving
|
|
30
93
|
user-authored AGENTS.md / CLAUDE.md content.
|
|
31
|
-
- README tool counts
|
|
94
|
+
- README tool counts match the v1.6.3 implementations: 15 MCP tools, 16 native
|
|
32
95
|
OpenCode tools, and 17 unique tool names across both surfaces. The OpenCode tool
|
|
33
96
|
list and the MCP-only status of `batch_query_knowledge_base` are documented explicitly.
|
|
34
97
|
|
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
|
-
- **
|
|
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** | **
|
|
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
|
|
176
|
-
|
|
177
|
-
`
|
|
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.
|
|
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
|
|
205
|
-
| `query_knowledge_base` | `query`, `limit`, `instruction`, `generateEmbeddings` | Perform hybrid search (RSF/RRF BM25 + dense
|
|
206
|
-
| `batch_query_knowledge_base` | `queries` (array), `limit`, `instruction`, `generateEmbeddings` | Execute multiple queries in
|
|
207
|
-
| `manage_knowledge_base` | `action`, `docId`, `snapshotPath` | Inspect
|
|
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 (
|
|
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** |
|
|
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**:
|
|
288
|
-
2. **
|
|
289
|
-
3. **
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
140
|
-
await
|
|
141
|
-
await
|
|
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;
|