@framers/agentos 0.1.121 → 0.1.123
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/api/TextToolCallParser.d.ts +61 -0
- package/dist/api/TextToolCallParser.d.ts.map +1 -0
- package/dist/api/TextToolCallParser.js +137 -0
- package/dist/api/TextToolCallParser.js.map +1 -0
- package/dist/api/agent.d.ts +7 -0
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +1 -0
- package/dist/api/agent.js.map +1 -1
- package/dist/api/generateText.d.ts +105 -0
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +191 -2
- package/dist/api/generateText.js.map +1 -1
- package/dist/cognitive_substrate/GMI.d.ts.map +1 -1
- package/dist/cognitive_substrate/GMI.js +27 -1
- package/dist/cognitive_substrate/GMI.js.map +1 -1
- package/dist/cognitive_substrate/IGMI.d.ts +5 -0
- package/dist/cognitive_substrate/IGMI.d.ts.map +1 -1
- package/dist/cognitive_substrate/IGMI.js.map +1 -1
- package/dist/emergent/AdaptPersonalityTool.d.ts +142 -0
- package/dist/emergent/AdaptPersonalityTool.d.ts.map +1 -0
- package/dist/emergent/AdaptPersonalityTool.js +198 -0
- package/dist/emergent/AdaptPersonalityTool.js.map +1 -0
- package/dist/emergent/CreateWorkflowTool.d.ts +182 -0
- package/dist/emergent/CreateWorkflowTool.d.ts.map +1 -0
- package/dist/emergent/CreateWorkflowTool.js +330 -0
- package/dist/emergent/CreateWorkflowTool.js.map +1 -0
- package/dist/emergent/EmergentCapabilityEngine.d.ts +68 -0
- package/dist/emergent/EmergentCapabilityEngine.d.ts.map +1 -1
- package/dist/emergent/EmergentCapabilityEngine.js +99 -0
- package/dist/emergent/EmergentCapabilityEngine.js.map +1 -1
- package/dist/emergent/ManageSkillsTool.d.ts +154 -0
- package/dist/emergent/ManageSkillsTool.d.ts.map +1 -0
- package/dist/emergent/ManageSkillsTool.js +241 -0
- package/dist/emergent/ManageSkillsTool.js.map +1 -0
- package/dist/emergent/PersonalityMutationStore.d.ts +183 -0
- package/dist/emergent/PersonalityMutationStore.d.ts.map +1 -0
- package/dist/emergent/PersonalityMutationStore.js +222 -0
- package/dist/emergent/PersonalityMutationStore.js.map +1 -0
- package/dist/emergent/SelfEvaluateTool.d.ts +199 -0
- package/dist/emergent/SelfEvaluateTool.d.ts.map +1 -0
- package/dist/emergent/SelfEvaluateTool.js +410 -0
- package/dist/emergent/SelfEvaluateTool.js.map +1 -0
- package/dist/emergent/SelfImprovementConfig.d.ts +177 -0
- package/dist/emergent/SelfImprovementConfig.d.ts.map +1 -0
- package/dist/emergent/SelfImprovementConfig.js +43 -0
- package/dist/emergent/SelfImprovementConfig.js.map +1 -0
- package/dist/emergent/index.d.ts +8 -0
- package/dist/emergent/index.d.ts.map +1 -1
- package/dist/emergent/index.js +6 -0
- package/dist/emergent/index.js.map +1 -1
- package/dist/emergent/types.d.ts +9 -0
- package/dist/emergent/types.d.ts.map +1 -1
- package/dist/emergent/types.js.map +1 -1
- package/dist/memory/AgentMemory.d.ts +2 -2
- package/dist/memory/AgentMemory.d.ts.map +1 -1
- package/dist/memory/AgentMemory.js +4 -3
- package/dist/memory/AgentMemory.js.map +1 -1
- package/dist/memory/consolidation/ConsolidationLoop.d.ts +31 -4
- package/dist/memory/consolidation/ConsolidationLoop.d.ts.map +1 -1
- package/dist/memory/consolidation/ConsolidationLoop.js +112 -69
- package/dist/memory/consolidation/ConsolidationLoop.js.map +1 -1
- package/dist/memory/facade/Memory.d.ts +12 -6
- package/dist/memory/facade/Memory.d.ts.map +1 -1
- package/dist/memory/facade/Memory.js +216 -212
- package/dist/memory/facade/Memory.js.map +1 -1
- package/dist/memory/facade/types.d.ts +11 -1
- package/dist/memory/facade/types.d.ts.map +1 -1
- package/dist/memory/feedback/RetrievalFeedbackSignal.d.ts +7 -10
- package/dist/memory/feedback/RetrievalFeedbackSignal.d.ts.map +1 -1
- package/dist/memory/feedback/RetrievalFeedbackSignal.js +40 -36
- package/dist/memory/feedback/RetrievalFeedbackSignal.js.map +1 -1
- package/dist/memory/io/ChatGptImporter.d.ts.map +1 -1
- package/dist/memory/io/ChatGptImporter.js +24 -18
- package/dist/memory/io/ChatGptImporter.js.map +1 -1
- package/dist/memory/io/CsvImporter.d.ts.map +1 -1
- package/dist/memory/io/CsvImporter.js +21 -8
- package/dist/memory/io/CsvImporter.js.map +1 -1
- package/dist/memory/io/JsonExporter.d.ts.map +1 -1
- package/dist/memory/io/JsonExporter.js +6 -17
- package/dist/memory/io/JsonExporter.js.map +1 -1
- package/dist/memory/io/JsonImporter.d.ts +3 -0
- package/dist/memory/io/JsonImporter.d.ts.map +1 -1
- package/dist/memory/io/JsonImporter.js +58 -29
- package/dist/memory/io/JsonImporter.js.map +1 -1
- package/dist/memory/io/MarkdownExporter.d.ts.map +1 -1
- package/dist/memory/io/MarkdownExporter.js +1 -4
- package/dist/memory/io/MarkdownExporter.js.map +1 -1
- package/dist/memory/io/MarkdownImporter.d.ts.map +1 -1
- package/dist/memory/io/MarkdownImporter.js +12 -7
- package/dist/memory/io/MarkdownImporter.js.map +1 -1
- package/dist/memory/io/ObsidianExporter.d.ts +14 -9
- package/dist/memory/io/ObsidianExporter.d.ts.map +1 -1
- package/dist/memory/io/ObsidianExporter.js +35 -23
- package/dist/memory/io/ObsidianExporter.js.map +1 -1
- package/dist/memory/io/ObsidianImporter.d.ts.map +1 -1
- package/dist/memory/io/ObsidianImporter.js +34 -27
- package/dist/memory/io/ObsidianImporter.js.map +1 -1
- package/dist/memory/io/SqliteExporter.d.ts +1 -2
- package/dist/memory/io/SqliteExporter.d.ts.map +1 -1
- package/dist/memory/io/SqliteExporter.js +2 -3
- package/dist/memory/io/SqliteExporter.js.map +1 -1
- package/dist/memory/io/SqliteImporter.d.ts +3 -0
- package/dist/memory/io/SqliteImporter.d.ts.map +1 -1
- package/dist/memory/io/SqliteImporter.js +62 -27
- package/dist/memory/io/SqliteImporter.js.map +1 -1
- package/dist/memory/store/SqliteBrain.d.ts +84 -24
- package/dist/memory/store/SqliteBrain.d.ts.map +1 -1
- package/dist/memory/store/SqliteBrain.js +139 -55
- package/dist/memory/store/SqliteBrain.js.map +1 -1
- package/dist/memory/store/SqliteKnowledgeGraph.d.ts +6 -2
- package/dist/memory/store/SqliteKnowledgeGraph.d.ts.map +1 -1
- package/dist/memory/store/SqliteKnowledgeGraph.js +94 -120
- package/dist/memory/store/SqliteKnowledgeGraph.js.map +1 -1
- package/dist/memory/store/SqliteMemoryGraph.d.ts +4 -5
- package/dist/memory/store/SqliteMemoryGraph.d.ts.map +1 -1
- package/dist/memory/store/SqliteMemoryGraph.js +31 -41
- package/dist/memory/store/SqliteMemoryGraph.js.map +1 -1
- package/dist/memory/tools/MemoryAddTool.d.ts.map +1 -1
- package/dist/memory/tools/MemoryAddTool.js +12 -16
- package/dist/memory/tools/MemoryAddTool.js.map +1 -1
- package/dist/memory/tools/MemoryDeleteTool.d.ts.map +1 -1
- package/dist/memory/tools/MemoryDeleteTool.js +1 -3
- package/dist/memory/tools/MemoryDeleteTool.js.map +1 -1
- package/dist/memory/tools/MemoryMergeTool.d.ts.map +1 -1
- package/dist/memory/tools/MemoryMergeTool.js +13 -22
- package/dist/memory/tools/MemoryMergeTool.js.map +1 -1
- package/dist/memory/tools/MemorySearchTool.js +4 -4
- package/dist/memory/tools/MemorySearchTool.js.map +1 -1
- package/dist/memory/tools/MemoryUpdateTool.d.ts.map +1 -1
- package/dist/memory/tools/MemoryUpdateTool.js +10 -18
- package/dist/memory/tools/MemoryUpdateTool.js.map +1 -1
- package/dist/orchestration/runtime/LoopController.d.ts +7 -0
- package/dist/orchestration/runtime/LoopController.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.js +7 -0
- package/dist/orchestration/runtime/LoopController.js.map +1 -1
- package/package.json +1 -1
|
@@ -63,7 +63,7 @@ function sha256(content) {
|
|
|
63
63
|
*
|
|
64
64
|
* ## Quick start
|
|
65
65
|
* ```ts
|
|
66
|
-
* const mem =
|
|
66
|
+
* const mem = await Memory.create({ store: 'sqlite', path: './brain.sqlite' });
|
|
67
67
|
*
|
|
68
68
|
* await mem.remember('The user prefers dark mode');
|
|
69
69
|
* const results = await mem.recall('dark mode preference');
|
|
@@ -74,67 +74,31 @@ function sha256(content) {
|
|
|
74
74
|
*/
|
|
75
75
|
export class Memory {
|
|
76
76
|
// -------------------------------------------------------------------
|
|
77
|
-
// Constructor
|
|
77
|
+
// Constructor (private — use Memory.create() instead)
|
|
78
78
|
// -------------------------------------------------------------------
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* Initialization sequence:
|
|
83
|
-
* 1. Merge `config` with defaults (store='sqlite', path=tmpdir, graph=true,
|
|
84
|
-
* selfImprove=true, decay=true).
|
|
85
|
-
* 2. Create `SqliteBrain(config.path)`.
|
|
86
|
-
* 3. Check embedding dimension compatibility (warn on mismatch).
|
|
87
|
-
* 4. Create `SqliteKnowledgeGraph(brain)`.
|
|
88
|
-
* 5. Create `SqliteMemoryGraph(brain)` and call `.initialize()`.
|
|
89
|
-
* 6. Create `LoaderRegistry()` (pre-registers all built-in loaders).
|
|
90
|
-
* 7. Create `FolderScanner(registry)`.
|
|
91
|
-
* 8. Create `ChunkingEngine()`.
|
|
92
|
-
* 9. If `selfImprove`: create `RetrievalFeedbackSignal(brain)` and
|
|
93
|
-
* `ConsolidationLoop(brain, memoryGraph)`.
|
|
94
|
-
*
|
|
95
|
-
* @param config - Optional configuration; see {@link MemoryConfig}.
|
|
80
|
+
* Private constructor. Receives an already-opened SqliteBrain and
|
|
81
|
+
* pre-computed configuration. Use {@link Memory.create} to instantiate.
|
|
96
82
|
*/
|
|
97
|
-
constructor(config) {
|
|
83
|
+
constructor(brain, config) {
|
|
98
84
|
/** HNSW sidecar index for O(log n) vector search alongside SQLite. */
|
|
99
85
|
this._hnswSidecar = null;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
this._config = {
|
|
103
|
-
store: 'sqlite',
|
|
104
|
-
path: path.join(os.tmpdir(), `brain-${randomSuffix}.sqlite`),
|
|
105
|
-
graph: true,
|
|
106
|
-
selfImprove: true,
|
|
107
|
-
decay: true,
|
|
108
|
-
...config,
|
|
109
|
-
};
|
|
86
|
+
this._brain = brain;
|
|
87
|
+
this._config = config;
|
|
110
88
|
// Store the optional embedding function for vector search.
|
|
111
|
-
this._embed = config
|
|
112
|
-
|
|
113
|
-
throw new Error(`Memory currently supports only the SQLite-backed facade at runtime. ` +
|
|
114
|
-
`Received store="${this._config.store}".`);
|
|
115
|
-
}
|
|
116
|
-
// Step 2: create SqliteBrain.
|
|
117
|
-
this._brain = new SqliteBrain(this._config.path);
|
|
118
|
-
// Step 3: check embedding dimension compatibility.
|
|
119
|
-
const dimensions = this._config.embeddings?.dimensions ?? 1536;
|
|
120
|
-
const compatible = this._brain.checkEmbeddingCompat(dimensions);
|
|
121
|
-
if (!compatible) {
|
|
122
|
-
console.warn(`[Memory] Embedding dimension mismatch: expected ${dimensions} but brain ` +
|
|
123
|
-
`was previously configured with a different dimension. Vector similarity ` +
|
|
124
|
-
`searches may produce incorrect results.`);
|
|
125
|
-
}
|
|
126
|
-
// Step 4: create SqliteKnowledgeGraph.
|
|
89
|
+
this._embed = config.embed ?? null;
|
|
90
|
+
// Create SqliteKnowledgeGraph.
|
|
127
91
|
this._knowledgeGraph = new SqliteKnowledgeGraph(this._brain);
|
|
128
|
-
//
|
|
92
|
+
// Create SqliteMemoryGraph and initialize.
|
|
129
93
|
this._memoryGraph = new SqliteMemoryGraph(this._brain);
|
|
130
94
|
this._initPromise = this._memoryGraph.initialize();
|
|
131
|
-
//
|
|
95
|
+
// Create LoaderRegistry.
|
|
132
96
|
this._loaderRegistry = new LoaderRegistry();
|
|
133
|
-
//
|
|
97
|
+
// Create FolderScanner.
|
|
134
98
|
this._folderScanner = new FolderScanner(this._loaderRegistry);
|
|
135
|
-
//
|
|
99
|
+
// Create ChunkingEngine.
|
|
136
100
|
this._chunkingEngine = new ChunkingEngine();
|
|
137
|
-
//
|
|
101
|
+
// Self-improvement subsystems.
|
|
138
102
|
if (this._config.selfImprove) {
|
|
139
103
|
this._feedbackSignal = new RetrievalFeedbackSignal(this._brain);
|
|
140
104
|
this._consolidationLoop = new ConsolidationLoop(this._brain, this._memoryGraph);
|
|
@@ -143,7 +107,7 @@ export class Memory {
|
|
|
143
107
|
this._feedbackSignal = null;
|
|
144
108
|
this._consolidationLoop = null;
|
|
145
109
|
}
|
|
146
|
-
//
|
|
110
|
+
// HNSW sidecar index (O(log n) ANN alongside SQLite).
|
|
147
111
|
// Loads existing index from disk if present; auto-builds when trace count
|
|
148
112
|
// exceeds 1000 and embeddings are available. Falls back to FTS5-only
|
|
149
113
|
// recall if hnswlib-node is not installed or no embeddings exist.
|
|
@@ -164,6 +128,55 @@ export class Memory {
|
|
|
164
128
|
}
|
|
165
129
|
});
|
|
166
130
|
}
|
|
131
|
+
// -------------------------------------------------------------------
|
|
132
|
+
// Async factory
|
|
133
|
+
// -------------------------------------------------------------------
|
|
134
|
+
/**
|
|
135
|
+
* Create a new Memory instance and wire together all subsystems.
|
|
136
|
+
*
|
|
137
|
+
* Initialization sequence:
|
|
138
|
+
* 1. Merge `config` with defaults (store='sqlite', path=tmpdir, graph=true,
|
|
139
|
+
* selfImprove=true, decay=true).
|
|
140
|
+
* 2. Await `SqliteBrain.open(config.path)`.
|
|
141
|
+
* 3. Check embedding dimension compatibility (warn on mismatch).
|
|
142
|
+
* 4. Create `SqliteKnowledgeGraph(brain)`.
|
|
143
|
+
* 5. Create `SqliteMemoryGraph(brain)` and call `.initialize()`.
|
|
144
|
+
* 6. Create `LoaderRegistry()` (pre-registers all built-in loaders).
|
|
145
|
+
* 7. Create `FolderScanner(registry)`.
|
|
146
|
+
* 8. Create `ChunkingEngine()`.
|
|
147
|
+
* 9. If `selfImprove`: create `RetrievalFeedbackSignal(brain)` and
|
|
148
|
+
* `ConsolidationLoop(brain, memoryGraph)`.
|
|
149
|
+
*
|
|
150
|
+
* @param config - Optional configuration; see {@link MemoryConfig}.
|
|
151
|
+
* @returns A fully initialised Memory instance.
|
|
152
|
+
*/
|
|
153
|
+
static async create(config) {
|
|
154
|
+
// Step 1: merge with defaults.
|
|
155
|
+
const randomSuffix = Math.random().toString(36).slice(2, 10);
|
|
156
|
+
const merged = {
|
|
157
|
+
store: 'sqlite',
|
|
158
|
+
path: path.join(os.tmpdir(), `brain-${randomSuffix}.sqlite`),
|
|
159
|
+
graph: true,
|
|
160
|
+
selfImprove: true,
|
|
161
|
+
decay: true,
|
|
162
|
+
...config,
|
|
163
|
+
};
|
|
164
|
+
if (merged.store !== 'sqlite') {
|
|
165
|
+
throw new Error(`Memory currently supports only the SQLite-backed facade at runtime. ` +
|
|
166
|
+
`Received store="${merged.store}".`);
|
|
167
|
+
}
|
|
168
|
+
// Step 2: create SqliteBrain (async).
|
|
169
|
+
const brain = await SqliteBrain.open(merged.path);
|
|
170
|
+
// Step 3: check embedding dimension compatibility.
|
|
171
|
+
const dimensions = merged.embeddings?.dimensions ?? 1536;
|
|
172
|
+
const compatible = await brain.checkEmbeddingCompat(dimensions);
|
|
173
|
+
if (!compatible) {
|
|
174
|
+
console.warn(`[Memory] Embedding dimension mismatch: expected ${dimensions} but brain ` +
|
|
175
|
+
`was previously configured with a different dimension. Vector similarity ` +
|
|
176
|
+
`searches may produce incorrect results.`);
|
|
177
|
+
}
|
|
178
|
+
return new Memory(brain, merged);
|
|
179
|
+
}
|
|
167
180
|
// =========================================================================
|
|
168
181
|
// Core memory operations
|
|
169
182
|
// =========================================================================
|
|
@@ -184,7 +197,7 @@ export class Memory {
|
|
|
184
197
|
const type = options?.type ?? 'episodic';
|
|
185
198
|
const scope = options?.scope ?? 'user';
|
|
186
199
|
const scopeId = options?.scopeId ?? '';
|
|
187
|
-
const existing = this._findExistingTraceByHash(contentHash, type, scope, scopeId);
|
|
200
|
+
const existing = await this._findExistingTraceByHash(contentHash, type, scope, scopeId);
|
|
188
201
|
if (existing) {
|
|
189
202
|
return this._buildTrace(existing);
|
|
190
203
|
}
|
|
@@ -207,22 +220,28 @@ export class Memory {
|
|
|
207
220
|
}
|
|
208
221
|
}
|
|
209
222
|
// Insert into memory_traces.
|
|
210
|
-
this._brain.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
await this._brain.run(`INSERT INTO memory_traces
|
|
224
|
+
(id, type, scope, content, embedding, strength, created_at,
|
|
225
|
+
last_accessed, retrieval_count, tags, emotions, metadata, deleted)
|
|
226
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, NULL, 0, ?, ?, ?, 0)`, [
|
|
227
|
+
id,
|
|
228
|
+
type,
|
|
229
|
+
scope,
|
|
230
|
+
content,
|
|
231
|
+
embeddingBlob, // Binary blob or null
|
|
232
|
+
importance,
|
|
233
|
+
now,
|
|
234
|
+
JSON.stringify(tags),
|
|
235
|
+
JSON.stringify({}),
|
|
236
|
+
JSON.stringify(buildInitialTraceMetadata({}, { contentHash, entities, scopeId })),
|
|
237
|
+
]);
|
|
217
238
|
// Sync FTS5 index. The external-content FTS5 table needs explicit insert.
|
|
218
|
-
this._brain.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
)`)
|
|
225
|
-
.run(id, content, JSON.stringify(tags));
|
|
239
|
+
await this._brain.run(`INSERT INTO memory_traces_fts (rowid, content, tags)
|
|
240
|
+
VALUES (
|
|
241
|
+
(SELECT rowid FROM memory_traces WHERE id = ?),
|
|
242
|
+
?,
|
|
243
|
+
?
|
|
244
|
+
)`, [id, content, JSON.stringify(tags)]);
|
|
226
245
|
// Add to memory graph if available.
|
|
227
246
|
if (this._config.graph) {
|
|
228
247
|
await this._memoryGraph.addNode(id, {
|
|
@@ -237,20 +256,17 @@ export class Memory {
|
|
|
237
256
|
// The embedding is stored in the memory_traces table; if it's non-null,
|
|
238
257
|
// also index it in the HNSW sidecar for fast ANN recall.
|
|
239
258
|
if (this._hnswSidecar) {
|
|
240
|
-
const row = this._brain.
|
|
241
|
-
.prepare('SELECT embedding FROM memory_traces WHERE id = ?')
|
|
242
|
-
.get(id);
|
|
259
|
+
const row = await this._brain.get('SELECT embedding FROM memory_traces WHERE id = ?', [id]);
|
|
243
260
|
if (row?.embedding && row.embedding.length > 0) {
|
|
244
261
|
const { blobToEmbedding, isLegacyJsonBlob } = await import('../../rag/utils/vectorMath.js');
|
|
245
262
|
const vec = isLegacyJsonBlob(row.embedding)
|
|
246
263
|
? JSON.parse(row.embedding)
|
|
247
264
|
: blobToEmbedding(row.embedding);
|
|
248
|
-
const
|
|
265
|
+
const countRow = await this._brain.get('SELECT COUNT(*) as c FROM memory_traces WHERE deleted = 0');
|
|
266
|
+
const count = countRow?.c ?? 0;
|
|
249
267
|
if (!this._hnswSidecar.isActive && count >= 1000) {
|
|
250
268
|
// Threshold crossed — rebuild full index from all embeddings.
|
|
251
|
-
const allRows = this._brain.
|
|
252
|
-
.prepare('SELECT id, embedding FROM memory_traces WHERE deleted = 0 AND embedding IS NOT NULL')
|
|
253
|
-
.all();
|
|
269
|
+
const allRows = await this._brain.all('SELECT id, embedding FROM memory_traces WHERE deleted = 0 AND embedding IS NOT NULL');
|
|
254
270
|
const data = allRows
|
|
255
271
|
.filter(r => r.embedding && r.embedding.length > 0)
|
|
256
272
|
.map(r => ({
|
|
@@ -363,9 +379,7 @@ export class Memory {
|
|
|
363
379
|
ORDER BY abs(fts.rank) DESC
|
|
364
380
|
LIMIT ?
|
|
365
381
|
`;
|
|
366
|
-
const ftsRows = this._brain.
|
|
367
|
-
.prepare(ftsSql)
|
|
368
|
-
.all(...params, ftsQuery, limit * 3);
|
|
382
|
+
const ftsRows = await this._brain.all(ftsSql, [...params, ftsQuery, limit * 3]);
|
|
369
383
|
const ftsRank = new Map(ftsRows.map((r, i) => [r.id, i + 1]));
|
|
370
384
|
// Merge all candidate IDs.
|
|
371
385
|
const allIds = new Set([...hnswIds, ...ftsRows.map(r => r.id)]);
|
|
@@ -384,10 +398,8 @@ export class Memory {
|
|
|
384
398
|
// Fetch full rows for the top candidates.
|
|
385
399
|
if (topIds.length > 0) {
|
|
386
400
|
const placeholders = topIds.map(() => '?').join(',');
|
|
387
|
-
const fullRows = this._brain.
|
|
388
|
-
|
|
389
|
-
.all(...topIds);
|
|
390
|
-
const updatedRows = this._applyRecallAccessUpdates(fullRows);
|
|
401
|
+
const fullRows = await this._brain.all(`SELECT t.*, 0.0 as rank FROM memory_traces t WHERE t.id IN (${placeholders}) AND t.deleted = 0`, topIds);
|
|
402
|
+
const updatedRows = await this._applyRecallAccessUpdates(fullRows);
|
|
391
403
|
const rrfMap = new Map(scored.map(s => [s.id, s.rrfScore]));
|
|
392
404
|
return updatedRows.map((row) => ({
|
|
393
405
|
trace: this._buildTrace(row),
|
|
@@ -409,10 +421,8 @@ export class Memory {
|
|
|
409
421
|
LIMIT ?
|
|
410
422
|
`;
|
|
411
423
|
params.push(ftsQuery, limit);
|
|
412
|
-
const rows = this._brain.
|
|
413
|
-
|
|
414
|
-
.all(...params);
|
|
415
|
-
const updatedRows = this._applyRecallAccessUpdates(rows);
|
|
424
|
+
const rows = await this._brain.all(sql, params);
|
|
425
|
+
const updatedRows = await this._applyRecallAccessUpdates(rows);
|
|
416
426
|
return updatedRows.map((row) => ({
|
|
417
427
|
trace: this._buildTrace(row),
|
|
418
428
|
score: row.strength * Math.abs(row.rank),
|
|
@@ -428,9 +438,7 @@ export class Memory {
|
|
|
428
438
|
*/
|
|
429
439
|
async forget(traceId) {
|
|
430
440
|
await this._initPromise;
|
|
431
|
-
this._brain.
|
|
432
|
-
.prepare('UPDATE memory_traces SET deleted = 1 WHERE id = ?')
|
|
433
|
-
.run(traceId);
|
|
441
|
+
await this._brain.run('UPDATE memory_traces SET deleted = 1 WHERE id = ?', [traceId]);
|
|
434
442
|
}
|
|
435
443
|
// =========================================================================
|
|
436
444
|
// Document ingestion
|
|
@@ -618,28 +626,24 @@ export class Memory {
|
|
|
618
626
|
/**
|
|
619
627
|
* Record retrieval feedback for a memory trace.
|
|
620
628
|
*
|
|
621
|
-
*
|
|
622
|
-
*
|
|
629
|
+
* The feedback is persisted asynchronously. This method returns a Promise
|
|
630
|
+
* that resolves once the feedback has been written.
|
|
623
631
|
*
|
|
624
632
|
* @param traceId - The ID of the trace being evaluated.
|
|
625
633
|
* @param signal - Whether the trace was `'used'` or `'ignored'` by the LLM.
|
|
626
634
|
*/
|
|
627
|
-
feedback(traceId, signal) {
|
|
635
|
+
async feedback(traceId, signal) {
|
|
628
636
|
if (!this._feedbackSignal)
|
|
629
637
|
return;
|
|
630
638
|
try {
|
|
631
639
|
const now = Date.now();
|
|
632
|
-
const row = this._brain.
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
this._brain.db
|
|
640
|
-
.prepare(`INSERT INTO retrieval_feedback (trace_id, signal, query, created_at)
|
|
641
|
-
VALUES (?, ?, NULL, ?)`)
|
|
642
|
-
.run(traceId, signal, now);
|
|
640
|
+
const row = await this._brain.get(`SELECT id, type, scope, content, embedding, strength, created_at,
|
|
641
|
+
last_accessed, retrieval_count, tags, emotions, metadata, deleted
|
|
642
|
+
FROM memory_traces
|
|
643
|
+
WHERE id = ?
|
|
644
|
+
LIMIT 1`, [traceId]);
|
|
645
|
+
await this._brain.run(`INSERT INTO retrieval_feedback (trace_id, signal, query, created_at)
|
|
646
|
+
VALUES (?, ?, NULL, ?)`, [traceId, signal, now]);
|
|
643
647
|
if (!row)
|
|
644
648
|
return;
|
|
645
649
|
if (signal === 'used') {
|
|
@@ -650,11 +654,15 @@ export class Memory {
|
|
|
650
654
|
reinforcementInterval: update.reinforcementInterval,
|
|
651
655
|
nextReinforcementAt: update.nextReinforcementAt,
|
|
652
656
|
}));
|
|
653
|
-
this._brain.
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
657
|
+
await this._brain.run(`UPDATE memory_traces
|
|
658
|
+
SET strength = ?, last_accessed = ?, retrieval_count = ?, metadata = ?
|
|
659
|
+
WHERE id = ?`, [
|
|
660
|
+
update.encodingStrength,
|
|
661
|
+
update.lastAccessedAt,
|
|
662
|
+
update.retrievalCount,
|
|
663
|
+
metadata,
|
|
664
|
+
traceId,
|
|
665
|
+
]);
|
|
658
666
|
return;
|
|
659
667
|
}
|
|
660
668
|
const penalty = penalizeUnused(this._buildTrace(row), now);
|
|
@@ -667,11 +675,9 @@ export class Memory {
|
|
|
667
675
|
? { nextReinforcementAt: existingDecay.nextReinforcementAt }
|
|
668
676
|
: {}),
|
|
669
677
|
}));
|
|
670
|
-
this._brain.
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
WHERE id = ?`)
|
|
674
|
-
.run(penalty.encodingStrength, penalty.lastAccessedAt, metadata, traceId);
|
|
678
|
+
await this._brain.run(`UPDATE memory_traces
|
|
679
|
+
SET strength = ?, last_accessed = ?, metadata = ?
|
|
680
|
+
WHERE id = ?`, [penalty.encodingStrength, penalty.lastAccessedAt, metadata, traceId]);
|
|
675
681
|
}
|
|
676
682
|
catch {
|
|
677
683
|
// Explicit feedback is best-effort; the caller should not fail on analytics updates.
|
|
@@ -758,7 +764,7 @@ export class Memory {
|
|
|
758
764
|
break;
|
|
759
765
|
}
|
|
760
766
|
if (result.imported > 0) {
|
|
761
|
-
this._rebuildFtsIndex();
|
|
767
|
+
await this._rebuildFtsIndex();
|
|
762
768
|
}
|
|
763
769
|
return result;
|
|
764
770
|
}
|
|
@@ -807,63 +813,42 @@ export class Memory {
|
|
|
807
813
|
*/
|
|
808
814
|
async health() {
|
|
809
815
|
await this._initPromise;
|
|
810
|
-
const db = this._brain.db;
|
|
811
816
|
// Total traces (active + deleted).
|
|
812
|
-
const totalRow =
|
|
813
|
-
.prepare('SELECT COUNT(*) AS cnt FROM memory_traces')
|
|
814
|
-
.get();
|
|
817
|
+
const totalRow = await this._brain.get('SELECT COUNT(*) AS cnt FROM memory_traces');
|
|
815
818
|
const totalTraces = totalRow?.cnt ?? 0;
|
|
816
819
|
// Active traces (not deleted).
|
|
817
|
-
const activeRow =
|
|
818
|
-
.prepare('SELECT COUNT(*) AS cnt FROM memory_traces WHERE deleted = 0')
|
|
819
|
-
.get();
|
|
820
|
+
const activeRow = await this._brain.get('SELECT COUNT(*) AS cnt FROM memory_traces WHERE deleted = 0');
|
|
820
821
|
const activeTraces = activeRow?.cnt ?? 0;
|
|
821
822
|
// Average strength of active traces.
|
|
822
|
-
const avgRow =
|
|
823
|
-
.prepare('SELECT AVG(strength) AS avg_s FROM memory_traces WHERE deleted = 0')
|
|
824
|
-
.get();
|
|
823
|
+
const avgRow = await this._brain.get('SELECT AVG(strength) AS avg_s FROM memory_traces WHERE deleted = 0');
|
|
825
824
|
const avgStrength = avgRow?.avg_s ?? 0;
|
|
826
825
|
// Weakest active trace.
|
|
827
|
-
const weakRow =
|
|
828
|
-
.prepare('SELECT MIN(strength) AS min_s FROM memory_traces WHERE deleted = 0')
|
|
829
|
-
.get();
|
|
826
|
+
const weakRow = await this._brain.get('SELECT MIN(strength) AS min_s FROM memory_traces WHERE deleted = 0');
|
|
830
827
|
const weakestTraceStrength = weakRow?.min_s ?? 0;
|
|
831
828
|
// Knowledge graph counts.
|
|
832
|
-
const nodesRow =
|
|
833
|
-
.prepare('SELECT COUNT(*) AS cnt FROM knowledge_nodes')
|
|
834
|
-
.get();
|
|
829
|
+
const nodesRow = await this._brain.get('SELECT COUNT(*) AS cnt FROM knowledge_nodes');
|
|
835
830
|
const graphNodes = nodesRow?.cnt ?? 0;
|
|
836
|
-
const edgesRow =
|
|
837
|
-
.prepare('SELECT COUNT(*) AS cnt FROM knowledge_edges')
|
|
838
|
-
.get();
|
|
831
|
+
const edgesRow = await this._brain.get('SELECT COUNT(*) AS cnt FROM knowledge_edges');
|
|
839
832
|
const graphEdges = edgesRow?.cnt ?? 0;
|
|
840
833
|
// Last consolidation timestamp.
|
|
841
|
-
const lastConsolRow =
|
|
842
|
-
.prepare('SELECT ran_at FROM consolidation_log ORDER BY ran_at DESC LIMIT 1')
|
|
843
|
-
.get();
|
|
834
|
+
const lastConsolRow = await this._brain.get('SELECT ran_at FROM consolidation_log ORDER BY ran_at DESC LIMIT 1');
|
|
844
835
|
const lastConsolidation = lastConsolRow
|
|
845
836
|
? new Date(lastConsolRow.ran_at).toISOString()
|
|
846
837
|
: null;
|
|
847
838
|
// Traces per type.
|
|
848
|
-
const typeRows =
|
|
849
|
-
.prepare('SELECT type, COUNT(*) AS cnt FROM memory_traces WHERE deleted = 0 GROUP BY type')
|
|
850
|
-
.all();
|
|
839
|
+
const typeRows = await this._brain.all('SELECT type, COUNT(*) AS cnt FROM memory_traces WHERE deleted = 0 GROUP BY type');
|
|
851
840
|
const tracesPerType = {};
|
|
852
841
|
for (const row of typeRows) {
|
|
853
842
|
tracesPerType[row.type] = row.cnt;
|
|
854
843
|
}
|
|
855
844
|
// Traces per scope.
|
|
856
|
-
const scopeRows =
|
|
857
|
-
.prepare('SELECT scope, COUNT(*) AS cnt FROM memory_traces WHERE deleted = 0 GROUP BY scope')
|
|
858
|
-
.all();
|
|
845
|
+
const scopeRows = await this._brain.all('SELECT scope, COUNT(*) AS cnt FROM memory_traces WHERE deleted = 0 GROUP BY scope');
|
|
859
846
|
const tracesPerScope = {};
|
|
860
847
|
for (const row of scopeRows) {
|
|
861
848
|
tracesPerScope[row.scope] = row.cnt;
|
|
862
849
|
}
|
|
863
850
|
// Total document chunks.
|
|
864
|
-
const docsRow =
|
|
865
|
-
.prepare('SELECT COUNT(*) AS cnt FROM documents')
|
|
866
|
-
.get();
|
|
851
|
+
const docsRow = await this._brain.get('SELECT COUNT(*) AS cnt FROM documents');
|
|
867
852
|
const documentsIngested = docsRow?.cnt ?? 0;
|
|
868
853
|
return {
|
|
869
854
|
totalTraces,
|
|
@@ -889,7 +874,7 @@ export class Memory {
|
|
|
889
874
|
*/
|
|
890
875
|
async close() {
|
|
891
876
|
await this._initPromise;
|
|
892
|
-
this._brain.close();
|
|
877
|
+
await this._brain.close();
|
|
893
878
|
}
|
|
894
879
|
// =========================================================================
|
|
895
880
|
// Private helpers
|
|
@@ -956,50 +941,58 @@ export class Memory {
|
|
|
956
941
|
* importer-used `import_hash` key so dedup works across facade and import
|
|
957
942
|
* workflows.
|
|
958
943
|
*/
|
|
959
|
-
_findExistingTraceByHash(contentHash, type, scope, scopeId) {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
.get(type, scope, scopeId, contentHash, contentHash);
|
|
944
|
+
async _findExistingTraceByHash(contentHash, type, scope, scopeId) {
|
|
945
|
+
const row = await this._brain.get(`SELECT id, type, scope, content, embedding, strength, created_at,
|
|
946
|
+
last_accessed, retrieval_count, tags, emotions, metadata, deleted
|
|
947
|
+
FROM memory_traces
|
|
948
|
+
WHERE deleted = 0
|
|
949
|
+
AND type = ?
|
|
950
|
+
AND scope = ?
|
|
951
|
+
AND ifnull(json_extract(metadata, '$.scopeId'), '') = ?
|
|
952
|
+
AND (
|
|
953
|
+
json_extract(metadata, '$.content_hash') = ?
|
|
954
|
+
OR json_extract(metadata, '$.import_hash') = ?
|
|
955
|
+
)
|
|
956
|
+
LIMIT 1`, [type, scope, scopeId, contentHash, contentHash]);
|
|
957
|
+
return row ?? undefined;
|
|
974
958
|
}
|
|
975
959
|
/**
|
|
976
960
|
* Apply spaced-repetition access updates to recalled rows and persist the
|
|
977
961
|
* updated retrieval metadata back to SQLite.
|
|
978
962
|
*/
|
|
979
|
-
_applyRecallAccessUpdates(rows) {
|
|
963
|
+
async _applyRecallAccessUpdates(rows) {
|
|
980
964
|
if (rows.length === 0)
|
|
981
965
|
return rows;
|
|
982
966
|
const now = Date.now();
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
967
|
+
return this._brain.transaction(async (trx) => {
|
|
968
|
+
const results = [];
|
|
969
|
+
for (const row of rows) {
|
|
970
|
+
const update = updateOnRetrieval(this._buildTrace(row), now);
|
|
971
|
+
const metadata = JSON.stringify(withPersistedDecayState(parseTraceMetadata(row.metadata), {
|
|
972
|
+
stability: update.stability,
|
|
973
|
+
accessCount: update.accessCount,
|
|
974
|
+
reinforcementInterval: update.reinforcementInterval,
|
|
975
|
+
nextReinforcementAt: update.nextReinforcementAt,
|
|
976
|
+
}));
|
|
977
|
+
await trx.run(`UPDATE memory_traces
|
|
978
|
+
SET strength = ?, last_accessed = ?, retrieval_count = ?, metadata = ?
|
|
979
|
+
WHERE id = ?`, [
|
|
980
|
+
update.encodingStrength,
|
|
981
|
+
update.lastAccessedAt,
|
|
982
|
+
update.retrievalCount,
|
|
983
|
+
metadata,
|
|
984
|
+
row.id,
|
|
985
|
+
]);
|
|
986
|
+
results.push({
|
|
987
|
+
...row,
|
|
988
|
+
strength: update.encodingStrength,
|
|
989
|
+
last_accessed: update.lastAccessedAt,
|
|
990
|
+
retrieval_count: update.retrievalCount,
|
|
991
|
+
metadata,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
return results;
|
|
995
|
+
});
|
|
1003
996
|
}
|
|
1004
997
|
/**
|
|
1005
998
|
* Persist one loaded document into the documents/chunks/traces tables.
|
|
@@ -1009,40 +1002,46 @@ export class Memory {
|
|
|
1009
1002
|
*/
|
|
1010
1003
|
async _ingestLoadedDocument(source, doc, chunking, result) {
|
|
1011
1004
|
const contentHash = sha256(doc.content);
|
|
1012
|
-
const existingDoc = this._brain.
|
|
1013
|
-
.prepare(`SELECT id FROM documents WHERE content_hash = ? LIMIT 1`)
|
|
1014
|
-
.get(contentHash);
|
|
1005
|
+
const existingDoc = await this._brain.get(`SELECT id FROM documents WHERE content_hash = ? LIMIT 1`, [contentHash]);
|
|
1015
1006
|
if (existingDoc) {
|
|
1016
1007
|
return;
|
|
1017
1008
|
}
|
|
1018
1009
|
const chunks = await this._chunkingEngine.chunk(doc.content, chunking);
|
|
1019
1010
|
const docId = `doc_${crypto.randomUUID()}`;
|
|
1020
|
-
this._brain.
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1011
|
+
await this._brain.run(`INSERT INTO documents
|
|
1012
|
+
(id, path, format, title, content_hash, chunk_count, metadata, ingested_at)
|
|
1013
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
1014
|
+
docId,
|
|
1015
|
+
doc.metadata.source ?? source,
|
|
1016
|
+
doc.format,
|
|
1017
|
+
doc.metadata.title ?? null,
|
|
1018
|
+
contentHash,
|
|
1019
|
+
chunks.length,
|
|
1020
|
+
JSON.stringify(doc.metadata),
|
|
1021
|
+
Date.now(),
|
|
1022
|
+
]);
|
|
1025
1023
|
for (const chunk of chunks) {
|
|
1026
1024
|
const chunkId = `chunk_${crypto.randomUUID()}`;
|
|
1027
1025
|
const traceId = nextTraceId();
|
|
1028
1026
|
const createdAt = Date.now();
|
|
1029
|
-
this._brain.
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1027
|
+
await this._brain.run(`INSERT INTO memory_traces
|
|
1028
|
+
(id, type, scope, content, embedding, strength, created_at,
|
|
1029
|
+
last_accessed, retrieval_count, tags, emotions, metadata, deleted)
|
|
1030
|
+
VALUES (?, 'semantic', 'user', ?, NULL, 1.0, ?, NULL, 0, '[]', '{}', ?, 0)`, [
|
|
1031
|
+
traceId,
|
|
1032
|
+
chunk.content,
|
|
1033
|
+
createdAt,
|
|
1034
|
+
JSON.stringify(buildInitialTraceMetadata({
|
|
1035
|
+
document_id: docId,
|
|
1036
|
+
chunk_index: chunk.index,
|
|
1037
|
+
}, { contentHash: sha256(chunk.content) })),
|
|
1038
|
+
]);
|
|
1039
|
+
await this._brain.run(`INSERT INTO memory_traces_fts (rowid, content, tags)
|
|
1040
|
+
VALUES (
|
|
1041
|
+
(SELECT rowid FROM memory_traces WHERE id = ?),
|
|
1042
|
+
?,
|
|
1043
|
+
'[]'
|
|
1044
|
+
)`, [traceId, chunk.content]);
|
|
1046
1045
|
if (this._config.graph && !this._memoryGraph.hasNode(traceId)) {
|
|
1047
1046
|
await this._memoryGraph.addNode(traceId, {
|
|
1048
1047
|
type: 'semantic',
|
|
@@ -1052,10 +1051,15 @@ export class Memory {
|
|
|
1052
1051
|
createdAt,
|
|
1053
1052
|
});
|
|
1054
1053
|
}
|
|
1055
|
-
this._brain.
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1054
|
+
await this._brain.run(`INSERT INTO document_chunks (id, document_id, trace_id, content, chunk_index, page_number, embedding)
|
|
1055
|
+
VALUES (?, ?, ?, ?, ?, ?, NULL)`, [
|
|
1056
|
+
chunkId,
|
|
1057
|
+
docId,
|
|
1058
|
+
traceId,
|
|
1059
|
+
chunk.content,
|
|
1060
|
+
chunk.index,
|
|
1061
|
+
chunk.pageNumber ?? null,
|
|
1062
|
+
]);
|
|
1059
1063
|
result.chunksCreated++;
|
|
1060
1064
|
result.tracesCreated++;
|
|
1061
1065
|
}
|
|
@@ -1063,9 +1067,9 @@ export class Memory {
|
|
|
1063
1067
|
/**
|
|
1064
1068
|
* Rebuild the external-content FTS index after bulk import operations.
|
|
1065
1069
|
*/
|
|
1066
|
-
_rebuildFtsIndex() {
|
|
1070
|
+
async _rebuildFtsIndex() {
|
|
1067
1071
|
try {
|
|
1068
|
-
this._brain.
|
|
1072
|
+
await this._brain.exec(`INSERT INTO memory_traces_fts(memory_traces_fts) VALUES('rebuild')`);
|
|
1069
1073
|
}
|
|
1070
1074
|
catch {
|
|
1071
1075
|
// Best-effort; imports still succeed even if the FTS rebuild is unavailable.
|