@199-bio/engram 0.6.1 → 0.7.2
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/index.js +121 -14
- package/dist/retrieval/hybrid.d.ts.map +1 -1
- package/dist/storage/database.d.ts.map +1 -1
- package/dist/web/chat-handler.d.ts.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/consolidation/consolidator.ts +2 -2
- package/src/index.ts +131 -14
- package/src/retrieval/hybrid.ts +32 -8
- package/src/storage/database.ts +19 -7
- package/src/web/chat-handler.ts +337 -3
- package/src/web/server.ts +166 -9
- package/src/web/static/app.js +201 -32
- package/src/web/static/index.html +4 -0
- package/src/web/static/style.css +147 -0
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { EngramDatabase } from "./storage/database.js";
|
|
|
14
14
|
import { KnowledgeGraph } from "./graph/knowledge-graph.js";
|
|
15
15
|
import { createRetriever } from "./retrieval/colbert.js";
|
|
16
16
|
import { HybridSearch } from "./retrieval/hybrid.js";
|
|
17
|
-
import { EngramWebServer } from "./web/server.js";
|
|
17
|
+
import { EngramWebServer, getRunningServerUrl } from "./web/server.js";
|
|
18
18
|
import { Consolidator } from "./consolidation/consolidator.js";
|
|
19
19
|
// ============ Configuration ============
|
|
20
20
|
const DB_PATH = process.env.ENGRAM_DB_PATH
|
|
@@ -44,11 +44,21 @@ async function initialize() {
|
|
|
44
44
|
if (consolidator.isConfigured()) {
|
|
45
45
|
console.error(`[Engram] Consolidation enabled (ANTHROPIC_API_KEY found)`);
|
|
46
46
|
}
|
|
47
|
+
// Start web server automatically (unless another instance is already running)
|
|
48
|
+
const existingUrl = getRunningServerUrl();
|
|
49
|
+
if (!existingUrl) {
|
|
50
|
+
webServer = new EngramWebServer({ db, graph, search });
|
|
51
|
+
const url = await webServer.start();
|
|
52
|
+
console.error(`[Engram] Web interface: ${url}`);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.error(`[Engram] Web interface already running: ${existingUrl}`);
|
|
56
|
+
}
|
|
47
57
|
}
|
|
48
58
|
// ============ MCP Server ============
|
|
49
59
|
const server = new Server({
|
|
50
60
|
name: "engram",
|
|
51
|
-
version: "0.
|
|
61
|
+
version: "0.7.2",
|
|
52
62
|
}, {
|
|
53
63
|
capabilities: {
|
|
54
64
|
tools: {},
|
|
@@ -59,7 +69,7 @@ const server = new Server({
|
|
|
59
69
|
const TOOLS = [
|
|
60
70
|
{
|
|
61
71
|
name: "remember",
|
|
62
|
-
description: "Store information
|
|
72
|
+
description: "Store NEW information the user shares. Extract entities (people, organizations, places) and relationships. Do NOT use for corrections - use edit_memory instead. Do NOT use before checking if info already exists - use recall first.",
|
|
63
73
|
inputSchema: {
|
|
64
74
|
type: "object",
|
|
65
75
|
properties: {
|
|
@@ -69,14 +79,14 @@ const TOOLS = [
|
|
|
69
79
|
},
|
|
70
80
|
importance: {
|
|
71
81
|
type: "number",
|
|
72
|
-
description: "0-1 score
|
|
82
|
+
description: "0-1 score with anchors: 0.9=core identity (name, birthday, family), 0.8=major preferences/life events, 0.6=notable facts, 0.5=general info (default), 0.3=casual mentions, 0.1=trivial/ephemeral",
|
|
73
83
|
minimum: 0,
|
|
74
84
|
maximum: 1,
|
|
75
85
|
default: 0.5,
|
|
76
86
|
},
|
|
77
87
|
emotional_weight: {
|
|
78
88
|
type: "number",
|
|
79
|
-
description: "0-1 emotional significance
|
|
89
|
+
description: "0-1 emotional significance with anchors: 0.9=major life events (births, deaths, achievements), 0.7=celebrations/disappointments, 0.5=neutral (default), 0.3=mild sentiment, 0.1=purely factual",
|
|
80
90
|
minimum: 0,
|
|
81
91
|
maximum: 1,
|
|
82
92
|
default: 0.5,
|
|
@@ -91,7 +101,7 @@ const TOOLS = [
|
|
|
91
101
|
items: {
|
|
92
102
|
type: "object",
|
|
93
103
|
properties: {
|
|
94
|
-
name: { type: "string", description: "Entity name (e.g., '
|
|
104
|
+
name: { type: "string", description: "Entity name (e.g., 'John Smith', 'Google', 'Paris')" },
|
|
95
105
|
type: { type: "string", enum: ["person", "organization", "place"], description: "Entity type" },
|
|
96
106
|
},
|
|
97
107
|
required: ["name", "type"],
|
|
@@ -123,7 +133,7 @@ const TOOLS = [
|
|
|
123
133
|
},
|
|
124
134
|
{
|
|
125
135
|
name: "recall",
|
|
126
|
-
description: "
|
|
136
|
+
description: "Search stored memories. Use FIRST before answering questions about the user, their preferences, history, or anything previously discussed. Also use before remember to check if information already exists.",
|
|
127
137
|
inputSchema: {
|
|
128
138
|
type: "object",
|
|
129
139
|
properties: {
|
|
@@ -154,13 +164,13 @@ const TOOLS = [
|
|
|
154
164
|
},
|
|
155
165
|
{
|
|
156
166
|
name: "forget",
|
|
157
|
-
description: "Delete a
|
|
167
|
+
description: "Delete a memory by its ID. Use when user explicitly asks to remove or forget specific stored information. Get the memory ID from recall results first.",
|
|
158
168
|
inputSchema: {
|
|
159
169
|
type: "object",
|
|
160
170
|
properties: {
|
|
161
171
|
id: {
|
|
162
172
|
type: "string",
|
|
163
|
-
description: "The memory ID to
|
|
173
|
+
description: "The memory ID to delete (get this from recall results)",
|
|
164
174
|
},
|
|
165
175
|
},
|
|
166
176
|
required: ["id"],
|
|
@@ -173,6 +183,37 @@ const TOOLS = [
|
|
|
173
183
|
openWorldHint: false,
|
|
174
184
|
},
|
|
175
185
|
},
|
|
186
|
+
{
|
|
187
|
+
name: "edit_memory",
|
|
188
|
+
description: "Correct or update an existing memory. Use instead of forget+remember when fixing mistakes, updating outdated info, or adjusting importance. Preserves the memory's history and relationships.",
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
id: {
|
|
193
|
+
type: "string",
|
|
194
|
+
description: "The memory ID to edit (get this from recall results)",
|
|
195
|
+
},
|
|
196
|
+
content: {
|
|
197
|
+
type: "string",
|
|
198
|
+
description: "New content for the memory (replaces existing). Omit to keep current content.",
|
|
199
|
+
},
|
|
200
|
+
importance: {
|
|
201
|
+
type: "number",
|
|
202
|
+
description: "New importance (0-1): 0.9=core identity, 0.8=major, 0.5=normal, 0.3=minor. Omit to keep current.",
|
|
203
|
+
minimum: 0,
|
|
204
|
+
maximum: 1,
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
required: ["id"],
|
|
208
|
+
},
|
|
209
|
+
annotations: {
|
|
210
|
+
title: "Edit Memory",
|
|
211
|
+
readOnlyHint: false,
|
|
212
|
+
destructiveHint: false,
|
|
213
|
+
idempotentHint: true,
|
|
214
|
+
openWorldHint: false,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
176
217
|
{
|
|
177
218
|
name: "engram_web",
|
|
178
219
|
description: "Launch the Engram web interface for browsing, searching, and editing memories visually. Returns a URL to open in your browser.",
|
|
@@ -196,7 +237,7 @@ const TOOLS = [
|
|
|
196
237
|
},
|
|
197
238
|
{
|
|
198
239
|
name: "consolidate",
|
|
199
|
-
description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Use periodically or when requested.",
|
|
240
|
+
description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Requires ANTHROPIC_API_KEY. Use periodically or when explicitly requested.",
|
|
200
241
|
inputSchema: {
|
|
201
242
|
type: "object",
|
|
202
243
|
properties: {
|
|
@@ -213,7 +254,7 @@ const TOOLS = [
|
|
|
213
254
|
readOnlyHint: false,
|
|
214
255
|
destructiveHint: false,
|
|
215
256
|
idempotentHint: false,
|
|
216
|
-
openWorldHint:
|
|
257
|
+
openWorldHint: true, // Calls Anthropic API for consolidation
|
|
217
258
|
},
|
|
218
259
|
},
|
|
219
260
|
];
|
|
@@ -317,19 +358,85 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
317
358
|
}
|
|
318
359
|
// Remove from semantic index
|
|
319
360
|
await search.removeFromIndex(id);
|
|
320
|
-
//
|
|
321
|
-
db.
|
|
361
|
+
// Soft-delete: mark as disabled instead of hard delete
|
|
362
|
+
db.updateMemory(id, { disabled: true });
|
|
322
363
|
return {
|
|
323
364
|
content: [
|
|
324
365
|
{
|
|
325
366
|
type: "text",
|
|
326
|
-
text: JSON.stringify({ success: true,
|
|
367
|
+
text: JSON.stringify({ success: true, disabled_id: id, message: "Memory disabled (soft-deleted)" }),
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
case "edit_memory": {
|
|
373
|
+
const { id, content, importance } = args;
|
|
374
|
+
const memory = db.getMemory(id);
|
|
375
|
+
if (!memory) {
|
|
376
|
+
return {
|
|
377
|
+
content: [
|
|
378
|
+
{
|
|
379
|
+
type: "text",
|
|
380
|
+
text: JSON.stringify({ success: false, error: "Memory not found" }),
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
// Build updates object
|
|
386
|
+
const updates = {};
|
|
387
|
+
if (content !== undefined)
|
|
388
|
+
updates.content = content;
|
|
389
|
+
if (importance !== undefined)
|
|
390
|
+
updates.importance = importance;
|
|
391
|
+
if (Object.keys(updates).length === 0) {
|
|
392
|
+
return {
|
|
393
|
+
content: [
|
|
394
|
+
{
|
|
395
|
+
type: "text",
|
|
396
|
+
text: JSON.stringify({ success: false, error: "No updates provided" }),
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
// Update the memory
|
|
402
|
+
const updated = db.updateMemory(id, updates);
|
|
403
|
+
// Re-index if content changed
|
|
404
|
+
if (content !== undefined && updated) {
|
|
405
|
+
await search.removeFromIndex(id);
|
|
406
|
+
await search.indexMemory(updated);
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
content: [
|
|
410
|
+
{
|
|
411
|
+
type: "text",
|
|
412
|
+
text: JSON.stringify({
|
|
413
|
+
success: true,
|
|
414
|
+
memory_id: id,
|
|
415
|
+
updated_fields: Object.keys(updates),
|
|
416
|
+
}),
|
|
327
417
|
},
|
|
328
418
|
],
|
|
329
419
|
};
|
|
330
420
|
}
|
|
331
421
|
case "engram_web": {
|
|
332
422
|
const { port = 3847 } = args;
|
|
423
|
+
// Check if a server is already running (from any MCP instance)
|
|
424
|
+
const existingUrl = getRunningServerUrl();
|
|
425
|
+
if (existingUrl) {
|
|
426
|
+
return {
|
|
427
|
+
content: [
|
|
428
|
+
{
|
|
429
|
+
type: "text",
|
|
430
|
+
text: JSON.stringify({
|
|
431
|
+
success: true,
|
|
432
|
+
url: existingUrl,
|
|
433
|
+
message: `Web interface already running at ${existingUrl}`,
|
|
434
|
+
reused: true,
|
|
435
|
+
}, null, 2),
|
|
436
|
+
},
|
|
437
|
+
],
|
|
438
|
+
};
|
|
439
|
+
}
|
|
333
440
|
// Create or reuse web server
|
|
334
441
|
if (!webServer) {
|
|
335
442
|
webServer = new EngramWebServer({ db, graph, search, port });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAwDD,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAyIhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B
|
|
1
|
+
{"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAwDD,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAyIhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAQrB;;;OAGG;YACW,WAAW;IA8BzB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;IAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../src/web/chat-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"chat-handler.d.ts","sourceRoot":"","sources":["../../src/web/chat-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAkB,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAmQtD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,YAAY,CAAoG;gBAE5G,OAAO,EAAE;QACnB,EAAE,EAAE,cAAc,CAAC;QACnB,KAAK,EAAE,cAAc,CAAC;QACtB,MAAM,EAAE,YAAY,CAAC;KACtB;IAWD,YAAY,IAAI,OAAO;IAIvB,MAAM,IAAI,OAAO;IAIjB,cAAc,IAAI,MAAM;IAIxB,YAAY,IAAI,IAAI;YAKN,YAAY;IAgBpB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzC,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC;YAsHrD,cAAc;YAgFd,WAAW;CAoQ1B"}
|
package/dist/web/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAwBtD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAmBnD;AAED,UAAU,gBAAgB;IACxB,EAAE,EAAE,cAAc,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,IAAI,CAAc;IAC1B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,IAAI,CAAS;gBAET,OAAO,EAAE,gBAAgB;IAa/B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwD9B,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;IActB,IAAI,IAAI,IAAI;YAQE,aAAa;YA+Bb,SAAS;YAoWT,WAAW;IAgCzB,OAAO,CAAC,SAAS;CAclB"}
|
package/package.json
CHANGED
|
@@ -74,8 +74,8 @@ const EPISODE_EXTRACTION_SYSTEM = `You are extracting structured memories from a
|
|
|
74
74
|
"importance": 0.5,
|
|
75
75
|
"emotional_weight": 0.5,
|
|
76
76
|
"event_time": "2024-12-01 or null if not mentioned",
|
|
77
|
-
"entities": [{"name": "
|
|
78
|
-
"relationships": [{"from": "
|
|
77
|
+
"entities": [{"name": "John", "type": "person"}],
|
|
78
|
+
"relationships": [{"from": "John", "to": "Acme Corp", "type": "works_at"}]
|
|
79
79
|
}
|
|
80
80
|
]
|
|
81
81
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { EngramDatabase } from "./storage/database.js";
|
|
|
19
19
|
import { KnowledgeGraph } from "./graph/knowledge-graph.js";
|
|
20
20
|
import { createRetriever } from "./retrieval/colbert.js";
|
|
21
21
|
import { HybridSearch } from "./retrieval/hybrid.js";
|
|
22
|
-
import { EngramWebServer } from "./web/server.js";
|
|
22
|
+
import { EngramWebServer, getRunningServerUrl } from "./web/server.js";
|
|
23
23
|
import { Consolidator } from "./consolidation/consolidator.js";
|
|
24
24
|
|
|
25
25
|
// ============ Configuration ============
|
|
@@ -59,6 +59,16 @@ async function initialize(): Promise<void> {
|
|
|
59
59
|
if (consolidator.isConfigured()) {
|
|
60
60
|
console.error(`[Engram] Consolidation enabled (ANTHROPIC_API_KEY found)`);
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
// Start web server automatically (unless another instance is already running)
|
|
64
|
+
const existingUrl = getRunningServerUrl();
|
|
65
|
+
if (!existingUrl) {
|
|
66
|
+
webServer = new EngramWebServer({ db, graph, search });
|
|
67
|
+
const url = await webServer.start();
|
|
68
|
+
console.error(`[Engram] Web interface: ${url}`);
|
|
69
|
+
} else {
|
|
70
|
+
console.error(`[Engram] Web interface already running: ${existingUrl}`);
|
|
71
|
+
}
|
|
62
72
|
}
|
|
63
73
|
|
|
64
74
|
// ============ MCP Server ============
|
|
@@ -66,7 +76,7 @@ async function initialize(): Promise<void> {
|
|
|
66
76
|
const server = new Server(
|
|
67
77
|
{
|
|
68
78
|
name: "engram",
|
|
69
|
-
version: "0.
|
|
79
|
+
version: "0.7.2",
|
|
70
80
|
},
|
|
71
81
|
{
|
|
72
82
|
capabilities: {
|
|
@@ -81,7 +91,7 @@ const TOOLS = [
|
|
|
81
91
|
{
|
|
82
92
|
name: "remember",
|
|
83
93
|
description:
|
|
84
|
-
"Store information
|
|
94
|
+
"Store NEW information the user shares. Extract entities (people, organizations, places) and relationships. Do NOT use for corrections - use edit_memory instead. Do NOT use before checking if info already exists - use recall first.",
|
|
85
95
|
inputSchema: {
|
|
86
96
|
type: "object" as const,
|
|
87
97
|
properties: {
|
|
@@ -91,14 +101,14 @@ const TOOLS = [
|
|
|
91
101
|
},
|
|
92
102
|
importance: {
|
|
93
103
|
type: "number",
|
|
94
|
-
description: "0-1 score
|
|
104
|
+
description: "0-1 score with anchors: 0.9=core identity (name, birthday, family), 0.8=major preferences/life events, 0.6=notable facts, 0.5=general info (default), 0.3=casual mentions, 0.1=trivial/ephemeral",
|
|
95
105
|
minimum: 0,
|
|
96
106
|
maximum: 1,
|
|
97
107
|
default: 0.5,
|
|
98
108
|
},
|
|
99
109
|
emotional_weight: {
|
|
100
110
|
type: "number",
|
|
101
|
-
description: "0-1 emotional significance
|
|
111
|
+
description: "0-1 emotional significance with anchors: 0.9=major life events (births, deaths, achievements), 0.7=celebrations/disappointments, 0.5=neutral (default), 0.3=mild sentiment, 0.1=purely factual",
|
|
102
112
|
minimum: 0,
|
|
103
113
|
maximum: 1,
|
|
104
114
|
default: 0.5,
|
|
@@ -113,7 +123,7 @@ const TOOLS = [
|
|
|
113
123
|
items: {
|
|
114
124
|
type: "object",
|
|
115
125
|
properties: {
|
|
116
|
-
name: { type: "string", description: "Entity name (e.g., '
|
|
126
|
+
name: { type: "string", description: "Entity name (e.g., 'John Smith', 'Google', 'Paris')" },
|
|
117
127
|
type: { type: "string", enum: ["person", "organization", "place"], description: "Entity type" },
|
|
118
128
|
},
|
|
119
129
|
required: ["name", "type"],
|
|
@@ -146,7 +156,7 @@ const TOOLS = [
|
|
|
146
156
|
{
|
|
147
157
|
name: "recall",
|
|
148
158
|
description:
|
|
149
|
-
"
|
|
159
|
+
"Search stored memories. Use FIRST before answering questions about the user, their preferences, history, or anything previously discussed. Also use before remember to check if information already exists.",
|
|
150
160
|
inputSchema: {
|
|
151
161
|
type: "object" as const,
|
|
152
162
|
properties: {
|
|
@@ -177,13 +187,13 @@ const TOOLS = [
|
|
|
177
187
|
},
|
|
178
188
|
{
|
|
179
189
|
name: "forget",
|
|
180
|
-
description: "Delete a
|
|
190
|
+
description: "Delete a memory by its ID. Use when user explicitly asks to remove or forget specific stored information. Get the memory ID from recall results first.",
|
|
181
191
|
inputSchema: {
|
|
182
192
|
type: "object" as const,
|
|
183
193
|
properties: {
|
|
184
194
|
id: {
|
|
185
195
|
type: "string",
|
|
186
|
-
description: "The memory ID to
|
|
196
|
+
description: "The memory ID to delete (get this from recall results)",
|
|
187
197
|
},
|
|
188
198
|
},
|
|
189
199
|
required: ["id"],
|
|
@@ -196,6 +206,37 @@ const TOOLS = [
|
|
|
196
206
|
openWorldHint: false,
|
|
197
207
|
},
|
|
198
208
|
},
|
|
209
|
+
{
|
|
210
|
+
name: "edit_memory",
|
|
211
|
+
description: "Correct or update an existing memory. Use instead of forget+remember when fixing mistakes, updating outdated info, or adjusting importance. Preserves the memory's history and relationships.",
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: "object" as const,
|
|
214
|
+
properties: {
|
|
215
|
+
id: {
|
|
216
|
+
type: "string",
|
|
217
|
+
description: "The memory ID to edit (get this from recall results)",
|
|
218
|
+
},
|
|
219
|
+
content: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "New content for the memory (replaces existing). Omit to keep current content.",
|
|
222
|
+
},
|
|
223
|
+
importance: {
|
|
224
|
+
type: "number",
|
|
225
|
+
description: "New importance (0-1): 0.9=core identity, 0.8=major, 0.5=normal, 0.3=minor. Omit to keep current.",
|
|
226
|
+
minimum: 0,
|
|
227
|
+
maximum: 1,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
required: ["id"],
|
|
231
|
+
},
|
|
232
|
+
annotations: {
|
|
233
|
+
title: "Edit Memory",
|
|
234
|
+
readOnlyHint: false,
|
|
235
|
+
destructiveHint: false,
|
|
236
|
+
idempotentHint: true,
|
|
237
|
+
openWorldHint: false,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
199
240
|
{
|
|
200
241
|
name: "engram_web",
|
|
201
242
|
description: "Launch the Engram web interface for browsing, searching, and editing memories visually. Returns a URL to open in your browser.",
|
|
@@ -219,7 +260,7 @@ const TOOLS = [
|
|
|
219
260
|
},
|
|
220
261
|
{
|
|
221
262
|
name: "consolidate",
|
|
222
|
-
description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Use periodically or when requested.",
|
|
263
|
+
description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Requires ANTHROPIC_API_KEY. Use periodically or when explicitly requested.",
|
|
223
264
|
inputSchema: {
|
|
224
265
|
type: "object" as const,
|
|
225
266
|
properties: {
|
|
@@ -236,7 +277,7 @@ const TOOLS = [
|
|
|
236
277
|
readOnlyHint: false,
|
|
237
278
|
destructiveHint: false,
|
|
238
279
|
idempotentHint: false,
|
|
239
|
-
openWorldHint:
|
|
280
|
+
openWorldHint: true, // Calls Anthropic API for consolidation
|
|
240
281
|
},
|
|
241
282
|
},
|
|
242
283
|
];
|
|
@@ -375,14 +416,72 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
375
416
|
// Remove from semantic index
|
|
376
417
|
await search.removeFromIndex(id);
|
|
377
418
|
|
|
378
|
-
//
|
|
379
|
-
db.
|
|
419
|
+
// Soft-delete: mark as disabled instead of hard delete
|
|
420
|
+
db.updateMemory(id, { disabled: true });
|
|
380
421
|
|
|
381
422
|
return {
|
|
382
423
|
content: [
|
|
383
424
|
{
|
|
384
425
|
type: "text" as const,
|
|
385
|
-
text: JSON.stringify({ success: true,
|
|
426
|
+
text: JSON.stringify({ success: true, disabled_id: id, message: "Memory disabled (soft-deleted)" }),
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
case "edit_memory": {
|
|
433
|
+
const { id, content, importance } = args as {
|
|
434
|
+
id: string;
|
|
435
|
+
content?: string;
|
|
436
|
+
importance?: number;
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const memory = db.getMemory(id);
|
|
440
|
+
if (!memory) {
|
|
441
|
+
return {
|
|
442
|
+
content: [
|
|
443
|
+
{
|
|
444
|
+
type: "text" as const,
|
|
445
|
+
text: JSON.stringify({ success: false, error: "Memory not found" }),
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Build updates object
|
|
452
|
+
const updates: { content?: string; importance?: number } = {};
|
|
453
|
+
if (content !== undefined) updates.content = content;
|
|
454
|
+
if (importance !== undefined) updates.importance = importance;
|
|
455
|
+
|
|
456
|
+
if (Object.keys(updates).length === 0) {
|
|
457
|
+
return {
|
|
458
|
+
content: [
|
|
459
|
+
{
|
|
460
|
+
type: "text" as const,
|
|
461
|
+
text: JSON.stringify({ success: false, error: "No updates provided" }),
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Update the memory
|
|
468
|
+
const updated = db.updateMemory(id, updates);
|
|
469
|
+
|
|
470
|
+
// Re-index if content changed
|
|
471
|
+
if (content !== undefined && updated) {
|
|
472
|
+
await search.removeFromIndex(id);
|
|
473
|
+
await search.indexMemory(updated);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
content: [
|
|
478
|
+
{
|
|
479
|
+
type: "text" as const,
|
|
480
|
+
text: JSON.stringify({
|
|
481
|
+
success: true,
|
|
482
|
+
memory_id: id,
|
|
483
|
+
updated_fields: Object.keys(updates),
|
|
484
|
+
}),
|
|
386
485
|
},
|
|
387
486
|
],
|
|
388
487
|
};
|
|
@@ -391,6 +490,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
391
490
|
case "engram_web": {
|
|
392
491
|
const { port = 3847 } = args as { port?: number };
|
|
393
492
|
|
|
493
|
+
// Check if a server is already running (from any MCP instance)
|
|
494
|
+
const existingUrl = getRunningServerUrl();
|
|
495
|
+
if (existingUrl) {
|
|
496
|
+
return {
|
|
497
|
+
content: [
|
|
498
|
+
{
|
|
499
|
+
type: "text" as const,
|
|
500
|
+
text: JSON.stringify({
|
|
501
|
+
success: true,
|
|
502
|
+
url: existingUrl,
|
|
503
|
+
message: `Web interface already running at ${existingUrl}`,
|
|
504
|
+
reused: true,
|
|
505
|
+
}, null, 2),
|
|
506
|
+
},
|
|
507
|
+
],
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
394
511
|
// Create or reuse web server
|
|
395
512
|
if (!webServer) {
|
|
396
513
|
webServer = new EngramWebServer({ db, graph, search, port });
|
package/src/retrieval/hybrid.ts
CHANGED
|
@@ -102,7 +102,7 @@ export class HybridSearch {
|
|
|
102
102
|
includeGraph = true,
|
|
103
103
|
bm25Weight = 1.0,
|
|
104
104
|
semanticWeight = 1.0,
|
|
105
|
-
graphWeight = 0.
|
|
105
|
+
graphWeight = 0.3,
|
|
106
106
|
useReranking = true, // Default: reranking mode for better quality
|
|
107
107
|
} = options;
|
|
108
108
|
|
|
@@ -257,22 +257,46 @@ export class HybridSearch {
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Normalize a word for matching: lowercase, strip punctuation, handle possessives
|
|
262
|
+
* "John's" → "john", "Paris!" → "paris", "U.S.A." → "usa"
|
|
263
|
+
*/
|
|
264
|
+
private normalizeWord(word: string): string {
|
|
265
|
+
return word
|
|
266
|
+
.toLowerCase()
|
|
267
|
+
.replace(/'s$/, '') // Remove possessives: john's → john
|
|
268
|
+
.replace(/[^\p{L}\p{N}]/gu, '') // Keep only letters/numbers (Unicode-safe)
|
|
269
|
+
.trim();
|
|
270
|
+
}
|
|
271
|
+
|
|
260
272
|
/**
|
|
261
273
|
* Graph-based search: find known entities in query, traverse graph
|
|
274
|
+
* Uses normalized word matching to handle punctuation and possessives
|
|
262
275
|
*/
|
|
263
276
|
private async searchGraph(query: string): Promise<string[]> {
|
|
264
|
-
//
|
|
265
|
-
const
|
|
277
|
+
// Tokenize and normalize query words
|
|
278
|
+
const queryWords = query.split(/\s+/)
|
|
279
|
+
.map(w => this.normalizeWord(w))
|
|
280
|
+
.filter(w => w.length > 0);
|
|
281
|
+
const queryWordSet = new Set(queryWords);
|
|
282
|
+
|
|
283
|
+
// Find entities whose normalized names match query words
|
|
266
284
|
const allEntities = this.graph.listEntities(undefined, 500);
|
|
267
|
-
const matchedEntities = allEntities.filter(e =>
|
|
268
|
-
|
|
269
|
-
|
|
285
|
+
const matchedEntities = allEntities.filter(e => {
|
|
286
|
+
const entityWords = e.name.split(/\s+/)
|
|
287
|
+
.map(w => this.normalizeWord(w))
|
|
288
|
+
.filter(w => w.length > 0);
|
|
289
|
+
// Entity matches if ALL its words appear in the query
|
|
290
|
+
// e.g., "john" matches entity "John" but not "John Smith"
|
|
291
|
+
// e.g., "john's friend" matches entity "John" (possessive handled)
|
|
292
|
+
return entityWords.every(w => queryWordSet.has(w));
|
|
293
|
+
});
|
|
270
294
|
|
|
271
295
|
const memoryIds = new Set<string>();
|
|
272
296
|
|
|
273
297
|
for (const entity of matchedEntities) {
|
|
274
|
-
//
|
|
275
|
-
const relatedIds = this.graph.findRelatedMemoryIds(entity.name,
|
|
298
|
+
// Depth 1: only directly related memories, not transitive connections
|
|
299
|
+
const relatedIds = this.graph.findRelatedMemoryIds(entity.name, 1);
|
|
276
300
|
relatedIds.forEach(id => memoryIds.add(id));
|
|
277
301
|
}
|
|
278
302
|
|