@framers/agentos 0.2.12 → 0.3.0
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/dist/ingest-router/executors/EntityExtractor.d.ts +23 -0
- package/dist/ingest-router/executors/EntityExtractor.d.ts.map +1 -0
- package/dist/ingest-router/executors/EntityExtractor.js +69 -0
- package/dist/ingest-router/executors/EntityExtractor.js.map +1 -0
- package/dist/ingest-router/executors/EntityLinkingIngestExecutor.d.ts +46 -0
- package/dist/ingest-router/executors/EntityLinkingIngestExecutor.d.ts.map +1 -0
- package/dist/ingest-router/executors/EntityLinkingIngestExecutor.js +45 -0
- package/dist/ingest-router/executors/EntityLinkingIngestExecutor.js.map +1 -0
- package/dist/ingest-router/executors/entity-types.d.ts +55 -0
- package/dist/ingest-router/executors/entity-types.d.ts.map +1 -0
- package/dist/ingest-router/executors/entity-types.js +17 -0
- package/dist/ingest-router/executors/entity-types.js.map +1 -0
- package/dist/ingest-router/executors/index.d.ts +7 -0
- package/dist/ingest-router/executors/index.d.ts.map +1 -1
- package/dist/ingest-router/executors/index.js +6 -0
- package/dist/ingest-router/executors/index.js.map +1 -1
- package/dist/ingest-router/index.d.ts +2 -2
- package/dist/ingest-router/index.d.ts.map +1 -1
- package/dist/ingest-router/index.js +1 -1
- package/dist/ingest-router/index.js.map +1 -1
- package/dist/memory/AgentMemory.js +1 -1
- package/dist/memory/AgentMemory.js.map +1 -1
- package/dist/memory/CognitiveMemoryManager.js +4 -4
- package/dist/memory/CognitiveMemoryManager.js.map +1 -1
- package/dist/memory/archive/IMemoryArchive.d.ts +2 -2
- package/dist/memory/archive/SqlStorageMemoryArchive.d.ts +17 -13
- package/dist/memory/archive/SqlStorageMemoryArchive.d.ts.map +1 -1
- package/dist/memory/archive/SqlStorageMemoryArchive.js +36 -28
- package/dist/memory/archive/SqlStorageMemoryArchive.js.map +1 -1
- package/dist/memory/core/config.d.ts +4 -4
- package/dist/memory/core/config.d.ts.map +1 -1
- package/dist/memory/index.d.ts +3 -3
- package/dist/memory/index.d.ts.map +1 -1
- package/dist/memory/index.js +3 -3
- package/dist/memory/index.js.map +1 -1
- package/dist/memory/io/ChatGptImporter.d.ts +5 -5
- package/dist/memory/io/ChatGptImporter.d.ts.map +1 -1
- package/dist/memory/io/ChatGptImporter.js +9 -7
- package/dist/memory/io/ChatGptImporter.js.map +1 -1
- package/dist/memory/io/CsvImporter.d.ts +4 -4
- package/dist/memory/io/CsvImporter.d.ts.map +1 -1
- package/dist/memory/io/CsvImporter.js +11 -8
- package/dist/memory/io/CsvImporter.js.map +1 -1
- package/dist/memory/io/JsonExporter.d.ts +5 -5
- package/dist/memory/io/JsonExporter.d.ts.map +1 -1
- package/dist/memory/io/JsonExporter.js +13 -12
- package/dist/memory/io/JsonExporter.js.map +1 -1
- package/dist/memory/io/JsonImporter.d.ts +5 -5
- package/dist/memory/io/JsonImporter.d.ts.map +1 -1
- package/dist/memory/io/JsonImporter.js +50 -34
- package/dist/memory/io/JsonImporter.js.map +1 -1
- package/dist/memory/io/MarkdownExporter.d.ts +4 -4
- package/dist/memory/io/MarkdownExporter.d.ts.map +1 -1
- package/dist/memory/io/MarkdownExporter.js +1 -1
- package/dist/memory/io/MarkdownExporter.js.map +1 -1
- package/dist/memory/io/MarkdownImporter.d.ts +6 -6
- package/dist/memory/io/MarkdownImporter.d.ts.map +1 -1
- package/dist/memory/io/MarkdownImporter.js +8 -7
- package/dist/memory/io/MarkdownImporter.js.map +1 -1
- package/dist/memory/io/ObsidianImporter.d.ts +4 -4
- package/dist/memory/io/ObsidianImporter.d.ts.map +1 -1
- package/dist/memory/io/ObsidianImporter.js +15 -10
- package/dist/memory/io/ObsidianImporter.js.map +1 -1
- package/dist/memory/io/SqliteExporter.d.ts +5 -5
- package/dist/memory/io/SqliteExporter.d.ts.map +1 -1
- package/dist/memory/io/SqliteExporter.js +3 -3
- package/dist/memory/io/SqliteExporter.js.map +1 -1
- package/dist/memory/io/SqliteImporter.d.ts +4 -4
- package/dist/memory/io/SqliteImporter.d.ts.map +1 -1
- package/dist/memory/io/SqliteImporter.js +23 -16
- package/dist/memory/io/SqliteImporter.js.map +1 -1
- package/dist/memory/io/facade/Memory.d.ts +58 -10
- package/dist/memory/io/facade/Memory.d.ts.map +1 -1
- package/dist/memory/io/facade/Memory.js +124 -50
- package/dist/memory/io/facade/Memory.js.map +1 -1
- package/dist/memory/io/facade/types.d.ts +1 -1
- package/dist/memory/io/index.d.ts +2 -2
- package/dist/memory/io/index.js +2 -2
- package/dist/memory/io/tools/MemoryAddTool.d.ts +2 -2
- package/dist/memory/io/tools/MemoryAddTool.d.ts.map +1 -1
- package/dist/memory/io/tools/MemoryAddTool.js +2 -2
- package/dist/memory/io/tools/MemoryAddTool.js.map +1 -1
- package/dist/memory/io/tools/MemoryDeleteTool.d.ts +2 -2
- package/dist/memory/io/tools/MemoryDeleteTool.d.ts.map +1 -1
- package/dist/memory/io/tools/MemoryDeleteTool.js +1 -1
- package/dist/memory/io/tools/MemoryDeleteTool.js.map +1 -1
- package/dist/memory/io/tools/MemoryMergeTool.d.ts +2 -2
- package/dist/memory/io/tools/MemoryMergeTool.d.ts.map +1 -1
- package/dist/memory/io/tools/MemoryMergeTool.js +4 -3
- package/dist/memory/io/tools/MemoryMergeTool.js.map +1 -1
- package/dist/memory/io/tools/MemoryReflectTool.d.ts +2 -2
- package/dist/memory/io/tools/MemoryReflectTool.d.ts.map +1 -1
- package/dist/memory/io/tools/MemoryReflectTool.js.map +1 -1
- package/dist/memory/io/tools/MemorySearchTool.d.ts +2 -2
- package/dist/memory/io/tools/MemorySearchTool.d.ts.map +1 -1
- package/dist/memory/io/tools/MemorySearchTool.js.map +1 -1
- package/dist/memory/io/tools/MemoryUpdateTool.d.ts +2 -2
- package/dist/memory/io/tools/MemoryUpdateTool.d.ts.map +1 -1
- package/dist/memory/io/tools/MemoryUpdateTool.js +5 -4
- package/dist/memory/io/tools/MemoryUpdateTool.js.map +1 -1
- package/dist/memory/pipeline/consolidation/ConsolidationLoop.d.ts +3 -3
- package/dist/memory/pipeline/consolidation/ConsolidationLoop.d.ts.map +1 -1
- package/dist/memory/pipeline/consolidation/ConsolidationLoop.js +22 -17
- package/dist/memory/pipeline/consolidation/ConsolidationLoop.js.map +1 -1
- package/dist/memory/retrieval/feedback/RetrievalFeedbackSignal.d.ts +3 -3
- package/dist/memory/retrieval/feedback/RetrievalFeedbackSignal.d.ts.map +1 -1
- package/dist/memory/retrieval/feedback/RetrievalFeedbackSignal.js +15 -12
- package/dist/memory/retrieval/feedback/RetrievalFeedbackSignal.js.map +1 -1
- package/dist/memory/retrieval/graph/index.d.ts +0 -1
- package/dist/memory/retrieval/graph/index.d.ts.map +1 -1
- package/dist/memory/retrieval/graph/index.js +4 -1
- package/dist/memory/retrieval/graph/index.js.map +1 -1
- package/dist/memory/retrieval/store/{SqliteBrain.d.ts → Brain.d.ts} +111 -23
- package/dist/memory/retrieval/store/Brain.d.ts.map +1 -0
- package/dist/memory/retrieval/store/{SqliteBrain.js → Brain.js} +367 -76
- package/dist/memory/retrieval/store/Brain.js.map +1 -0
- package/dist/memory/retrieval/store/HnswSidecar.d.ts +1 -1
- package/dist/memory/retrieval/store/HnswSidecar.js +1 -1
- package/dist/memory/retrieval/store/MemoryStore.d.ts +6 -6
- package/dist/memory/retrieval/store/MemoryStore.d.ts.map +1 -1
- package/dist/memory/retrieval/store/MemoryStore.js +10 -9
- package/dist/memory/retrieval/store/MemoryStore.js.map +1 -1
- package/dist/memory/retrieval/store/{SqliteKnowledgeGraph.d.ts → SqlKnowledgeGraph.d.ts} +12 -12
- package/dist/memory/retrieval/store/SqlKnowledgeGraph.d.ts.map +1 -0
- package/dist/memory/retrieval/store/{SqliteKnowledgeGraph.js → SqlKnowledgeGraph.js} +83 -64
- package/dist/memory/retrieval/store/SqlKnowledgeGraph.js.map +1 -0
- package/dist/memory/retrieval/store/{SqliteMemoryGraph.d.ts → SqlMemoryGraph.d.ts} +11 -11
- package/dist/memory/retrieval/store/SqlMemoryGraph.d.ts.map +1 -0
- package/dist/memory/retrieval/store/{SqliteMemoryGraph.js → SqlMemoryGraph.js} +26 -24
- package/dist/memory/retrieval/store/SqlMemoryGraph.js.map +1 -0
- package/dist/memory/retrieval/store/migrations/v1-to-v2.d.ts +31 -0
- package/dist/memory/retrieval/store/migrations/v1-to-v2.d.ts.map +1 -0
- package/dist/memory/retrieval/store/migrations/v1-to-v2.js +423 -0
- package/dist/memory/retrieval/store/migrations/v1-to-v2.js.map +1 -0
- package/dist/memory-router/backends/EntityRetrievalRanker.d.ts +54 -0
- package/dist/memory-router/backends/EntityRetrievalRanker.d.ts.map +1 -0
- package/dist/memory-router/backends/EntityRetrievalRanker.js +39 -0
- package/dist/memory-router/backends/EntityRetrievalRanker.js.map +1 -0
- package/dist/memory-router/backends/index.d.ts +16 -0
- package/dist/memory-router/backends/index.d.ts.map +1 -0
- package/dist/memory-router/backends/index.js +16 -0
- package/dist/memory-router/backends/index.js.map +1 -0
- package/dist/memory-router/index.d.ts +2 -0
- package/dist/memory-router/index.d.ts.map +1 -1
- package/dist/memory-router/index.js +4 -0
- package/dist/memory-router/index.js.map +1 -1
- package/dist/rag/utils/vectorMath.d.ts +1 -1
- package/dist/rag/utils/vectorMath.js +1 -1
- package/dist/rag/vector-search/HnswIndexSidecar.d.ts +1 -1
- package/dist/rag/vector-search/HnswIndexSidecar.js +1 -1
- package/package.json +2 -2
- package/dist/memory/retrieval/graph/knowledge/SqliteKnowledgeGraph.d.ts +0 -10
- package/dist/memory/retrieval/graph/knowledge/SqliteKnowledgeGraph.d.ts.map +0 -1
- package/dist/memory/retrieval/graph/knowledge/SqliteKnowledgeGraph.js +0 -10
- package/dist/memory/retrieval/graph/knowledge/SqliteKnowledgeGraph.js.map +0 -1
- package/dist/memory/retrieval/store/SqliteBrain.d.ts.map +0 -1
- package/dist/memory/retrieval/store/SqliteBrain.js.map +0 -1
- package/dist/memory/retrieval/store/SqliteKnowledgeGraph.d.ts.map +0 -1
- package/dist/memory/retrieval/store/SqliteKnowledgeGraph.js.map +0 -1
- package/dist/memory/retrieval/store/SqliteMemoryGraph.d.ts.map +0 -1
- package/dist/memory/retrieval/store/SqliteMemoryGraph.js.map +0 -1
|
@@ -26,15 +26,35 @@
|
|
|
26
26
|
* - **JSON columns**: tags, emotions, metadata stored as JSON TEXT for schema flexibility
|
|
27
27
|
* without sacrificing query-ability via SQLite's json_extract().
|
|
28
28
|
*
|
|
29
|
-
* @module memory/store/
|
|
29
|
+
* @module memory/store/Brain
|
|
30
30
|
*/
|
|
31
|
-
import {
|
|
31
|
+
import { promises as fs } from 'node:fs';
|
|
32
|
+
import path from 'node:path';
|
|
33
|
+
import { resolveStorageAdapter, createStorageFeatures, createPostgresAdapter } from '@framers/sql-storage-adapter';
|
|
32
34
|
import { DDL_ARCHIVED_TRACES, DDL_ARCHIVED_TRACES_IDX_AGENT_TIME, DDL_ARCHIVED_TRACES_IDX_REASON, DDL_ARCHIVE_ACCESS_LOG, DDL_ARCHIVE_ACCESS_LOG_IDX, } from '../../archive/SqlStorageMemoryArchive.js';
|
|
35
|
+
import { migrateV1ToV2 } from './migrations/v1-to-v2.js';
|
|
36
|
+
/**
|
|
37
|
+
* Derive a stable brain identifier from the database file path.
|
|
38
|
+
*
|
|
39
|
+
* `:memory:` becomes `'default'`. For real paths, the file basename is used
|
|
40
|
+
* with extensions stripped (e.g. `companion-alice.sqlite` becomes
|
|
41
|
+
* `companion-alice`; `foo.brain.sqlite` becomes `foo.brain`).
|
|
42
|
+
*
|
|
43
|
+
* Used by {@link Brain.open} when the caller does not supply an
|
|
44
|
+
* explicit `brainId`.
|
|
45
|
+
*/
|
|
46
|
+
function deriveBrainIdFromPath(dbPath) {
|
|
47
|
+
if (dbPath === ':memory:')
|
|
48
|
+
return 'default';
|
|
49
|
+
const basename = path.basename(dbPath);
|
|
50
|
+
const lastDot = basename.lastIndexOf('.');
|
|
51
|
+
return lastDot > 0 ? basename.slice(0, lastDot) : basename;
|
|
52
|
+
}
|
|
33
53
|
// ---------------------------------------------------------------------------
|
|
34
54
|
// Constants
|
|
35
55
|
// ---------------------------------------------------------------------------
|
|
36
56
|
/** Current schema version. Increment when breaking schema changes are made. */
|
|
37
|
-
const SCHEMA_VERSION = '
|
|
57
|
+
const SCHEMA_VERSION = '2';
|
|
38
58
|
// ---------------------------------------------------------------------------
|
|
39
59
|
// DDL — full schema
|
|
40
60
|
// ---------------------------------------------------------------------------
|
|
@@ -44,8 +64,10 @@ const SCHEMA_VERSION = '1';
|
|
|
44
64
|
*/
|
|
45
65
|
const DDL_BRAIN_META = `
|
|
46
66
|
CREATE TABLE IF NOT EXISTS brain_meta (
|
|
47
|
-
|
|
48
|
-
|
|
67
|
+
brain_id TEXT NOT NULL,
|
|
68
|
+
key TEXT NOT NULL,
|
|
69
|
+
value TEXT NOT NULL,
|
|
70
|
+
PRIMARY KEY (brain_id, key)
|
|
49
71
|
);
|
|
50
72
|
`;
|
|
51
73
|
/**
|
|
@@ -59,7 +81,8 @@ CREATE TABLE IF NOT EXISTS brain_meta (
|
|
|
59
81
|
*/
|
|
60
82
|
const DDL_MEMORY_TRACES = `
|
|
61
83
|
CREATE TABLE IF NOT EXISTS memory_traces (
|
|
62
|
-
|
|
84
|
+
brain_id TEXT NOT NULL,
|
|
85
|
+
id TEXT NOT NULL,
|
|
63
86
|
type TEXT NOT NULL,
|
|
64
87
|
scope TEXT NOT NULL,
|
|
65
88
|
content TEXT NOT NULL,
|
|
@@ -71,8 +94,14 @@ CREATE TABLE IF NOT EXISTS memory_traces (
|
|
|
71
94
|
tags TEXT NOT NULL DEFAULT '[]',
|
|
72
95
|
emotions TEXT NOT NULL DEFAULT '{}',
|
|
73
96
|
metadata TEXT NOT NULL DEFAULT '{}',
|
|
74
|
-
deleted INTEGER NOT NULL DEFAULT 0
|
|
97
|
+
deleted INTEGER NOT NULL DEFAULT 0,
|
|
98
|
+
PRIMARY KEY (brain_id, id)
|
|
75
99
|
);
|
|
100
|
+
|
|
101
|
+
CREATE INDEX IF NOT EXISTS idx_memory_traces_brain_type
|
|
102
|
+
ON memory_traces (brain_id, type, created_at DESC);
|
|
103
|
+
CREATE INDEX IF NOT EXISTS idx_memory_traces_brain_scope
|
|
104
|
+
ON memory_traces (brain_id, scope);
|
|
76
105
|
`;
|
|
77
106
|
// FTS index DDL is now generated dynamically by features.fts.createIndex()
|
|
78
107
|
// to support both SQLite FTS5 and Postgres tsvector/GIN.
|
|
@@ -86,15 +115,20 @@ CREATE TABLE IF NOT EXISTS memory_traces (
|
|
|
86
115
|
*/
|
|
87
116
|
const DDL_KNOWLEDGE_NODES = `
|
|
88
117
|
CREATE TABLE IF NOT EXISTS knowledge_nodes (
|
|
89
|
-
|
|
118
|
+
brain_id TEXT NOT NULL,
|
|
119
|
+
id TEXT NOT NULL,
|
|
90
120
|
type TEXT NOT NULL,
|
|
91
121
|
label TEXT NOT NULL,
|
|
92
122
|
properties TEXT NOT NULL DEFAULT '{}',
|
|
93
123
|
embedding BLOB,
|
|
94
124
|
confidence REAL NOT NULL DEFAULT 1.0,
|
|
95
125
|
source TEXT NOT NULL DEFAULT '{}',
|
|
96
|
-
created_at INTEGER NOT NULL
|
|
126
|
+
created_at INTEGER NOT NULL,
|
|
127
|
+
PRIMARY KEY (brain_id, id)
|
|
97
128
|
);
|
|
129
|
+
|
|
130
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_brain_type
|
|
131
|
+
ON knowledge_nodes (brain_id, type);
|
|
98
132
|
`;
|
|
99
133
|
/**
|
|
100
134
|
* Knowledge graph edges (typed relationships).
|
|
@@ -105,15 +139,24 @@ CREATE TABLE IF NOT EXISTS knowledge_nodes (
|
|
|
105
139
|
*/
|
|
106
140
|
const DDL_KNOWLEDGE_EDGES = `
|
|
107
141
|
CREATE TABLE IF NOT EXISTS knowledge_edges (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
142
|
+
brain_id TEXT NOT NULL,
|
|
143
|
+
id TEXT NOT NULL,
|
|
144
|
+
source_id TEXT NOT NULL,
|
|
145
|
+
target_id TEXT NOT NULL,
|
|
111
146
|
type TEXT NOT NULL,
|
|
112
147
|
weight REAL NOT NULL DEFAULT 1.0,
|
|
113
148
|
bidirectional INTEGER NOT NULL DEFAULT 0,
|
|
114
149
|
metadata TEXT NOT NULL DEFAULT '{}',
|
|
115
|
-
created_at INTEGER NOT NULL
|
|
150
|
+
created_at INTEGER NOT NULL,
|
|
151
|
+
PRIMARY KEY (brain_id, id),
|
|
152
|
+
FOREIGN KEY (brain_id, source_id) REFERENCES knowledge_nodes(brain_id, id),
|
|
153
|
+
FOREIGN KEY (brain_id, target_id) REFERENCES knowledge_nodes(brain_id, id)
|
|
116
154
|
);
|
|
155
|
+
|
|
156
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_edges_brain_source
|
|
157
|
+
ON knowledge_edges (brain_id, source_id);
|
|
158
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_edges_brain_target
|
|
159
|
+
ON knowledge_edges (brain_id, target_id);
|
|
117
160
|
`;
|
|
118
161
|
/**
|
|
119
162
|
* Ingested document registry.
|
|
@@ -125,14 +168,16 @@ CREATE TABLE IF NOT EXISTS knowledge_edges (
|
|
|
125
168
|
*/
|
|
126
169
|
const DDL_DOCUMENTS = `
|
|
127
170
|
CREATE TABLE IF NOT EXISTS documents (
|
|
128
|
-
|
|
171
|
+
brain_id TEXT NOT NULL,
|
|
172
|
+
id TEXT NOT NULL,
|
|
129
173
|
path TEXT NOT NULL,
|
|
130
174
|
format TEXT NOT NULL,
|
|
131
175
|
title TEXT,
|
|
132
176
|
content_hash TEXT NOT NULL,
|
|
133
177
|
chunk_count INTEGER NOT NULL DEFAULT 0,
|
|
134
178
|
metadata TEXT NOT NULL DEFAULT '{}',
|
|
135
|
-
ingested_at INTEGER NOT NULL
|
|
179
|
+
ingested_at INTEGER NOT NULL,
|
|
180
|
+
PRIMARY KEY (brain_id, id)
|
|
136
181
|
);
|
|
137
182
|
`;
|
|
138
183
|
/**
|
|
@@ -144,14 +189,21 @@ CREATE TABLE IF NOT EXISTS documents (
|
|
|
144
189
|
*/
|
|
145
190
|
const DDL_DOCUMENT_CHUNKS = `
|
|
146
191
|
CREATE TABLE IF NOT EXISTS document_chunks (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
192
|
+
brain_id TEXT NOT NULL,
|
|
193
|
+
id TEXT NOT NULL,
|
|
194
|
+
document_id TEXT NOT NULL,
|
|
195
|
+
trace_id TEXT,
|
|
150
196
|
content TEXT NOT NULL,
|
|
151
197
|
chunk_index INTEGER NOT NULL,
|
|
152
198
|
page_number INTEGER,
|
|
153
|
-
embedding BLOB
|
|
199
|
+
embedding BLOB,
|
|
200
|
+
PRIMARY KEY (brain_id, id),
|
|
201
|
+
FOREIGN KEY (brain_id, document_id) REFERENCES documents(brain_id, id),
|
|
202
|
+
FOREIGN KEY (brain_id, trace_id) REFERENCES memory_traces(brain_id, id)
|
|
154
203
|
);
|
|
204
|
+
|
|
205
|
+
CREATE INDEX IF NOT EXISTS idx_document_chunks_brain_document
|
|
206
|
+
ON document_chunks (brain_id, document_id, chunk_index);
|
|
155
207
|
`;
|
|
156
208
|
/**
|
|
157
209
|
* Document image table.
|
|
@@ -161,14 +213,18 @@ CREATE TABLE IF NOT EXISTS document_chunks (
|
|
|
161
213
|
*/
|
|
162
214
|
const DDL_DOCUMENT_IMAGES = `
|
|
163
215
|
CREATE TABLE IF NOT EXISTS document_images (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
216
|
+
brain_id TEXT NOT NULL,
|
|
217
|
+
id TEXT NOT NULL,
|
|
218
|
+
document_id TEXT NOT NULL,
|
|
219
|
+
chunk_id TEXT,
|
|
167
220
|
data BLOB NOT NULL,
|
|
168
221
|
mime_type TEXT NOT NULL,
|
|
169
222
|
caption TEXT,
|
|
170
223
|
page_number INTEGER,
|
|
171
|
-
embedding BLOB
|
|
224
|
+
embedding BLOB,
|
|
225
|
+
PRIMARY KEY (brain_id, id),
|
|
226
|
+
FOREIGN KEY (brain_id, document_id) REFERENCES documents(brain_id, id),
|
|
227
|
+
FOREIGN KEY (brain_id, chunk_id) REFERENCES document_chunks(brain_id, id)
|
|
172
228
|
);
|
|
173
229
|
`;
|
|
174
230
|
/**
|
|
@@ -181,6 +237,7 @@ CREATE TABLE IF NOT EXISTS document_images (
|
|
|
181
237
|
const DDL_CONSOLIDATION_LOG = `
|
|
182
238
|
CREATE TABLE IF NOT EXISTS consolidation_log (
|
|
183
239
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
240
|
+
brain_id TEXT NOT NULL,
|
|
184
241
|
ran_at INTEGER NOT NULL,
|
|
185
242
|
pruned INTEGER NOT NULL DEFAULT 0,
|
|
186
243
|
merged INTEGER NOT NULL DEFAULT 0,
|
|
@@ -188,6 +245,9 @@ CREATE TABLE IF NOT EXISTS consolidation_log (
|
|
|
188
245
|
compacted INTEGER NOT NULL DEFAULT 0,
|
|
189
246
|
duration_ms INTEGER NOT NULL DEFAULT 0
|
|
190
247
|
);
|
|
248
|
+
|
|
249
|
+
CREATE INDEX IF NOT EXISTS idx_consolidation_log_brain_time
|
|
250
|
+
ON consolidation_log (brain_id, ran_at DESC);
|
|
191
251
|
`;
|
|
192
252
|
/**
|
|
193
253
|
* Retrieval feedback signals.
|
|
@@ -201,11 +261,16 @@ CREATE TABLE IF NOT EXISTS consolidation_log (
|
|
|
201
261
|
const DDL_RETRIEVAL_FEEDBACK = `
|
|
202
262
|
CREATE TABLE IF NOT EXISTS retrieval_feedback (
|
|
203
263
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
204
|
-
|
|
264
|
+
brain_id TEXT NOT NULL,
|
|
265
|
+
trace_id TEXT NOT NULL,
|
|
205
266
|
signal TEXT NOT NULL,
|
|
206
267
|
query TEXT,
|
|
207
|
-
created_at INTEGER NOT NULL
|
|
268
|
+
created_at INTEGER NOT NULL,
|
|
269
|
+
FOREIGN KEY (brain_id, trace_id) REFERENCES memory_traces(brain_id, id)
|
|
208
270
|
);
|
|
271
|
+
|
|
272
|
+
CREATE INDEX IF NOT EXISTS idx_retrieval_feedback_brain_trace
|
|
273
|
+
ON retrieval_feedback (brain_id, trace_id, created_at DESC);
|
|
209
274
|
`;
|
|
210
275
|
/**
|
|
211
276
|
* Conversation sessions.
|
|
@@ -215,11 +280,13 @@ CREATE TABLE IF NOT EXISTS retrieval_feedback (
|
|
|
215
280
|
*/
|
|
216
281
|
const DDL_CONVERSATIONS = `
|
|
217
282
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
218
|
-
|
|
283
|
+
brain_id TEXT NOT NULL,
|
|
284
|
+
id TEXT NOT NULL,
|
|
219
285
|
title TEXT,
|
|
220
286
|
created_at INTEGER NOT NULL,
|
|
221
287
|
updated_at INTEGER NOT NULL,
|
|
222
|
-
metadata TEXT NOT NULL DEFAULT '{}'
|
|
288
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
289
|
+
PRIMARY KEY (brain_id, id)
|
|
223
290
|
);
|
|
224
291
|
`;
|
|
225
292
|
/**
|
|
@@ -230,13 +297,19 @@ CREATE TABLE IF NOT EXISTS conversations (
|
|
|
230
297
|
*/
|
|
231
298
|
const DDL_MESSAGES = `
|
|
232
299
|
CREATE TABLE IF NOT EXISTS messages (
|
|
233
|
-
|
|
234
|
-
|
|
300
|
+
brain_id TEXT NOT NULL,
|
|
301
|
+
id TEXT NOT NULL,
|
|
302
|
+
conversation_id TEXT NOT NULL,
|
|
235
303
|
role TEXT NOT NULL,
|
|
236
304
|
content TEXT NOT NULL,
|
|
237
305
|
created_at INTEGER NOT NULL,
|
|
238
|
-
metadata TEXT NOT NULL DEFAULT '{}'
|
|
306
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
307
|
+
PRIMARY KEY (brain_id, id),
|
|
308
|
+
FOREIGN KEY (brain_id, conversation_id) REFERENCES conversations(brain_id, id)
|
|
239
309
|
);
|
|
310
|
+
|
|
311
|
+
CREATE INDEX IF NOT EXISTS idx_messages_brain_conversation
|
|
312
|
+
ON messages (brain_id, conversation_id, created_at);
|
|
240
313
|
`;
|
|
241
314
|
/**
|
|
242
315
|
* Prospective memory items table.
|
|
@@ -252,7 +325,8 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
252
325
|
*/
|
|
253
326
|
const DDL_PROSPECTIVE_ITEMS = `
|
|
254
327
|
CREATE TABLE IF NOT EXISTS prospective_items (
|
|
255
|
-
|
|
328
|
+
brain_id TEXT NOT NULL,
|
|
329
|
+
id TEXT NOT NULL,
|
|
256
330
|
content TEXT NOT NULL,
|
|
257
331
|
trigger_type TEXT NOT NULL,
|
|
258
332
|
trigger_at INTEGER,
|
|
@@ -264,11 +338,12 @@ CREATE TABLE IF NOT EXISTS prospective_items (
|
|
|
264
338
|
triggered INTEGER NOT NULL DEFAULT 0,
|
|
265
339
|
recurring INTEGER NOT NULL DEFAULT 0,
|
|
266
340
|
source_trace_id TEXT,
|
|
267
|
-
created_at INTEGER NOT NULL
|
|
341
|
+
created_at INTEGER NOT NULL,
|
|
342
|
+
PRIMARY KEY (brain_id, id)
|
|
268
343
|
);
|
|
269
344
|
`;
|
|
270
345
|
// ---------------------------------------------------------------------------
|
|
271
|
-
//
|
|
346
|
+
// Brain
|
|
272
347
|
// ---------------------------------------------------------------------------
|
|
273
348
|
/**
|
|
274
349
|
* Unified cross-platform connection manager for a single agent's persistent brain.
|
|
@@ -279,7 +354,7 @@ CREATE TABLE IF NOT EXISTS prospective_items (
|
|
|
279
354
|
*
|
|
280
355
|
* **Usage:**
|
|
281
356
|
* ```ts
|
|
282
|
-
* const brain = await
|
|
357
|
+
* const brain = await Brain.open('/path/to/agent/brain.sqlite');
|
|
283
358
|
*
|
|
284
359
|
* // Async query API for subsystems
|
|
285
360
|
* const row = await brain.get<{ value: string }>('SELECT value FROM brain_meta WHERE key = ?', ['schema_version']);
|
|
@@ -292,74 +367,119 @@ CREATE TABLE IF NOT EXISTS prospective_items (
|
|
|
292
367
|
* ```
|
|
293
368
|
*
|
|
294
369
|
* Subsystems (KnowledgeGraph, MemoryGraph, ConsolidationLoop, etc.)
|
|
295
|
-
* receive the `
|
|
370
|
+
* receive the `Brain` instance and call its async proxy methods
|
|
296
371
|
* (`run`, `get`, `all`, `exec`, `transaction`) for all database operations.
|
|
297
372
|
*/
|
|
298
|
-
export class
|
|
373
|
+
export class Brain {
|
|
299
374
|
// ---------------------------------------------------------------------------
|
|
300
|
-
// Constructor (private — use
|
|
375
|
+
// Constructor (private — use Brain.open())
|
|
301
376
|
// ---------------------------------------------------------------------------
|
|
302
377
|
/**
|
|
303
|
-
* Private constructor — use `
|
|
378
|
+
* Private constructor — use `Brain.open(dbPath)` instead.
|
|
304
379
|
*
|
|
305
380
|
* @param adapter - A fully initialised StorageAdapter instance.
|
|
306
381
|
* @param features - Platform-aware feature bundle.
|
|
382
|
+
* @param brainId - Brain identifier used to scope multi-tenant queries.
|
|
307
383
|
*/
|
|
308
|
-
constructor(adapter, features) {
|
|
384
|
+
constructor(adapter, features, brainId) {
|
|
309
385
|
this._adapter = adapter;
|
|
310
386
|
this._features = features;
|
|
387
|
+
this._brainId = brainId;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Brain identifier scoping every query through this Brain instance.
|
|
391
|
+
* Subsystems (KnowledgeGraph, MemoryGraph, ConsolidationLoop) read this
|
|
392
|
+
* to inject `brain_id` into their own SQL.
|
|
393
|
+
*/
|
|
394
|
+
get brainId() {
|
|
395
|
+
return this._brainId;
|
|
311
396
|
}
|
|
312
397
|
// ---------------------------------------------------------------------------
|
|
313
|
-
// Async
|
|
398
|
+
// Async factories (three named entry points)
|
|
314
399
|
// ---------------------------------------------------------------------------
|
|
315
400
|
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
* Async factory that replaces the previous synchronous constructor.
|
|
319
|
-
*
|
|
320
|
-
* Initialization sequence:
|
|
321
|
-
* 1. Resolve the best available storage adapter for the current runtime.
|
|
322
|
-
* 2. Enable WAL journal mode for concurrent read access (when supported).
|
|
323
|
-
* 3. Enable foreign key enforcement (OFF by default in SQLite).
|
|
324
|
-
* 4. Execute the full DDL schema (all `CREATE TABLE IF NOT EXISTS`).
|
|
325
|
-
* 5. Create the FTS5 virtual table for full-text memory search.
|
|
326
|
-
* 6. Seed `brain_meta` with `schema_version` and `created_at` if absent.
|
|
401
|
+
* Open a Brain backed by SQLite. Tries adapters in order:
|
|
402
|
+
* better-sqlite3 (Node native) -> sql.js (WASM) -> indexeddb (browser).
|
|
327
403
|
*
|
|
328
|
-
* @param
|
|
329
|
-
*
|
|
330
|
-
*
|
|
404
|
+
* @param path - File path. Use `:memory:` for in-process testing.
|
|
405
|
+
* @param opts.brainId - Optional explicit brainId; defaults to file basename
|
|
406
|
+
* (or `'default'` for `:memory:`).
|
|
407
|
+
* @param opts.priority - Override the default adapter priority.
|
|
408
|
+
* @returns A fully initialised `Brain` instance with the v2 schema.
|
|
331
409
|
*/
|
|
332
|
-
static async
|
|
410
|
+
static async openSqlite(path, opts = {}) {
|
|
333
411
|
const adapter = await resolveStorageAdapter({
|
|
334
|
-
filePath:
|
|
335
|
-
priority: ['better-sqlite3', 'sqljs', 'indexeddb'],
|
|
412
|
+
filePath: path,
|
|
413
|
+
priority: opts.priority ?? ['better-sqlite3', 'sqljs', 'indexeddb'],
|
|
336
414
|
quiet: true,
|
|
337
415
|
});
|
|
416
|
+
const brainId = opts.brainId ?? deriveBrainIdFromPath(path);
|
|
417
|
+
return Brain._initialize(adapter, brainId);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Open a Brain backed by PostgreSQL. Requires the `pg` npm package and
|
|
421
|
+
* a reachable Postgres instance.
|
|
422
|
+
*
|
|
423
|
+
* @param connectionString - Standard Postgres connection URL.
|
|
424
|
+
* @param opts.brainId - REQUIRED. Used to scope every query so multiple
|
|
425
|
+
* brains can share one Postgres database without leaking rows.
|
|
426
|
+
* @param opts.poolSize - pg connection pool size. Defaults to 10.
|
|
427
|
+
*/
|
|
428
|
+
static async openPostgres(connectionString, opts) {
|
|
429
|
+
if (!opts.brainId) {
|
|
430
|
+
throw new Error('Brain.openPostgres: opts.brainId is required (Postgres mode is multi-tenant)');
|
|
431
|
+
}
|
|
432
|
+
// Use createPostgresAdapter directly so we can pass pool size; the
|
|
433
|
+
// resolveStorageAdapter facade only forwards `connectionString`.
|
|
434
|
+
const adapter = await createPostgresAdapter({
|
|
435
|
+
connectionString,
|
|
436
|
+
max: opts.poolSize ?? 10,
|
|
437
|
+
});
|
|
438
|
+
await adapter.open();
|
|
439
|
+
return Brain._initialize(adapter, opts.brainId);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Open a Brain with a pre-resolved StorageAdapter. Use when sharing an
|
|
443
|
+
* adapter across subsystems (e.g., wilds-ai foundation pool + brain) or
|
|
444
|
+
* when the consumer needs full control over adapter resolution.
|
|
445
|
+
*
|
|
446
|
+
* @param adapter - Pre-built StorageAdapter instance.
|
|
447
|
+
* @param opts.brainId - Required for postgres-kind adapters; optional for
|
|
448
|
+
* sqlite-kind adapters (defaults to `'default'`).
|
|
449
|
+
*/
|
|
450
|
+
static async openWithAdapter(adapter, opts = {}) {
|
|
451
|
+
const isPostgres = adapter.kind.includes('postgres');
|
|
452
|
+
if (isPostgres && !opts.brainId) {
|
|
453
|
+
throw new Error('Brain.openWithAdapter: opts.brainId is required for postgres-kind adapters');
|
|
454
|
+
}
|
|
455
|
+
const brainId = opts.brainId ?? 'default';
|
|
456
|
+
return Brain._initialize(adapter, brainId);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Internal common initialization path used by all three factories.
|
|
460
|
+
*
|
|
461
|
+
* Sequence:
|
|
462
|
+
* 1. Build platform-aware feature bundle.
|
|
463
|
+
* 2. Set WAL mode (dialect.pragma returns null on Postgres).
|
|
464
|
+
* 3. Enable foreign key enforcement (dialect.pragma returns null on Postgres).
|
|
465
|
+
* 4. Auto-migrate v1 schemas to v2 (idempotent; no-op for fresh DBs and v2).
|
|
466
|
+
* 5. Apply full DDL via _initSchema().
|
|
467
|
+
* 6. Seed brain_meta defaults.
|
|
468
|
+
*/
|
|
469
|
+
static async _initialize(adapter, brainId) {
|
|
338
470
|
const features = createStorageFeatures(adapter);
|
|
339
|
-
const brain = new
|
|
340
|
-
// Step 1: WAL mode — dialect returns null for non-SQLite adapters.
|
|
471
|
+
const brain = new Brain(adapter, features, brainId);
|
|
341
472
|
const walPragma = features.dialect.pragma('journal_mode', 'WAL');
|
|
342
473
|
if (walPragma)
|
|
343
474
|
await adapter.exec(walPragma);
|
|
344
|
-
// Step 2: Foreign key enforcement — dialect returns null for Postgres (enforced by default).
|
|
345
475
|
const fkPragma = features.dialect.pragma('foreign_keys', 'ON');
|
|
346
476
|
if (fkPragma)
|
|
347
477
|
await adapter.exec(fkPragma);
|
|
348
|
-
|
|
478
|
+
await migrateV1ToV2(adapter, features, brainId);
|
|
349
479
|
await brain._initSchema();
|
|
350
|
-
// Step 4: Seed brain_meta defaults if this is a fresh database.
|
|
351
480
|
await brain._seedMeta();
|
|
352
481
|
return brain;
|
|
353
482
|
}
|
|
354
|
-
/**
|
|
355
|
-
* Alias for `open()` — matches the naming convention used by WildsMemoryFacade.
|
|
356
|
-
*
|
|
357
|
-
* @param dbPath - Absolute path to the `.sqlite` file.
|
|
358
|
-
* @returns A fully initialised `SqliteBrain` instance.
|
|
359
|
-
*/
|
|
360
|
-
static async create(dbPath) {
|
|
361
|
-
return SqliteBrain.open(dbPath);
|
|
362
|
-
}
|
|
363
483
|
// ---------------------------------------------------------------------------
|
|
364
484
|
// Async proxy methods (for consumer subsystems)
|
|
365
485
|
// ---------------------------------------------------------------------------
|
|
@@ -489,8 +609,8 @@ export class SqliteBrain {
|
|
|
489
609
|
// INSERT OR IGNORE is idempotent — no transaction needed.
|
|
490
610
|
// Avoids sql.js "cannot rollback" errors when DDL from _initSchema()
|
|
491
611
|
// leaves the connection in an implicit-commit state.
|
|
492
|
-
await this._adapter.run(dialect.insertOrIgnore('brain_meta', ['key', 'value'], ['?', '?']), ['schema_version', SCHEMA_VERSION]);
|
|
493
|
-
await this._adapter.run(dialect.insertOrIgnore('brain_meta', ['key', 'value'], ['?', '?']), ['created_at', Date.now().toString()]);
|
|
612
|
+
await this._adapter.run(dialect.insertOrIgnore('brain_meta', ['brain_id', 'key', 'value'], ['?', '?', '?']), [this._brainId, 'schema_version', SCHEMA_VERSION]);
|
|
613
|
+
await this._adapter.run(dialect.insertOrIgnore('brain_meta', ['brain_id', 'key', 'value'], ['?', '?', '?']), [this._brainId, 'created_at', Date.now().toString()]);
|
|
494
614
|
}
|
|
495
615
|
// ---------------------------------------------------------------------------
|
|
496
616
|
// Public API
|
|
@@ -502,7 +622,7 @@ export class SqliteBrain {
|
|
|
502
622
|
* @returns The stored string value, or `undefined` if the key does not exist.
|
|
503
623
|
*/
|
|
504
624
|
async getMeta(key) {
|
|
505
|
-
const row = await this._adapter.get('SELECT value FROM brain_meta WHERE key = ?', [key]);
|
|
625
|
+
const row = await this._adapter.get('SELECT value FROM brain_meta WHERE brain_id = ? AND key = ?', [this._brainId, key]);
|
|
506
626
|
return row?.value;
|
|
507
627
|
}
|
|
508
628
|
/**
|
|
@@ -515,7 +635,7 @@ export class SqliteBrain {
|
|
|
515
635
|
* @param value - The string value to store.
|
|
516
636
|
*/
|
|
517
637
|
async setMeta(key, value) {
|
|
518
|
-
await this._adapter.run(this._features.dialect.insertOrReplace('brain_meta', ['key', 'value'], ['?', '?'], 'key'), [key, value]);
|
|
638
|
+
await this._adapter.run(this._features.dialect.insertOrReplace('brain_meta', ['brain_id', 'key', 'value'], ['?', '?', '?'], 'brain_id, key'), [this._brainId, key, value]);
|
|
519
639
|
}
|
|
520
640
|
/**
|
|
521
641
|
* Check whether a given embedding dimension is compatible with this brain.
|
|
@@ -539,6 +659,131 @@ export class SqliteBrain {
|
|
|
539
659
|
}
|
|
540
660
|
return parseInt(stored, 10) === dimensions;
|
|
541
661
|
}
|
|
662
|
+
// ---------------------------------------------------------------------------
|
|
663
|
+
// Portable artifact: export to / import from a SQLite snapshot
|
|
664
|
+
// ---------------------------------------------------------------------------
|
|
665
|
+
/**
|
|
666
|
+
* Materialize this brain to a portable SQLite file at `targetPath`.
|
|
667
|
+
*
|
|
668
|
+
* Source can be any backend (SQLite, Postgres, Capacitor, etc.); output
|
|
669
|
+
* is always a fresh SQLite file. Used by `.wildsoul`-style export and
|
|
670
|
+
* other portability flows.
|
|
671
|
+
*
|
|
672
|
+
* Refuses to overwrite an existing file at `targetPath` so callers do
|
|
673
|
+
* not silently lose data.
|
|
674
|
+
*
|
|
675
|
+
* Forking semantics: rows are emitted with the source brainId. Importing
|
|
676
|
+
* the resulting file under a different brainId produces a fork.
|
|
677
|
+
*
|
|
678
|
+
* @param targetPath - Destination file path. File must not exist.
|
|
679
|
+
* @returns Bytes written to the destination file.
|
|
680
|
+
*/
|
|
681
|
+
async exportToSqlite(targetPath) {
|
|
682
|
+
// Refuse to overwrite an existing file.
|
|
683
|
+
try {
|
|
684
|
+
await fs.access(targetPath);
|
|
685
|
+
throw new Error(`Brain.exportToSqlite: target already exists: ${targetPath}`);
|
|
686
|
+
}
|
|
687
|
+
catch (err) {
|
|
688
|
+
const code = err.code;
|
|
689
|
+
if (code !== 'ENOENT') {
|
|
690
|
+
// Re-throw the "already exists" error and any other access error
|
|
691
|
+
// that isn't a missing-file response.
|
|
692
|
+
throw err;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
// Open a fresh SQLite Brain at the target path. We import under the
|
|
696
|
+
// source brainId so the export file is identifiable as belonging to
|
|
697
|
+
// this brain even if the receiving Brain has a different id.
|
|
698
|
+
const target = await Brain.openSqlite(targetPath, { brainId: this._brainId });
|
|
699
|
+
try {
|
|
700
|
+
for (const table of PORTABLE_TABLES) {
|
|
701
|
+
const rows = await this.all(`SELECT * FROM ${table} WHERE brain_id = ?`, [this._brainId]);
|
|
702
|
+
if (rows.length === 0)
|
|
703
|
+
continue;
|
|
704
|
+
// Upsert so source rows override the brain_meta defaults
|
|
705
|
+
// (schema_version, created_at) seeded during target initialisation.
|
|
706
|
+
await this._bulkCopy(target, table, rows, this._brainId, { upsert: true });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
finally {
|
|
710
|
+
await target.close();
|
|
711
|
+
}
|
|
712
|
+
const stat = await fs.stat(targetPath);
|
|
713
|
+
return { bytesWritten: stat.size };
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Load a portable SQLite file into this Brain's adapter.
|
|
717
|
+
*
|
|
718
|
+
* Forking semantics: rows from the source file are written under the
|
|
719
|
+
* RECEIVING brain's `brainId`, not the brainId stored in the source
|
|
720
|
+
* file. This means importing an `alice` snapshot into a Brain opened
|
|
721
|
+
* with `brainId: 'alice-fork'` produces a fork with no shared identity.
|
|
722
|
+
*
|
|
723
|
+
* @param sourcePath - Source SQLite file path (typically produced by
|
|
724
|
+
* `Brain.exportToSqlite`).
|
|
725
|
+
* @param opts.strategy - `'merge'` (default) upserts on PK collision;
|
|
726
|
+
* `'replace'` wipes all rows for the receiving `brainId` first.
|
|
727
|
+
* @returns Counts of rows imported per table.
|
|
728
|
+
*/
|
|
729
|
+
async importFromSqlite(sourcePath, opts = {}) {
|
|
730
|
+
const strategy = opts.strategy ?? 'merge';
|
|
731
|
+
const source = await Brain.openSqlite(sourcePath);
|
|
732
|
+
const tablesImported = {};
|
|
733
|
+
try {
|
|
734
|
+
if (strategy === 'replace') {
|
|
735
|
+
// Wipe existing rows for the receiving brainId in every portable table.
|
|
736
|
+
// Order matters: child tables before parent tables to satisfy FKs.
|
|
737
|
+
for (const table of [...PORTABLE_TABLES].reverse()) {
|
|
738
|
+
await this.run(`DELETE FROM ${table} WHERE brain_id = ?`, [this._brainId]);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
for (const table of PORTABLE_TABLES) {
|
|
742
|
+
// Read every row in the source file regardless of its stored brainId
|
|
743
|
+
// so we capture the full snapshot for re-insertion under our brainId.
|
|
744
|
+
const rows = await source.all(`SELECT * FROM ${table}`);
|
|
745
|
+
tablesImported[table] = rows.length;
|
|
746
|
+
if (rows.length === 0)
|
|
747
|
+
continue;
|
|
748
|
+
// Always use upsert to gracefully handle the brain_meta rows seeded
|
|
749
|
+
// by `_seedMeta` during the receiving Brain's initialization (which
|
|
750
|
+
// would otherwise collide with the source's schema_version/created_at).
|
|
751
|
+
await this._bulkCopy(this, table, rows, this._brainId, { upsert: true });
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
finally {
|
|
755
|
+
await source.close();
|
|
756
|
+
}
|
|
757
|
+
return { tablesImported };
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Internal helper: bulk-insert `rows` into `target.<table>`, rewriting
|
|
761
|
+
* `brain_id` on each row to `targetBrainId`. When `opts.upsert` is true,
|
|
762
|
+
* uses `dialect.insertOrReplace` so PK collisions overwrite (idempotent).
|
|
763
|
+
*/
|
|
764
|
+
async _bulkCopy(target, table, rows, targetBrainId, opts = {}) {
|
|
765
|
+
if (rows.length === 0)
|
|
766
|
+
return;
|
|
767
|
+
const columns = Object.keys(rows[0]);
|
|
768
|
+
const placeholders = columns.map(() => '?').join(', ');
|
|
769
|
+
const colList = columns.join(', ');
|
|
770
|
+
const stmt = opts.upsert
|
|
771
|
+
? target._features.dialect.insertOrReplace(table, columns, columns.map(() => '?'), PORTABLE_TABLE_PRIMARY_KEYS[table] ?? 'brain_id, id')
|
|
772
|
+
: `INSERT INTO ${table} (${colList}) VALUES (${placeholders})`;
|
|
773
|
+
// Single transaction per table for bulk-insert performance + atomicity.
|
|
774
|
+
await target._adapter.exec('BEGIN');
|
|
775
|
+
try {
|
|
776
|
+
for (const row of rows) {
|
|
777
|
+
const values = columns.map((c) => c === 'brain_id' ? targetBrainId : row[c]);
|
|
778
|
+
await target._adapter.run(stmt, values);
|
|
779
|
+
}
|
|
780
|
+
await target._adapter.exec('COMMIT');
|
|
781
|
+
}
|
|
782
|
+
catch (err) {
|
|
783
|
+
await target._adapter.exec('ROLLBACK');
|
|
784
|
+
throw err;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
542
787
|
/**
|
|
543
788
|
* Close the database connection.
|
|
544
789
|
*
|
|
@@ -550,4 +795,50 @@ export class SqliteBrain {
|
|
|
550
795
|
await this._adapter.close();
|
|
551
796
|
}
|
|
552
797
|
}
|
|
553
|
-
|
|
798
|
+
// ---------------------------------------------------------------------------
|
|
799
|
+
// Portable-artifact constants
|
|
800
|
+
// ---------------------------------------------------------------------------
|
|
801
|
+
/**
|
|
802
|
+
* Tables exported and imported by `Brain.exportToSqlite` / `importFromSqlite`.
|
|
803
|
+
* Order matters for import: parents before children to satisfy FKs.
|
|
804
|
+
*/
|
|
805
|
+
const PORTABLE_TABLES = [
|
|
806
|
+
'brain_meta',
|
|
807
|
+
'memory_traces',
|
|
808
|
+
'knowledge_nodes',
|
|
809
|
+
'knowledge_edges',
|
|
810
|
+
'documents',
|
|
811
|
+
'document_chunks',
|
|
812
|
+
'document_images',
|
|
813
|
+
'consolidation_log',
|
|
814
|
+
'retrieval_feedback',
|
|
815
|
+
'conversations',
|
|
816
|
+
'messages',
|
|
817
|
+
'prospective_items',
|
|
818
|
+
'archived_traces',
|
|
819
|
+
'archive_access_log',
|
|
820
|
+
];
|
|
821
|
+
/**
|
|
822
|
+
* Composite primary key columns for each portable table, used by
|
|
823
|
+
* `dialect.insertOrReplace` as the conflict target during merge import.
|
|
824
|
+
*
|
|
825
|
+
* Tables with `INTEGER PRIMARY KEY AUTOINCREMENT` (consolidation_log,
|
|
826
|
+
* retrieval_feedback) use `id` alone since their PK is system-generated.
|
|
827
|
+
*/
|
|
828
|
+
const PORTABLE_TABLE_PRIMARY_KEYS = {
|
|
829
|
+
brain_meta: 'brain_id, key',
|
|
830
|
+
memory_traces: 'brain_id, id',
|
|
831
|
+
knowledge_nodes: 'brain_id, id',
|
|
832
|
+
knowledge_edges: 'brain_id, id',
|
|
833
|
+
documents: 'brain_id, id',
|
|
834
|
+
document_chunks: 'brain_id, id',
|
|
835
|
+
document_images: 'brain_id, id',
|
|
836
|
+
consolidation_log: 'id',
|
|
837
|
+
retrieval_feedback: 'id',
|
|
838
|
+
conversations: 'brain_id, id',
|
|
839
|
+
messages: 'brain_id, id',
|
|
840
|
+
prospective_items: 'brain_id, id',
|
|
841
|
+
archived_traces: 'brain_id, trace_id',
|
|
842
|
+
archive_access_log: 'brain_id, trace_id, accessed_at',
|
|
843
|
+
};
|
|
844
|
+
//# sourceMappingURL=Brain.js.map
|