@yamo/memory-mesh 3.2.2 → 3.2.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/README.md +36 -4
- package/bin/memory_mesh.js +82 -1
- package/lib/memory/adapters/client.d.ts +5 -0
- package/lib/memory/adapters/client.js +28 -3
- package/lib/memory/adapters/client.ts +29 -3
- package/lib/memory/memory-mesh.d.ts +5 -0
- package/lib/memory/memory-mesh.js +32 -0
- package/lib/memory/memory-mesh.ts +31 -0
- package/lib/memory/schema.d.ts +16 -0
- package/lib/memory/schema.js +86 -7
- package/lib/memory/schema.ts +81 -7
- package/lib/yamo/schema.js +16 -6
- package/lib/yamo/schema.ts +16 -6
- package/package.json +12 -8
package/README.md
CHANGED
|
@@ -47,14 +47,46 @@ The installer will:
|
|
|
47
47
|
|
|
48
48
|
### CLI
|
|
49
49
|
|
|
50
|
+
The `memory-mesh` CLI provides seven commands for full subconscious CRUD and recall:
|
|
51
|
+
|
|
50
52
|
```bash
|
|
51
53
|
# Store a memory (automatically scrubbed & embedded)
|
|
52
|
-
|
|
54
|
+
memory-mesh store --content "My important memory" --type insight
|
|
55
|
+
|
|
56
|
+
# Store with full provenance metadata
|
|
57
|
+
memory-mesh store -c "Insight text" -t decision -r "Improves latency" -h "Caching reduces p95"
|
|
58
|
+
|
|
59
|
+
# Bulk-ingest a directory (recursive, by extension)
|
|
60
|
+
memory-mesh pull ./docs --extension ".md,.yamo" --type documentation
|
|
61
|
+
|
|
62
|
+
# Semantic search
|
|
63
|
+
memory-mesh search "query about orchestration" --limit 5
|
|
64
|
+
|
|
65
|
+
# Retrieve a specific record by ID
|
|
66
|
+
memory-mesh get --id mem_abc123
|
|
53
67
|
|
|
54
|
-
#
|
|
55
|
-
|
|
68
|
+
# Delete a record by ID
|
|
69
|
+
memory-mesh delete --id mem_abc123
|
|
70
|
+
|
|
71
|
+
# Synthesize insights from recent memories
|
|
72
|
+
memory-mesh reflect --topic "bugs" --lookback 10
|
|
73
|
+
|
|
74
|
+
# Database health and statistics
|
|
75
|
+
memory-mesh stats
|
|
56
76
|
```
|
|
57
77
|
|
|
78
|
+
**Command Reference:**
|
|
79
|
+
|
|
80
|
+
| Command | Key Options | Description |
|
|
81
|
+
|---------|-------------|-------------|
|
|
82
|
+
| `store` | `-c/--content` (required), `-t/--type`, `-r/--rationale`, `-h/--hypothesis` | Persist a semantic memory |
|
|
83
|
+
| `pull` | `<path>` (required), `-e/--extension`, `-t/--type` | Bulk-ingest a directory |
|
|
84
|
+
| `search` | `<query>` (required), `-l/--limit` | Semantic recall |
|
|
85
|
+
| `get` | `-i/--id` (required) | Fetch a record by ID |
|
|
86
|
+
| `delete` | `-i/--id` (required) | Remove a record by ID |
|
|
87
|
+
| `reflect` | `-t/--topic`, `-l/--lookback` | Synthesize insights from memories |
|
|
88
|
+
| `stats` | — | DB health, count, embedding model |
|
|
89
|
+
|
|
58
90
|
### Node.js API
|
|
59
91
|
|
|
60
92
|
```javascript
|
|
@@ -173,7 +205,7 @@ docker run -v $(pwd)/data:/app/runtime/data \
|
|
|
173
205
|
|
|
174
206
|
## About YAMO Protocol
|
|
175
207
|
|
|
176
|
-
Memory Mesh is built on the **YAMO (Yet Another
|
|
208
|
+
Memory Mesh is built on the **YAMO (Yet Another Model Ontology) Protocol** - a structured language for transparent AI agent collaboration with immutable provenance tracking.
|
|
177
209
|
|
|
178
210
|
**YAMO Protocol Features:**
|
|
179
211
|
- **Structured Agent Workflows**: Semicolon-terminated constraints, explicit handoff chains
|
package/bin/memory_mesh.js
CHANGED
|
@@ -21,7 +21,7 @@ const program = new Command();
|
|
|
21
21
|
program
|
|
22
22
|
.name('memory-mesh')
|
|
23
23
|
.description('YAMO Semantic Subconscious - Protocol-Native CLI')
|
|
24
|
-
.version('3.2.
|
|
24
|
+
.version('3.2.3');
|
|
25
25
|
|
|
26
26
|
// Helper for beautiful logging
|
|
27
27
|
const ui = {
|
|
@@ -194,4 +194,85 @@ program
|
|
|
194
194
|
}
|
|
195
195
|
});
|
|
196
196
|
|
|
197
|
+
// 5. Get Command
|
|
198
|
+
program
|
|
199
|
+
.command('get')
|
|
200
|
+
.description('Retrieve a memory record by ID')
|
|
201
|
+
.requiredOption('-i, --id <id>', 'Memory record ID')
|
|
202
|
+
.action(async (options) => {
|
|
203
|
+
const mesh = new MemoryMesh();
|
|
204
|
+
try {
|
|
205
|
+
const record = await mesh.get(options.id);
|
|
206
|
+
if (!record) {
|
|
207
|
+
ui.warn(`Record not found: ${options.id}`);
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
const meta = typeof record.metadata === 'string' ? JSON.parse(record.metadata) : (record.metadata || {});
|
|
211
|
+
ui.header(`Memory ${options.id}`);
|
|
212
|
+
console.log(`${pc.bold('ID:')} ${pc.dim(record.id)}`);
|
|
213
|
+
console.log(`${pc.bold('Type:')} ${pc.dim(meta.type || 'event')}`);
|
|
214
|
+
console.log(`${pc.bold('Created:')} ${pc.dim(record.created_at)}`);
|
|
215
|
+
console.log(`\n${pc.white(record.content)}`);
|
|
216
|
+
} catch (err) {
|
|
217
|
+
ui.error(`Get failed: ${err.message}`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
} finally {
|
|
220
|
+
await mesh.close();
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// 6. Delete Command
|
|
225
|
+
program
|
|
226
|
+
.command('delete')
|
|
227
|
+
.description('Permanently remove a memory record by ID')
|
|
228
|
+
.requiredOption('-i, --id <id>', 'Memory record ID to delete')
|
|
229
|
+
.action(async (options) => {
|
|
230
|
+
const mesh = new MemoryMesh();
|
|
231
|
+
try {
|
|
232
|
+
await mesh.delete(options.id);
|
|
233
|
+
ui.success(`Deleted record ${pc.bold(options.id)}`);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
ui.error(`Delete failed: ${err.message}`);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
} finally {
|
|
238
|
+
await mesh.close();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// 7. Reflect Command
|
|
243
|
+
program
|
|
244
|
+
.command('reflect')
|
|
245
|
+
.description('Synthesize insights from stored memories')
|
|
246
|
+
.option('-t, --topic <topic>', 'Focus the reflection on a specific topic')
|
|
247
|
+
.option('-l, --lookback <number>', 'Number of memories to review', '10')
|
|
248
|
+
.action(async (options) => {
|
|
249
|
+
const mesh = new MemoryMesh();
|
|
250
|
+
try {
|
|
251
|
+
ui.info(`Reflecting on ${options.topic ? `"${pc.italic(options.topic)}"` : 'recent memories'}...`);
|
|
252
|
+
const result = await mesh.reflect({
|
|
253
|
+
topic: options.topic,
|
|
254
|
+
lookback: parseInt(options.lookback),
|
|
255
|
+
});
|
|
256
|
+
ui.header('Reflection');
|
|
257
|
+
if (result.reflection) {
|
|
258
|
+
console.log(pc.white(result.reflection));
|
|
259
|
+
console.log(`\n${pc.bold('Confidence:')} ${pc.cyan((result.confidence * 100).toFixed(0))}%`);
|
|
260
|
+
} else {
|
|
261
|
+
console.log(pc.dim(`Reviewed ${result.count} memories${result.topic ? ` on topic: ${result.topic}` : ''}`));
|
|
262
|
+
console.log(`\n${pc.bold('Prompt for LLM:')}\n${pc.white(result.prompt)}`);
|
|
263
|
+
if (result.context?.length) {
|
|
264
|
+
console.log('');
|
|
265
|
+
result.context.forEach((m, i) => {
|
|
266
|
+
console.log(`${pc.cyan(`Memory ${i + 1}:`)} ${pc.white(m.content.substring(0, 200))}${m.content.length > 200 ? '...' : ''}`);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (err) {
|
|
271
|
+
ui.error(`Reflect failed: ${err.message}`);
|
|
272
|
+
process.exit(1);
|
|
273
|
+
} finally {
|
|
274
|
+
await mesh.close();
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
197
278
|
program.parse();
|
|
@@ -85,6 +85,11 @@ export declare class LanceDBClient {
|
|
|
85
85
|
* @throws {QueryError} If stats query fails
|
|
86
86
|
*/
|
|
87
87
|
getStats(): Promise<any>;
|
|
88
|
+
/**
|
|
89
|
+
* Compact old data files and prune versions older than 7 days.
|
|
90
|
+
* Best-effort — never throws.
|
|
91
|
+
*/
|
|
92
|
+
optimize(): Promise<void>;
|
|
88
93
|
/**
|
|
89
94
|
* Sanitize an ID to prevent SQL injection
|
|
90
95
|
* Removes any characters that aren't alphanumeric, underscore, or hyphen
|
|
@@ -201,7 +201,7 @@ export class LanceDBClient {
|
|
|
201
201
|
await this.connect();
|
|
202
202
|
}
|
|
203
203
|
this._validateVector(vector);
|
|
204
|
-
const { limit = 10, nprobes = 20, filter = null } = options;
|
|
204
|
+
const { limit = 10, nprobes = 20, filter = null, refineFactor, timeoutMs } = options;
|
|
205
205
|
return this._retryOperation(async () => {
|
|
206
206
|
if (!this.table) {
|
|
207
207
|
throw new StorageError("Table not initialized");
|
|
@@ -217,12 +217,21 @@ export class LanceDBClient {
|
|
|
217
217
|
// ignore
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
+
// Apply refineFactor for improved ANN recall (fetches N×candidates, reranks)
|
|
221
|
+
if (refineFactor && typeof refineFactor === "number") {
|
|
222
|
+
try {
|
|
223
|
+
query = query.refineFactor(refineFactor);
|
|
224
|
+
}
|
|
225
|
+
catch (_e) {
|
|
226
|
+
// ignore if not supported
|
|
227
|
+
}
|
|
228
|
+
}
|
|
220
229
|
// Apply filter if provided
|
|
221
230
|
if (filter) {
|
|
222
231
|
query = query.where(filter);
|
|
223
232
|
}
|
|
224
|
-
// Execute search with limit
|
|
225
|
-
const resultsArray = await query.limit(limit).toArray();
|
|
233
|
+
// Execute search with limit (and optional timeout)
|
|
234
|
+
const resultsArray = await query.limit(limit).toArray(timeoutMs ? { timeoutMs } : undefined);
|
|
226
235
|
return resultsArray.map((row) => ({
|
|
227
236
|
id: row.id,
|
|
228
237
|
content: row.content,
|
|
@@ -385,6 +394,22 @@ export class LanceDBClient {
|
|
|
385
394
|
};
|
|
386
395
|
});
|
|
387
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Compact old data files and prune versions older than 7 days.
|
|
399
|
+
* Best-effort — never throws.
|
|
400
|
+
*/
|
|
401
|
+
async optimize() {
|
|
402
|
+
if (!this.isConnected || !this.table)
|
|
403
|
+
return;
|
|
404
|
+
try {
|
|
405
|
+
await this.table.optimize({
|
|
406
|
+
cleanupOlderThan: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
catch (_e) {
|
|
410
|
+
// Best-effort — never block normal operations
|
|
411
|
+
}
|
|
412
|
+
}
|
|
388
413
|
/**
|
|
389
414
|
* Sanitize an ID to prevent SQL injection
|
|
390
415
|
* Removes any characters that aren't alphanumeric, underscore, or hyphen
|
|
@@ -201,7 +201,7 @@ export class LanceDBClient {
|
|
|
201
201
|
await this.connect();
|
|
202
202
|
}
|
|
203
203
|
this._validateVector(vector);
|
|
204
|
-
const { limit = 10, nprobes = 20, filter = null } = options;
|
|
204
|
+
const { limit = 10, nprobes = 20, filter = null, refineFactor, timeoutMs } = options;
|
|
205
205
|
return this._retryOperation(async () => {
|
|
206
206
|
if (!this.table) {
|
|
207
207
|
throw new StorageError("Table not initialized");
|
|
@@ -217,12 +217,23 @@ export class LanceDBClient {
|
|
|
217
217
|
// ignore
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
+
// Apply refineFactor for improved ANN recall (fetches N×candidates, reranks)
|
|
221
|
+
if (refineFactor && typeof refineFactor === "number") {
|
|
222
|
+
try {
|
|
223
|
+
query = query.refineFactor(refineFactor);
|
|
224
|
+
}
|
|
225
|
+
catch (_e) {
|
|
226
|
+
// ignore if not supported
|
|
227
|
+
}
|
|
228
|
+
}
|
|
220
229
|
// Apply filter if provided
|
|
221
230
|
if (filter) {
|
|
222
231
|
query = query.where(filter);
|
|
223
232
|
}
|
|
224
|
-
// Execute search with limit
|
|
225
|
-
const resultsArray = await query.limit(limit).toArray(
|
|
233
|
+
// Execute search with limit (and optional timeout)
|
|
234
|
+
const resultsArray = await query.limit(limit).toArray(
|
|
235
|
+
timeoutMs ? { timeoutMs } : undefined,
|
|
236
|
+
);
|
|
226
237
|
return resultsArray.map((row) => ({
|
|
227
238
|
id: row.id,
|
|
228
239
|
content: row.content,
|
|
@@ -385,6 +396,21 @@ export class LanceDBClient {
|
|
|
385
396
|
};
|
|
386
397
|
});
|
|
387
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Compact old data files and prune versions older than 7 days.
|
|
401
|
+
* Best-effort — never throws.
|
|
402
|
+
*/
|
|
403
|
+
async optimize() {
|
|
404
|
+
if (!this.isConnected || !this.table) return;
|
|
405
|
+
try {
|
|
406
|
+
await this.table.optimize({
|
|
407
|
+
cleanupOlderThan: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
catch (_e) {
|
|
411
|
+
// Best-effort — never block normal operations
|
|
412
|
+
}
|
|
413
|
+
}
|
|
388
414
|
/**
|
|
389
415
|
* Sanitize an ID to prevent SQL injection
|
|
390
416
|
* Removes any characters that aren't alphanumeric, underscore, or hyphen
|
|
@@ -444,6 +444,11 @@ export declare class MemoryMesh {
|
|
|
444
444
|
* await mesh.close(); // Clean up
|
|
445
445
|
* ```
|
|
446
446
|
*/
|
|
447
|
+
/**
|
|
448
|
+
* Compact old data files and prune versions older than 7 days.
|
|
449
|
+
* Best-effort — delegates to LanceDBClient.optimize().
|
|
450
|
+
*/
|
|
451
|
+
optimize(): Promise<any>;
|
|
447
452
|
close(): Promise<void>;
|
|
448
453
|
}
|
|
449
454
|
/**
|
|
@@ -288,8 +288,16 @@ export class MemoryMesh {
|
|
|
288
288
|
const skillSchema = createSynthesizedSkillSchema(this.vectorDimension);
|
|
289
289
|
this.skillTable = await this.client.db.createTable("synthesized_skills", [], {
|
|
290
290
|
schema: skillSchema,
|
|
291
|
+
storageOptions: { new_table_data_storage_version: "stable" },
|
|
291
292
|
});
|
|
292
293
|
}
|
|
294
|
+
// Migrate manifest paths to V2 layout (idempotent)
|
|
295
|
+
try {
|
|
296
|
+
await this.skillTable.migrateManifestPathsV2();
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// Already migrated or not a local table — ignore
|
|
300
|
+
}
|
|
293
301
|
if (process.env.YAMO_DEBUG === "true") {
|
|
294
302
|
logger.debug("YAMO blocks and synthesized skills tables initialized");
|
|
295
303
|
}
|
|
@@ -1706,6 +1714,13 @@ description: Auto-generated skill to handle: ${enrichedPrompt || topic}
|
|
|
1706
1714
|
* await mesh.close(); // Clean up
|
|
1707
1715
|
* ```
|
|
1708
1716
|
*/
|
|
1717
|
+
/**
|
|
1718
|
+
* Compact old data files and prune versions older than 7 days.
|
|
1719
|
+
* Best-effort — delegates to LanceDBClient.optimize().
|
|
1720
|
+
*/
|
|
1721
|
+
async optimize() {
|
|
1722
|
+
return this.client?.optimize?.();
|
|
1723
|
+
}
|
|
1709
1724
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1710
1725
|
async close() {
|
|
1711
1726
|
try {
|
|
@@ -1808,6 +1823,23 @@ export async function run() {
|
|
|
1808
1823
|
else if (action === "stats") {
|
|
1809
1824
|
process.stdout.write(`[MemoryMesh] Database Statistics:\n${JSON.stringify({ status: "ok", stats: await mesh.stats() }, null, 2)}\n`);
|
|
1810
1825
|
}
|
|
1826
|
+
else if (action === "get") {
|
|
1827
|
+
const record = await mesh.get(input.id);
|
|
1828
|
+
if (!record) {
|
|
1829
|
+
process.stdout.write(`[MemoryMesh] Record not found: ${input.id}\n${JSON.stringify({ status: "not_found", id: input.id })}\n`);
|
|
1830
|
+
}
|
|
1831
|
+
else {
|
|
1832
|
+
process.stdout.write(`[MemoryMesh] Record ${record.id}\n${JSON.stringify({ status: "ok", record }, null, 2)}\n`);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
else if (action === "delete") {
|
|
1836
|
+
await mesh.delete(input.id);
|
|
1837
|
+
process.stdout.write(`[MemoryMesh] Deleted record ${input.id}\n${JSON.stringify({ status: "ok", id: input.id })}\n`);
|
|
1838
|
+
}
|
|
1839
|
+
else if (action === "reflect") {
|
|
1840
|
+
const result = await mesh.reflect({ topic: input.topic, lookback: input.lookback });
|
|
1841
|
+
process.stdout.write(`[MemoryMesh] Reflection complete.\n${JSON.stringify({ status: "ok", result }, null, 2)}\n`);
|
|
1842
|
+
}
|
|
1811
1843
|
else {
|
|
1812
1844
|
logger.error({ action }, "Unknown action");
|
|
1813
1845
|
process.exit(1);
|
|
@@ -320,8 +320,16 @@ export class MemoryMesh {
|
|
|
320
320
|
const skillSchema = createSynthesizedSkillSchema(this.vectorDimension);
|
|
321
321
|
this.skillTable = await this.client.db.createTable("synthesized_skills", [], {
|
|
322
322
|
schema: skillSchema,
|
|
323
|
+
storageOptions: { new_table_data_storage_version: "stable" },
|
|
323
324
|
});
|
|
324
325
|
}
|
|
326
|
+
// Migrate manifest paths to V2 layout (idempotent)
|
|
327
|
+
try {
|
|
328
|
+
await this.skillTable.migrateManifestPathsV2();
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// Already migrated or not a local table — ignore
|
|
332
|
+
}
|
|
325
333
|
if (process.env.YAMO_DEBUG === "true") {
|
|
326
334
|
logger.debug("YAMO blocks and synthesized skills tables initialized");
|
|
327
335
|
}
|
|
@@ -1793,6 +1801,13 @@ description: Auto-generated skill to handle: ${enrichedPrompt || topic}
|
|
|
1793
1801
|
* await mesh.close(); // Clean up
|
|
1794
1802
|
* ```
|
|
1795
1803
|
*/
|
|
1804
|
+
/**
|
|
1805
|
+
* Compact old data files and prune versions older than 7 days.
|
|
1806
|
+
* Best-effort — delegates to LanceDBClient.optimize().
|
|
1807
|
+
*/
|
|
1808
|
+
async optimize() {
|
|
1809
|
+
return this.client?.optimize?.();
|
|
1810
|
+
}
|
|
1796
1811
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1797
1812
|
async close() {
|
|
1798
1813
|
try {
|
|
@@ -1895,6 +1910,22 @@ export async function run() {
|
|
|
1895
1910
|
else if (action === "stats") {
|
|
1896
1911
|
process.stdout.write(`[MemoryMesh] Database Statistics:\n${JSON.stringify({ status: "ok", stats: await mesh.stats() }, null, 2)}\n`);
|
|
1897
1912
|
}
|
|
1913
|
+
else if (action === "get") {
|
|
1914
|
+
const record = await mesh.get(input.id);
|
|
1915
|
+
if (!record) {
|
|
1916
|
+
process.stdout.write(`[MemoryMesh] Record not found: ${input.id}\n${JSON.stringify({ status: "not_found", id: input.id })}\n`);
|
|
1917
|
+
} else {
|
|
1918
|
+
process.stdout.write(`[MemoryMesh] Record ${record.id}\n${JSON.stringify({ status: "ok", record }, null, 2)}\n`);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
else if (action === "delete") {
|
|
1922
|
+
await mesh.delete(input.id);
|
|
1923
|
+
process.stdout.write(`[MemoryMesh] Deleted record ${input.id}\n${JSON.stringify({ status: "ok", id: input.id })}\n`);
|
|
1924
|
+
}
|
|
1925
|
+
else if (action === "reflect") {
|
|
1926
|
+
const result = await mesh.reflect({ topic: input.topic, lookback: input.lookback });
|
|
1927
|
+
process.stdout.write(`[MemoryMesh] Reflection complete.\n${JSON.stringify({ status: "ok", result }, null, 2)}\n`);
|
|
1928
|
+
}
|
|
1898
1929
|
else {
|
|
1899
1930
|
logger.error({ action }, "Unknown action");
|
|
1900
1931
|
process.exit(1);
|
package/lib/memory/schema.d.ts
CHANGED
|
@@ -57,6 +57,20 @@ export declare function createSynthesizedSkillSchema(vectorDim?: number): arrow.
|
|
|
57
57
|
* @returns {boolean} True if V2 schema detected
|
|
58
58
|
*/
|
|
59
59
|
export declare function isSchemaV2(schema: any): any;
|
|
60
|
+
/**
|
|
61
|
+
* Migrate an existing table to V2:
|
|
62
|
+
* 1. Migrate manifest paths to V2 layout (efficient versioning, idempotent)
|
|
63
|
+
* 2. Add nullable V2 columns to memory_entries-style tables if not already present
|
|
64
|
+
*
|
|
65
|
+
* Safe to call on any table — non-memory tables skip the schema column additions.
|
|
66
|
+
*/
|
|
67
|
+
export declare function migrateTableV2(table: any): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Ensure the vector column has an IVF_PQ index.
|
|
70
|
+
* Skipped when: table has too few rows, index already exists, or table is a mock.
|
|
71
|
+
* Called automatically by createMemoryTableWithDimension after migration.
|
|
72
|
+
*/
|
|
73
|
+
export declare function ensureVectorIndex(table: any): Promise<void>;
|
|
60
74
|
/**
|
|
61
75
|
* Memory table schema using Apache Arrow format (default 384 dimensions)
|
|
62
76
|
* @deprecated Use createMemorySchema(vectorDim) for dynamic dimensions
|
|
@@ -113,6 +127,8 @@ declare const _default: {
|
|
|
113
127
|
createMemorySchema: typeof createMemorySchema;
|
|
114
128
|
createMemorySchemaV2: typeof createMemorySchemaV2;
|
|
115
129
|
isSchemaV2: typeof isSchemaV2;
|
|
130
|
+
migrateTableV2: typeof migrateTableV2;
|
|
131
|
+
ensureVectorIndex: typeof ensureVectorIndex;
|
|
116
132
|
getEmbeddingDimension: typeof getEmbeddingDimension;
|
|
117
133
|
DEFAULT_VECTOR_DIMENSION: number;
|
|
118
134
|
EMBEDDING_DIMENSIONS: {
|
package/lib/memory/schema.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - text-embedding-3-small: 1536 dimensions
|
|
10
10
|
*/
|
|
11
11
|
import * as arrow from "apache-arrow";
|
|
12
|
+
import { Index } from "@lancedb/lancedb";
|
|
12
13
|
/**
|
|
13
14
|
* Default vector dimension (all-MiniLM-L6-v2)
|
|
14
15
|
*/
|
|
@@ -112,6 +113,75 @@ export function createSynthesizedSkillSchema(vectorDim = DEFAULT_VECTOR_DIMENSIO
|
|
|
112
113
|
export function isSchemaV2(schema) {
|
|
113
114
|
return schema.fields.some((f) => f.name === "session_id");
|
|
114
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Migrate an existing table to V2:
|
|
118
|
+
* 1. Migrate manifest paths to V2 layout (efficient versioning, idempotent)
|
|
119
|
+
* 2. Add nullable V2 columns to memory_entries-style tables if not already present
|
|
120
|
+
*
|
|
121
|
+
* Safe to call on any table — non-memory tables skip the schema column additions.
|
|
122
|
+
*/
|
|
123
|
+
export async function migrateTableV2(table) {
|
|
124
|
+
// Step 1: manifest path migration (idempotent on already-migrated tables)
|
|
125
|
+
try {
|
|
126
|
+
await table.migrateManifestPathsV2();
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Already migrated or not a local table — ignore
|
|
130
|
+
}
|
|
131
|
+
// Step 2: add V2 schema columns if this is a memory_entries-style table (V1)
|
|
132
|
+
// Guard: schema() may not exist on mock tables in tests
|
|
133
|
+
if (typeof table.schema !== "function")
|
|
134
|
+
return;
|
|
135
|
+
let schema;
|
|
136
|
+
try {
|
|
137
|
+
schema = await table.schema();
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return; // Can't inspect schema — skip
|
|
141
|
+
}
|
|
142
|
+
if (isSchemaV2(schema))
|
|
143
|
+
return;
|
|
144
|
+
// Only add V2 columns if the table has the V1 memory_entries shape
|
|
145
|
+
const fieldNames = schema.fields.map((f) => f.name);
|
|
146
|
+
if (!fieldNames.includes("content") || !fieldNames.includes("vector"))
|
|
147
|
+
return;
|
|
148
|
+
await table.addColumns([
|
|
149
|
+
{ name: "session_id", valueSql: "cast(null as string)" },
|
|
150
|
+
{ name: "agent_id", valueSql: "cast(null as string)" },
|
|
151
|
+
{ name: "memory_type", valueSql: "cast(null as string)" },
|
|
152
|
+
{ name: "importance_score", valueSql: "cast(null as float)" },
|
|
153
|
+
{ name: "access_count", valueSql: "cast(null as int)" },
|
|
154
|
+
{ name: "last_accessed", valueSql: "cast(null as timestamp)" },
|
|
155
|
+
]);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Ensure the vector column has an IVF_PQ index.
|
|
159
|
+
* Skipped when: table has too few rows, index already exists, or table is a mock.
|
|
160
|
+
* Called automatically by createMemoryTableWithDimension after migration.
|
|
161
|
+
*/
|
|
162
|
+
export async function ensureVectorIndex(table) {
|
|
163
|
+
if (typeof table.listIndices !== "function")
|
|
164
|
+
return;
|
|
165
|
+
try {
|
|
166
|
+
const indices = await table.listIndices();
|
|
167
|
+
if (indices.some((i) => i.columns.includes("vector")))
|
|
168
|
+
return;
|
|
169
|
+
const rowCount = await table.countRows();
|
|
170
|
+
if (rowCount < INDEX_CONFIG.vector.num_partitions)
|
|
171
|
+
return;
|
|
172
|
+
await table.createIndex("vector", {
|
|
173
|
+
config: Index.ivfPq({
|
|
174
|
+
numPartitions: INDEX_CONFIG.vector.num_partitions,
|
|
175
|
+
numSubVectors: INDEX_CONFIG.vector.num_sub_vectors,
|
|
176
|
+
distanceType: INDEX_CONFIG.vector.metric,
|
|
177
|
+
}),
|
|
178
|
+
replace: false,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Index creation is best-effort — never block table access
|
|
183
|
+
}
|
|
184
|
+
}
|
|
115
185
|
/**
|
|
116
186
|
* Memory table schema using Apache Arrow format (default 384 dimensions)
|
|
117
187
|
* @deprecated Use createMemorySchema(vectorDim) for dynamic dimensions
|
|
@@ -153,16 +223,23 @@ export async function createMemoryTable(db, tableName = "memory_entries") {
|
|
|
153
223
|
*/
|
|
154
224
|
export async function createMemoryTableWithDimension(db, tableName, vectorDim) {
|
|
155
225
|
try {
|
|
156
|
-
// Check if table already exists
|
|
157
226
|
const existingTables = await db.tableNames();
|
|
227
|
+
let table;
|
|
158
228
|
if (existingTables.includes(tableName)) {
|
|
159
|
-
|
|
229
|
+
table = await db.openTable(tableName);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
// New tables use V2 schema and stable storage format
|
|
233
|
+
const schema = createMemorySchemaV2(vectorDim);
|
|
234
|
+
table = await db.createTable(tableName, [], {
|
|
235
|
+
schema,
|
|
236
|
+
storageOptions: { new_table_data_storage_version: "stable" },
|
|
237
|
+
});
|
|
160
238
|
}
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
const table = await db.createTable(tableName, [], { schema }); // Cast to any because lancedb types might be strict about options
|
|
239
|
+
// Migrate existing tables to V2 (manifest paths + schema columns, idempotent)
|
|
240
|
+
await migrateTableV2(table);
|
|
241
|
+
// Ensure vector index exists (no-op if already present or insufficient rows)
|
|
242
|
+
await ensureVectorIndex(table);
|
|
166
243
|
return table;
|
|
167
244
|
}
|
|
168
245
|
catch (error) {
|
|
@@ -178,6 +255,8 @@ export default {
|
|
|
178
255
|
createMemorySchema,
|
|
179
256
|
createMemorySchemaV2,
|
|
180
257
|
isSchemaV2,
|
|
258
|
+
migrateTableV2,
|
|
259
|
+
ensureVectorIndex,
|
|
181
260
|
getEmbeddingDimension,
|
|
182
261
|
DEFAULT_VECTOR_DIMENSION,
|
|
183
262
|
EMBEDDING_DIMENSIONS,
|
package/lib/memory/schema.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* - text-embedding-3-small: 1536 dimensions
|
|
10
10
|
*/
|
|
11
11
|
import * as arrow from "apache-arrow";
|
|
12
|
+
import * as lancedb from "@lancedb/lancedb";
|
|
13
|
+
import { Index } from "@lancedb/lancedb";
|
|
12
14
|
/**
|
|
13
15
|
* Default vector dimension (all-MiniLM-L6-v2)
|
|
14
16
|
*/
|
|
@@ -112,6 +114,69 @@ export function createSynthesizedSkillSchema(vectorDim = DEFAULT_VECTOR_DIMENSIO
|
|
|
112
114
|
export function isSchemaV2(schema) {
|
|
113
115
|
return schema.fields.some((f) => f.name === "session_id");
|
|
114
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Migrate an existing table to V2:
|
|
119
|
+
* 1. Migrate manifest paths to V2 layout (efficient versioning, idempotent)
|
|
120
|
+
* 2. Add nullable V2 columns to memory_entries-style tables if not already present
|
|
121
|
+
*
|
|
122
|
+
* Safe to call on any table — non-memory tables skip the schema column additions.
|
|
123
|
+
*/
|
|
124
|
+
export async function migrateTableV2(table) {
|
|
125
|
+
// Step 1: manifest path migration (idempotent on already-migrated tables)
|
|
126
|
+
try {
|
|
127
|
+
await table.migrateManifestPathsV2();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Already migrated or not a local table — ignore
|
|
131
|
+
}
|
|
132
|
+
// Step 2: add V2 schema columns if this is a memory_entries-style table (V1)
|
|
133
|
+
// Guard: schema() may not exist on mock tables in tests
|
|
134
|
+
if (typeof table.schema !== "function") return;
|
|
135
|
+
let schema;
|
|
136
|
+
try {
|
|
137
|
+
schema = await table.schema();
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
return; // Can't inspect schema — skip
|
|
141
|
+
}
|
|
142
|
+
if (isSchemaV2(schema)) return;
|
|
143
|
+
// Only add V2 columns if the table has the V1 memory_entries shape
|
|
144
|
+
const fieldNames = schema.fields.map((f) => f.name);
|
|
145
|
+
if (!fieldNames.includes("content") || !fieldNames.includes("vector")) return;
|
|
146
|
+
await table.addColumns([
|
|
147
|
+
{ name: "session_id", valueSql: "cast(null as string)" },
|
|
148
|
+
{ name: "agent_id", valueSql: "cast(null as string)" },
|
|
149
|
+
{ name: "memory_type", valueSql: "cast(null as string)" },
|
|
150
|
+
{ name: "importance_score", valueSql: "cast(null as float)" },
|
|
151
|
+
{ name: "access_count", valueSql: "cast(null as int)" },
|
|
152
|
+
{ name: "last_accessed", valueSql: "cast(null as timestamp)" },
|
|
153
|
+
]);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Ensure the vector column has an IVF_PQ index.
|
|
157
|
+
* Skipped when: table has too few rows, index already exists, or table is a mock.
|
|
158
|
+
* Called automatically by createMemoryTableWithDimension after migration.
|
|
159
|
+
*/
|
|
160
|
+
export async function ensureVectorIndex(table) {
|
|
161
|
+
if (typeof table.listIndices !== "function") return;
|
|
162
|
+
try {
|
|
163
|
+
const indices = await table.listIndices();
|
|
164
|
+
if (indices.some((i) => i.columns.includes("vector"))) return;
|
|
165
|
+
const rowCount = await table.countRows();
|
|
166
|
+
if (rowCount < INDEX_CONFIG.vector.num_partitions) return;
|
|
167
|
+
await table.createIndex("vector", {
|
|
168
|
+
config: Index.ivfPq({
|
|
169
|
+
numPartitions: INDEX_CONFIG.vector.num_partitions,
|
|
170
|
+
numSubVectors: INDEX_CONFIG.vector.num_sub_vectors,
|
|
171
|
+
distanceType: INDEX_CONFIG.vector.metric,
|
|
172
|
+
}),
|
|
173
|
+
replace: false,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Index creation is best-effort — never block table access
|
|
178
|
+
}
|
|
179
|
+
}
|
|
115
180
|
/**
|
|
116
181
|
* Memory table schema using Apache Arrow format (default 384 dimensions)
|
|
117
182
|
* @deprecated Use createMemorySchema(vectorDim) for dynamic dimensions
|
|
@@ -153,16 +218,23 @@ export async function createMemoryTable(db, tableName = "memory_entries") {
|
|
|
153
218
|
*/
|
|
154
219
|
export async function createMemoryTableWithDimension(db, tableName, vectorDim) {
|
|
155
220
|
try {
|
|
156
|
-
// Check if table already exists
|
|
157
221
|
const existingTables = await db.tableNames();
|
|
222
|
+
let table;
|
|
158
223
|
if (existingTables.includes(tableName)) {
|
|
159
|
-
|
|
224
|
+
table = await db.openTable(tableName);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// New tables use V2 schema and stable storage format
|
|
228
|
+
const schema = createMemorySchemaV2(vectorDim);
|
|
229
|
+
table = await db.createTable(tableName, [], {
|
|
230
|
+
schema,
|
|
231
|
+
storageOptions: { new_table_data_storage_version: "stable" },
|
|
232
|
+
});
|
|
160
233
|
}
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
const table = await db.createTable(tableName, [], { schema }); // Cast to any because lancedb types might be strict about options
|
|
234
|
+
// Migrate existing tables to V2 (manifest paths + schema columns, idempotent)
|
|
235
|
+
await migrateTableV2(table);
|
|
236
|
+
// Ensure vector index exists (no-op if already present or insufficient rows)
|
|
237
|
+
await ensureVectorIndex(table);
|
|
166
238
|
return table;
|
|
167
239
|
}
|
|
168
240
|
catch (error) {
|
|
@@ -178,6 +250,8 @@ export default {
|
|
|
178
250
|
createMemorySchema,
|
|
179
251
|
createMemorySchemaV2,
|
|
180
252
|
isSchemaV2,
|
|
253
|
+
migrateTableV2,
|
|
254
|
+
ensureVectorIndex,
|
|
181
255
|
getEmbeddingDimension,
|
|
182
256
|
DEFAULT_VECTOR_DIMENSION,
|
|
183
257
|
EMBEDDING_DIMENSIONS,
|
package/lib/yamo/schema.js
CHANGED
|
@@ -40,15 +40,25 @@ export function createYamoSchema() {
|
|
|
40
40
|
*/
|
|
41
41
|
export async function createYamoTable(db, tableName = "yamo_blocks") {
|
|
42
42
|
try {
|
|
43
|
-
// Check if table already exists
|
|
44
43
|
const existingTables = await db.tableNames();
|
|
44
|
+
let table;
|
|
45
45
|
if (existingTables.includes(tableName)) {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
table = await db.openTable(tableName);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const schema = createYamoSchema();
|
|
50
|
+
table = await db.createTable(tableName, [], {
|
|
51
|
+
schema,
|
|
52
|
+
storageOptions: { new_table_data_storage_version: "stable" },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Migrate manifest paths to V2 layout (idempotent)
|
|
56
|
+
try {
|
|
57
|
+
await table.migrateManifestPathsV2();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Already migrated or not a local table — ignore
|
|
48
61
|
}
|
|
49
|
-
// Create new table with YAMO schema
|
|
50
|
-
const schema = createYamoSchema();
|
|
51
|
-
const table = await db.createTable(tableName, [], { schema });
|
|
52
62
|
return table;
|
|
53
63
|
}
|
|
54
64
|
catch (error) {
|
package/lib/yamo/schema.ts
CHANGED
|
@@ -40,15 +40,25 @@ export function createYamoSchema() {
|
|
|
40
40
|
*/
|
|
41
41
|
export async function createYamoTable(db, tableName = "yamo_blocks") {
|
|
42
42
|
try {
|
|
43
|
-
// Check if table already exists
|
|
44
43
|
const existingTables = await db.tableNames();
|
|
44
|
+
let table;
|
|
45
45
|
if (existingTables.includes(tableName)) {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
table = await db.openTable(tableName);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const schema = createYamoSchema();
|
|
50
|
+
table = await db.createTable(tableName, [], {
|
|
51
|
+
schema,
|
|
52
|
+
storageOptions: { new_table_data_storage_version: "stable" },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Migrate manifest paths to V2 layout (idempotent)
|
|
56
|
+
try {
|
|
57
|
+
await table.migrateManifestPathsV2();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Already migrated or not a local table — ignore
|
|
48
61
|
}
|
|
49
|
-
// Create new table with YAMO schema
|
|
50
|
-
const schema = createYamoSchema();
|
|
51
|
-
const table = await db.createTable(tableName, [], { schema });
|
|
52
62
|
return table;
|
|
53
63
|
}
|
|
54
64
|
catch (error) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yamo/memory-mesh",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"description": "Portable semantic memory system with Layer 0 Scrubber for YAMO agents (v3 Singularity Edition)",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/yamo-protocol/yamo-memory-mesh"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"main": "lib/memory/index.js",
|
|
7
11
|
"types": "lib/memory/index.d.ts",
|
|
@@ -23,16 +27,16 @@
|
|
|
23
27
|
"prepublishOnly": "npm run build"
|
|
24
28
|
},
|
|
25
29
|
"dependencies": {
|
|
26
|
-
"@lancedb/lancedb": "^0.
|
|
27
|
-
"@xenova/transformers": "^2.17.
|
|
28
|
-
"apache-arrow": "^
|
|
30
|
+
"@lancedb/lancedb": "^0.26.2",
|
|
31
|
+
"@xenova/transformers": "^2.17.2",
|
|
32
|
+
"apache-arrow": "^18.1.0",
|
|
29
33
|
"commander": "^14.0.3",
|
|
30
|
-
"onnxruntime-node": "^1.
|
|
34
|
+
"onnxruntime-node": "^1.24.3",
|
|
31
35
|
"pino": "^10.3.1",
|
|
32
36
|
"pino-pretty": "^13.1.3",
|
|
33
37
|
"cli-progress": "^3.12.0",
|
|
34
38
|
"picocolors": "^1.1.1",
|
|
35
|
-
"glob": "^13.0.
|
|
39
|
+
"glob": "^13.0.6"
|
|
36
40
|
},
|
|
37
41
|
"author": "Soverane Labs",
|
|
38
42
|
"license": "MIT",
|
|
@@ -40,9 +44,9 @@
|
|
|
40
44
|
"node": ">=18.0.0"
|
|
41
45
|
},
|
|
42
46
|
"devDependencies": {
|
|
43
|
-
"@types/node": "^25.0
|
|
47
|
+
"@types/node": "^25.4.0",
|
|
44
48
|
"cohere-ai": "^7.20.0",
|
|
45
|
-
"openai": "^6.
|
|
49
|
+
"openai": "^6.27.0",
|
|
46
50
|
"typescript": "^5.9.3"
|
|
47
51
|
}
|
|
48
52
|
}
|