cozo-memory 1.0.5 → 1.0.7
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 +122 -0
- package/dist/api_bridge.js +6 -4
- package/dist/cli-commands.js +210 -0
- package/dist/cli.js +490 -0
- package/dist/embedding-service.js +1 -1
- package/dist/hybrid-search.js +8 -3
- package/dist/index.js +108 -2
- package/dist/temporal-normalizer.js +2 -0
- package/dist/test-hybrid-debug.js +52 -0
- package/dist/test-mcp-search.js +47 -0
- package/dist/test-search-simple.js +27 -0
- package/dist/test-user-profile.js +59 -0
- package/dist/timestamp-utils.js +44 -0
- package/dist/tui-blessed.js +789 -0
- package/dist/tui-launcher.js +61 -0
- package/dist/tui.js +133 -0
- package/dist/tui.py +481 -0
- package/package.json +19 -2
package/dist/cli.js
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Pure CLI for CozoDB Memory
|
|
5
|
+
* Usage: cozo-memory <command> [options]
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
const commander_1 = require("commander");
|
|
45
|
+
const cli_commands_js_1 = require("./cli-commands.js");
|
|
46
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const program = new commander_1.Command();
|
|
49
|
+
const cli = new cli_commands_js_1.CLICommands();
|
|
50
|
+
// Helper to format output
|
|
51
|
+
function formatOutput(data, format = 'pretty') {
|
|
52
|
+
if (format === 'json') {
|
|
53
|
+
console.log(JSON.stringify(data, null, 2));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.log(chalk_1.default.cyan(JSON.stringify(data, null, 2)));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Helper to handle errors
|
|
60
|
+
function handleError(error) {
|
|
61
|
+
console.error(chalk_1.default.red('Error:'), error.message || error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
program
|
|
65
|
+
.name('cozo-memory')
|
|
66
|
+
.description('CLI for CozoDB Memory - Local-first persistent memory for AI agents')
|
|
67
|
+
.version('1.0.6');
|
|
68
|
+
// Entity commands
|
|
69
|
+
const entity = program.command('entity').description('Entity operations');
|
|
70
|
+
entity
|
|
71
|
+
.command('create')
|
|
72
|
+
.description('Create a new entity')
|
|
73
|
+
.requiredOption('-n, --name <name>', 'Entity name')
|
|
74
|
+
.requiredOption('-t, --type <type>', 'Entity type')
|
|
75
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
76
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
77
|
+
.action(async (options) => {
|
|
78
|
+
try {
|
|
79
|
+
await cli.init();
|
|
80
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
81
|
+
const result = await cli.createEntity(options.name, options.type, metadata);
|
|
82
|
+
formatOutput(result, options.format);
|
|
83
|
+
await cli.close();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
handleError(error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
entity
|
|
90
|
+
.command('get')
|
|
91
|
+
.description('Get entity details')
|
|
92
|
+
.requiredOption('-i, --id <id>', 'Entity ID')
|
|
93
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
94
|
+
.action(async (options) => {
|
|
95
|
+
try {
|
|
96
|
+
await cli.init();
|
|
97
|
+
const result = await cli.getEntity(options.id);
|
|
98
|
+
formatOutput(result, options.format);
|
|
99
|
+
await cli.close();
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
handleError(error);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
entity
|
|
106
|
+
.command('delete')
|
|
107
|
+
.description('Delete an entity')
|
|
108
|
+
.requiredOption('-i, --id <id>', 'Entity ID')
|
|
109
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
110
|
+
.action(async (options) => {
|
|
111
|
+
try {
|
|
112
|
+
await cli.init();
|
|
113
|
+
const result = await cli.deleteEntity(options.id);
|
|
114
|
+
formatOutput(result, options.format);
|
|
115
|
+
await cli.close();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
handleError(error);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
// Observation commands
|
|
122
|
+
const observation = program.command('observation').alias('obs').description('Observation operations');
|
|
123
|
+
observation
|
|
124
|
+
.command('add')
|
|
125
|
+
.description('Add observation to entity')
|
|
126
|
+
.requiredOption('-i, --entity-id <id>', 'Entity ID')
|
|
127
|
+
.requiredOption('-t, --text <text>', 'Observation text')
|
|
128
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
129
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
130
|
+
.action(async (options) => {
|
|
131
|
+
try {
|
|
132
|
+
await cli.init();
|
|
133
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
134
|
+
const result = await cli.addObservation(options.entityId, options.text, metadata);
|
|
135
|
+
formatOutput(result, options.format);
|
|
136
|
+
await cli.close();
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
handleError(error);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
// Relation commands
|
|
143
|
+
const relation = program.command('relation').alias('rel').description('Relation operations');
|
|
144
|
+
relation
|
|
145
|
+
.command('create')
|
|
146
|
+
.description('Create relation between entities')
|
|
147
|
+
.requiredOption('--from <id>', 'From entity ID')
|
|
148
|
+
.requiredOption('--to <id>', 'To entity ID')
|
|
149
|
+
.requiredOption('--type <type>', 'Relation type')
|
|
150
|
+
.option('-s, --strength <number>', 'Relation strength (0-1)', parseFloat)
|
|
151
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
152
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
153
|
+
.action(async (options) => {
|
|
154
|
+
try {
|
|
155
|
+
await cli.init();
|
|
156
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
157
|
+
const result = await cli.createRelation(options.from, options.to, options.type, options.strength, metadata);
|
|
158
|
+
formatOutput(result, options.format);
|
|
159
|
+
await cli.close();
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
handleError(error);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// Search commands
|
|
166
|
+
const search = program.command('search').description('Search operations');
|
|
167
|
+
search
|
|
168
|
+
.command('query')
|
|
169
|
+
.description('Search memory')
|
|
170
|
+
.requiredOption('-q, --query <query>', 'Search query')
|
|
171
|
+
.option('-l, --limit <number>', 'Result limit', parseInt, 10)
|
|
172
|
+
.option('-t, --types <types>', 'Entity types (comma-separated)')
|
|
173
|
+
.option('--no-entities', 'Exclude entities')
|
|
174
|
+
.option('--no-observations', 'Exclude observations')
|
|
175
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
176
|
+
.action(async (options) => {
|
|
177
|
+
try {
|
|
178
|
+
await cli.init();
|
|
179
|
+
const entityTypes = options.types ? options.types.split(',') : undefined;
|
|
180
|
+
const result = await cli.search(options.query, options.limit, entityTypes, options.entities, options.observations);
|
|
181
|
+
formatOutput(result, options.format);
|
|
182
|
+
await cli.close();
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
handleError(error);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
search
|
|
189
|
+
.command('context')
|
|
190
|
+
.description('Get contextual information')
|
|
191
|
+
.requiredOption('-q, --query <query>', 'Context query')
|
|
192
|
+
.option('-w, --window <number>', 'Context window', parseInt)
|
|
193
|
+
.option('-h, --hours <number>', 'Time range in hours', parseInt)
|
|
194
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
195
|
+
.action(async (options) => {
|
|
196
|
+
try {
|
|
197
|
+
await cli.init();
|
|
198
|
+
const result = await cli.context(options.query, options.window, options.hours);
|
|
199
|
+
formatOutput(result, options.format);
|
|
200
|
+
await cli.close();
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
handleError(error);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
// Graph commands
|
|
207
|
+
const graph = program.command('graph').description('Graph operations');
|
|
208
|
+
graph
|
|
209
|
+
.command('explore')
|
|
210
|
+
.description('Explore graph from entity')
|
|
211
|
+
.requiredOption('-s, --start <id>', 'Start entity ID')
|
|
212
|
+
.option('-e, --end <id>', 'End entity ID (for path finding)')
|
|
213
|
+
.option('-h, --hops <number>', 'Max hops', parseInt, 3)
|
|
214
|
+
.option('-t, --types <types>', 'Relation types (comma-separated)')
|
|
215
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
216
|
+
.action(async (options) => {
|
|
217
|
+
try {
|
|
218
|
+
await cli.init();
|
|
219
|
+
const relationTypes = options.types ? options.types.split(',') : undefined;
|
|
220
|
+
const result = await cli.explore(options.start, options.end, options.hops, relationTypes);
|
|
221
|
+
formatOutput(result, options.format);
|
|
222
|
+
await cli.close();
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
handleError(error);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
graph
|
|
229
|
+
.command('pagerank')
|
|
230
|
+
.description('Calculate PageRank')
|
|
231
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
232
|
+
.action(async (options) => {
|
|
233
|
+
try {
|
|
234
|
+
await cli.init();
|
|
235
|
+
const result = await cli.pagerank();
|
|
236
|
+
formatOutput(result, options.format);
|
|
237
|
+
await cli.close();
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
handleError(error);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
graph
|
|
244
|
+
.command('communities')
|
|
245
|
+
.description('Detect communities')
|
|
246
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
247
|
+
.action(async (options) => {
|
|
248
|
+
try {
|
|
249
|
+
await cli.init();
|
|
250
|
+
const result = await cli.communities();
|
|
251
|
+
formatOutput(result, options.format);
|
|
252
|
+
await cli.close();
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
handleError(error);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
// System commands
|
|
259
|
+
const system = program.command('system').alias('sys').description('System operations');
|
|
260
|
+
system
|
|
261
|
+
.command('health')
|
|
262
|
+
.description('Check system health')
|
|
263
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
264
|
+
.action(async (options) => {
|
|
265
|
+
try {
|
|
266
|
+
await cli.init();
|
|
267
|
+
const result = await cli.health();
|
|
268
|
+
formatOutput(result, options.format);
|
|
269
|
+
await cli.close();
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
handleError(error);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
system
|
|
276
|
+
.command('metrics')
|
|
277
|
+
.description('Get system metrics')
|
|
278
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
279
|
+
.action(async (options) => {
|
|
280
|
+
try {
|
|
281
|
+
await cli.init();
|
|
282
|
+
const result = await cli.metrics();
|
|
283
|
+
formatOutput(result, options.format);
|
|
284
|
+
await cli.close();
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
handleError(error);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
// Export/Import commands
|
|
291
|
+
const exportCmd = program.command('export').description('Export memory');
|
|
292
|
+
exportCmd
|
|
293
|
+
.command('json')
|
|
294
|
+
.description('Export as JSON')
|
|
295
|
+
.option('-o, --output <file>', 'Output file')
|
|
296
|
+
.option('--include-metadata', 'Include metadata')
|
|
297
|
+
.option('--include-relationships', 'Include relationships')
|
|
298
|
+
.option('--include-observations', 'Include observations')
|
|
299
|
+
.action(async (options) => {
|
|
300
|
+
try {
|
|
301
|
+
await cli.init();
|
|
302
|
+
const result = await cli.exportMemory('json', {
|
|
303
|
+
includeMetadata: options.includeMetadata,
|
|
304
|
+
includeRelationships: options.includeRelationships,
|
|
305
|
+
includeObservations: options.includeObservations
|
|
306
|
+
});
|
|
307
|
+
const jsonData = typeof result.data === 'string' ? result.data : JSON.stringify(result.data, null, 2);
|
|
308
|
+
if (options.output) {
|
|
309
|
+
fs.writeFileSync(options.output, jsonData);
|
|
310
|
+
console.log(chalk_1.default.green(`✓ Exported to ${options.output}`));
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
console.log(jsonData);
|
|
314
|
+
}
|
|
315
|
+
await cli.close();
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
handleError(error);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
exportCmd
|
|
322
|
+
.command('markdown')
|
|
323
|
+
.description('Export as Markdown')
|
|
324
|
+
.option('-o, --output <file>', 'Output file')
|
|
325
|
+
.action(async (options) => {
|
|
326
|
+
try {
|
|
327
|
+
await cli.init();
|
|
328
|
+
const result = await cli.exportMemory('markdown');
|
|
329
|
+
if (options.output) {
|
|
330
|
+
fs.writeFileSync(options.output, result.data);
|
|
331
|
+
console.log(chalk_1.default.green(`✓ Exported to ${options.output}`));
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
console.log(result.data);
|
|
335
|
+
}
|
|
336
|
+
await cli.close();
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
handleError(error);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
exportCmd
|
|
343
|
+
.command('obsidian')
|
|
344
|
+
.description('Export as Obsidian ZIP')
|
|
345
|
+
.requiredOption('-o, --output <file>', 'Output ZIP file')
|
|
346
|
+
.action(async (options) => {
|
|
347
|
+
try {
|
|
348
|
+
await cli.init();
|
|
349
|
+
const result = await cli.exportMemory('obsidian');
|
|
350
|
+
// Obsidian export returns zipBuffer, not data
|
|
351
|
+
const buffer = result.zipBuffer || result.data;
|
|
352
|
+
if (!buffer) {
|
|
353
|
+
throw new Error('No buffer returned from export');
|
|
354
|
+
}
|
|
355
|
+
fs.writeFileSync(options.output, buffer);
|
|
356
|
+
console.log(chalk_1.default.green(`✓ Exported to ${options.output}`));
|
|
357
|
+
await cli.close();
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
handleError(error);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
const importCmd = program.command('import').description('Import memory');
|
|
364
|
+
importCmd
|
|
365
|
+
.command('file')
|
|
366
|
+
.description('Import from file')
|
|
367
|
+
.requiredOption('-i, --input <file>', 'Input file')
|
|
368
|
+
.requiredOption('-f, --format <format>', 'Source format (cozo|mem0|memgpt|markdown)')
|
|
369
|
+
.option('-s, --strategy <strategy>', 'Merge strategy (skip|overwrite|merge)', 'skip')
|
|
370
|
+
.action(async (options) => {
|
|
371
|
+
try {
|
|
372
|
+
await cli.init();
|
|
373
|
+
const data = fs.readFileSync(options.input, 'utf-8');
|
|
374
|
+
const result = await cli.importMemory(data, options.format, {
|
|
375
|
+
mergeStrategy: options.strategy
|
|
376
|
+
});
|
|
377
|
+
formatOutput(result, 'pretty');
|
|
378
|
+
await cli.close();
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
handleError(error);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
// Ingest commands
|
|
385
|
+
const ingest = program.command('ingest').description('Ingest files');
|
|
386
|
+
ingest
|
|
387
|
+
.command('file')
|
|
388
|
+
.description('Ingest file into entity')
|
|
389
|
+
.requiredOption('-i, --entity-id <id>', 'Entity ID')
|
|
390
|
+
.requiredOption('-p, --path <path>', 'File path')
|
|
391
|
+
.requiredOption('-f, --format <format>', 'File format (markdown|json|pdf)')
|
|
392
|
+
.option('-c, --chunking <type>', 'Chunking type (none|paragraphs)', 'paragraphs')
|
|
393
|
+
.option('-m, --max <number>', 'Max observations', parseInt)
|
|
394
|
+
.option('--no-deduplicate', 'Disable deduplication')
|
|
395
|
+
.action(async (options) => {
|
|
396
|
+
try {
|
|
397
|
+
await cli.init();
|
|
398
|
+
const result = await cli.ingestFile(options.entityId, options.format, options.path, undefined, {
|
|
399
|
+
chunking: options.chunking,
|
|
400
|
+
maxObservations: options.max,
|
|
401
|
+
deduplicate: options.deduplicate
|
|
402
|
+
});
|
|
403
|
+
formatOutput(result, 'pretty');
|
|
404
|
+
await cli.close();
|
|
405
|
+
}
|
|
406
|
+
catch (error) {
|
|
407
|
+
handleError(error);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
// User Profile commands
|
|
411
|
+
const profile = program.command('profile').description('User profile management');
|
|
412
|
+
profile
|
|
413
|
+
.command('show')
|
|
414
|
+
.description('Show current user profile')
|
|
415
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
416
|
+
.action(async (options) => {
|
|
417
|
+
try {
|
|
418
|
+
await cli.init();
|
|
419
|
+
const result = await cli.getUserProfile();
|
|
420
|
+
formatOutput(result, options.format);
|
|
421
|
+
await cli.close();
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
handleError(error);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
profile
|
|
428
|
+
.command('update')
|
|
429
|
+
.description('Update user profile metadata')
|
|
430
|
+
.option('-n, --name <name>', 'Profile name')
|
|
431
|
+
.option('-t, --type <type>', 'Profile type')
|
|
432
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
433
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
434
|
+
.action(async (options) => {
|
|
435
|
+
try {
|
|
436
|
+
await cli.init();
|
|
437
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
438
|
+
const result = await cli.editUserProfile({
|
|
439
|
+
name: options.name,
|
|
440
|
+
type: options.type,
|
|
441
|
+
metadata
|
|
442
|
+
});
|
|
443
|
+
formatOutput(result, options.format);
|
|
444
|
+
await cli.close();
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
handleError(error);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
profile
|
|
451
|
+
.command('add-preference')
|
|
452
|
+
.description('Add preference to user profile')
|
|
453
|
+
.requiredOption('-t, --text <text>', 'Preference text')
|
|
454
|
+
.option('-m, --metadata <json>', 'Metadata as JSON string')
|
|
455
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
456
|
+
.action(async (options) => {
|
|
457
|
+
try {
|
|
458
|
+
await cli.init();
|
|
459
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : undefined;
|
|
460
|
+
const result = await cli.editUserProfile({
|
|
461
|
+
observations: [{ text: options.text, metadata }]
|
|
462
|
+
});
|
|
463
|
+
formatOutput(result, options.format);
|
|
464
|
+
await cli.close();
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
handleError(error);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
profile
|
|
471
|
+
.command('reset')
|
|
472
|
+
.description('Clear all preferences and optionally set new ones')
|
|
473
|
+
.option('-t, --text <text>', 'New preference text (can be used multiple times)')
|
|
474
|
+
.option('-f, --format <format>', 'Output format (json|pretty)', 'pretty')
|
|
475
|
+
.action(async (options) => {
|
|
476
|
+
try {
|
|
477
|
+
await cli.init();
|
|
478
|
+
const observations = options.text ? [{ text: options.text }] : [];
|
|
479
|
+
const result = await cli.editUserProfile({
|
|
480
|
+
clear_observations: true,
|
|
481
|
+
observations
|
|
482
|
+
});
|
|
483
|
+
formatOutput(result, options.format);
|
|
484
|
+
await cli.close();
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
handleError(error);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
program.parse();
|
|
@@ -108,7 +108,7 @@ class EmbeddingService {
|
|
|
108
108
|
"onnx-community/Qwen3-Embedding-0.6B-ONNX": 1024,
|
|
109
109
|
};
|
|
110
110
|
this.dimensions = dimensionMap[this.modelId] || 1024;
|
|
111
|
-
console.
|
|
111
|
+
console.error(`[EmbeddingService] Using model: ${this.modelId} (${this.dimensions} dimensions)`);
|
|
112
112
|
}
|
|
113
113
|
// Public getter for dimensions
|
|
114
114
|
getDimensions() {
|
package/dist/hybrid-search.js
CHANGED
|
@@ -184,7 +184,7 @@ class HybridSearch {
|
|
|
184
184
|
console.error('--- DEBUG: Cozo Datalog Query ---');
|
|
185
185
|
console.error(datalogQuery);
|
|
186
186
|
console.error('--- DEBUG: Params ---');
|
|
187
|
-
console.
|
|
187
|
+
console.error(JSON.stringify(params, null, 2));
|
|
188
188
|
try {
|
|
189
189
|
const results = await this.db.run(datalogQuery, params);
|
|
190
190
|
let searchResults = results.rows.map((r) => ({
|
|
@@ -234,8 +234,13 @@ class HybridSearch {
|
|
|
234
234
|
const { limit: queryLimit = 10, filters, graphConstraints, vectorParams } = options;
|
|
235
235
|
// @ts-ignore
|
|
236
236
|
const { topk = 5, efSearch = 50 } = vectorParams || {};
|
|
237
|
-
//
|
|
238
|
-
return
|
|
237
|
+
// Use advancedSearch as the default implementation
|
|
238
|
+
return this.advancedSearch({
|
|
239
|
+
...options,
|
|
240
|
+
vectorParams: {
|
|
241
|
+
efSearch: 100
|
|
242
|
+
}
|
|
243
|
+
});
|
|
239
244
|
}
|
|
240
245
|
async graphRag(options) {
|
|
241
246
|
console.error("[HybridSearch] Starting graphRag with options:", JSON.stringify(options, null, 2));
|
package/dist/index.js
CHANGED
|
@@ -1032,7 +1032,8 @@ class MemoryServer {
|
|
|
1032
1032
|
[$id, [${now}, true], $name, $type, $embedding, $name_embedding, $metadata]
|
|
1033
1033
|
] :insert entity {id, created_at => name, type, embedding, name_embedding, metadata}
|
|
1034
1034
|
`, { id, name, type, embedding, name_embedding, metadata: metadata || {} });
|
|
1035
|
-
|
|
1035
|
+
const created_at_iso = new Date(Math.floor(now / 1000)).toISOString();
|
|
1036
|
+
return { id, name, type, created_at: now, created_at_iso, status: "Entity created" };
|
|
1036
1037
|
}
|
|
1037
1038
|
async initUserProfile() {
|
|
1038
1039
|
try {
|
|
@@ -1191,9 +1192,12 @@ class MemoryServer {
|
|
|
1191
1192
|
// Optional: Automatic inference after new observation (in background)
|
|
1192
1193
|
const suggestionsRaw = await this.inferenceEngine.inferRelations(entityId);
|
|
1193
1194
|
const suggestions = await this.formatInferredRelationsForContext(suggestionsRaw);
|
|
1195
|
+
const created_at_iso = new Date(Math.floor(now / 1000)).toISOString();
|
|
1194
1196
|
return {
|
|
1195
1197
|
id,
|
|
1196
1198
|
entity_id: entityId,
|
|
1199
|
+
created_at: now,
|
|
1200
|
+
created_at_iso,
|
|
1197
1201
|
status: "Observation saved",
|
|
1198
1202
|
inferred_suggestions: suggestions
|
|
1199
1203
|
};
|
|
@@ -1247,7 +1251,15 @@ class MemoryServer {
|
|
|
1247
1251
|
strength: args.strength ?? 1.0,
|
|
1248
1252
|
metadata: args.metadata || {}
|
|
1249
1253
|
});
|
|
1250
|
-
|
|
1254
|
+
const created_at_iso = new Date(Math.floor(now / 1000)).toISOString();
|
|
1255
|
+
return {
|
|
1256
|
+
from_id: args.from_id,
|
|
1257
|
+
to_id: args.to_id,
|
|
1258
|
+
relation_type: args.relation_type,
|
|
1259
|
+
created_at: now,
|
|
1260
|
+
created_at_iso,
|
|
1261
|
+
status: "Relationship created"
|
|
1262
|
+
};
|
|
1251
1263
|
}
|
|
1252
1264
|
async exploreGraph(args) {
|
|
1253
1265
|
await this.initPromise;
|
|
@@ -2161,6 +2173,64 @@ ids[id] <- $ids
|
|
|
2161
2173
|
defaultEntityType: args.defaultEntityType
|
|
2162
2174
|
});
|
|
2163
2175
|
}
|
|
2176
|
+
async editUserProfile(args) {
|
|
2177
|
+
try {
|
|
2178
|
+
const current = await this.db.run('?[name, type, metadata] := *entity{id: $id, name, type, metadata, @ "NOW"}', { id: exports.USER_ENTITY_ID });
|
|
2179
|
+
if (current.rows.length === 0) {
|
|
2180
|
+
return { error: "User profile not found. Initialize it first." };
|
|
2181
|
+
}
|
|
2182
|
+
if (args.name || args.type || args.metadata) {
|
|
2183
|
+
const updateResult = await this.updateEntity({
|
|
2184
|
+
id: exports.USER_ENTITY_ID,
|
|
2185
|
+
name: args.name,
|
|
2186
|
+
type: args.type,
|
|
2187
|
+
metadata: args.metadata
|
|
2188
|
+
});
|
|
2189
|
+
if (updateResult.error) {
|
|
2190
|
+
return updateResult;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
if (args.clear_observations) {
|
|
2194
|
+
const existingObs = await this.db.run('?[id] := *observation{id, entity_id: $eid, @ "NOW"}', { eid: exports.USER_ENTITY_ID });
|
|
2195
|
+
for (const row of existingObs.rows) {
|
|
2196
|
+
await this.db.run('?[id, entity_id, text, embedding, metadata, created_at] := *observation{id, entity_id, text, embedding, metadata, created_at, @ "NOW"}, id = $id :delete observation {id, entity_id, text, embedding, metadata, created_at}', { id: row[0] });
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
if (args.observations && args.observations.length > 0) {
|
|
2200
|
+
for (const obs of args.observations) {
|
|
2201
|
+
await this.addObservation({
|
|
2202
|
+
entity_id: exports.USER_ENTITY_ID,
|
|
2203
|
+
text: obs.text,
|
|
2204
|
+
metadata: { ...obs.metadata, kind: "user_preference" },
|
|
2205
|
+
deduplicate: true
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
const updated = await this.db.run('?[name, type, metadata] := *entity{id: $id, name, type, metadata, @ "NOW"}', { id: exports.USER_ENTITY_ID });
|
|
2210
|
+
const observations = await this.db.run('?[id, text, metadata] := *observation{id, entity_id: $eid, text, metadata, @ "NOW"}', { eid: exports.USER_ENTITY_ID });
|
|
2211
|
+
return {
|
|
2212
|
+
status: "User profile updated",
|
|
2213
|
+
profile: {
|
|
2214
|
+
id: exports.USER_ENTITY_ID,
|
|
2215
|
+
name: updated.rows[0][0],
|
|
2216
|
+
type: updated.rows[0][1],
|
|
2217
|
+
metadata: updated.rows[0][2],
|
|
2218
|
+
observations: observations.rows.map((r) => ({
|
|
2219
|
+
id: r[0],
|
|
2220
|
+
text: r[1],
|
|
2221
|
+
metadata: r[2]
|
|
2222
|
+
}))
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
}
|
|
2226
|
+
catch (error) {
|
|
2227
|
+
console.error("[UserProfile] Error editing user profile:", error);
|
|
2228
|
+
return {
|
|
2229
|
+
error: "Failed to edit user profile",
|
|
2230
|
+
message: error.message || String(error)
|
|
2231
|
+
};
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2164
2234
|
registerTools() {
|
|
2165
2235
|
const MetadataSchema = zod_1.z.record(zod_1.z.string(), zod_1.z.any());
|
|
2166
2236
|
const MutateMemorySchema = zod_1.z.discriminatedUnion("action", [
|
|
@@ -3366,6 +3436,42 @@ Supported actions:
|
|
|
3366
3436
|
return JSON.stringify({ error: "Unknown action" });
|
|
3367
3437
|
},
|
|
3368
3438
|
});
|
|
3439
|
+
// User Profile Management Tool
|
|
3440
|
+
this.mcp.addTool({
|
|
3441
|
+
name: "edit_user_profile",
|
|
3442
|
+
description: `Direct management of the global user profile ('global_user_profile').
|
|
3443
|
+
This tool allows manual editing of user preferences, work style, and profile metadata.
|
|
3444
|
+
|
|
3445
|
+
The user profile is automatically boosted in all searches (50% score boost) and used for personalization.
|
|
3446
|
+
|
|
3447
|
+
Parameters:
|
|
3448
|
+
- name?: string - Update the profile name (default: "The User")
|
|
3449
|
+
- type?: string - Update the profile type (default: "User")
|
|
3450
|
+
- metadata?: object - Update or merge profile metadata
|
|
3451
|
+
- observations?: Array<{ text: string, metadata?: object }> - Add new preference observations
|
|
3452
|
+
- clear_observations?: boolean - Remove all existing observations before adding new ones
|
|
3453
|
+
|
|
3454
|
+
Examples:
|
|
3455
|
+
- Add preferences: { observations: [{ text: "Prefers TypeScript over JavaScript" }] }
|
|
3456
|
+
- Update metadata: { metadata: { timezone: "Europe/Berlin", language: "de" } }
|
|
3457
|
+
- Reset and set new preferences: { clear_observations: true, observations: [{ text: "New preference" }] }
|
|
3458
|
+
|
|
3459
|
+
Note: Use 'mutate_memory' with action='add_observation' and entity_id='global_user_profile' for implicit preference updates.`,
|
|
3460
|
+
parameters: zod_1.z.object({
|
|
3461
|
+
name: zod_1.z.string().optional().describe("New name for the user profile"),
|
|
3462
|
+
type: zod_1.z.string().optional().describe("New type for the user profile"),
|
|
3463
|
+
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional().describe("Metadata to merge with existing metadata"),
|
|
3464
|
+
observations: zod_1.z.array(zod_1.z.object({
|
|
3465
|
+
text: zod_1.z.string().describe("Preference or work style description"),
|
|
3466
|
+
metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.any()).optional().describe("Optional metadata for this observation")
|
|
3467
|
+
})).optional().describe("New observations to add to the profile"),
|
|
3468
|
+
clear_observations: zod_1.z.boolean().optional().default(false).describe("Clear all existing observations before adding new ones")
|
|
3469
|
+
}),
|
|
3470
|
+
execute: async (args) => {
|
|
3471
|
+
await this.initPromise;
|
|
3472
|
+
return JSON.stringify(await this.editUserProfile(args));
|
|
3473
|
+
}
|
|
3474
|
+
});
|
|
3369
3475
|
}
|
|
3370
3476
|
async start() {
|
|
3371
3477
|
await this.mcp.start({ transportType: "stdio" });
|