@199-bio/engram 0.8.0 → 0.8.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/consolidation/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAU,MAAM,EAAW,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0FtD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAA6B;gBAGzC,EAAE,EAAE,cAAc,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,YAAY;IAYvB,YAAY,IAAI,OAAO;IAIvB;;;OAGG;IACG,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC;QAC3D,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IAwEF;;OAEG;YACW,gBAAgB;IAoE9B;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuHjE;;OAEG;IACH,SAAS,IAAI;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,CAAC;KAClC;IAeD;;;OAGG;IACG,mBAAmB,CAAC,OAAO,GAAE;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IAwFF;;OAEG;YACW,2BAA2B;IAqDzC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CA4BH"}
1
+ {"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/consolidation/consolidator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,cAAc,EAAU,MAAM,EAAW,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0FtD,UAAU,kBAAkB;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,EAAE,CAAiB;IAC3B,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAA6B;gBAGzC,EAAE,EAAE,cAAc,EAClB,KAAK,CAAC,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,YAAY;IAUvB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAmBpB,YAAY,IAAI,OAAO;IAKvB;;;OAGG;IACG,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC;QAC3D,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IAyEF;;OAEG;YACW,gBAAgB;IAqE9B;;OAEG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAwHjE;;OAEG;IACH,SAAS,IAAI;QACX,UAAU,EAAE,OAAO,CAAC;QACpB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,wBAAwB,EAAE,MAAM,CAAC;KAClC;IAeD;;;OAGG;IACG,mBAAmB,CAAC,OAAO,GAAE;QACjC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QACf,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IAyFF;;OAEG;YACW,2BAA2B;IAsDzC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC;QAC7B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC;CA4BH"}
package/dist/index.js CHANGED
@@ -10,6 +10,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
10
10
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
11
11
  import path from "path";
12
12
  import os from "os";
13
+ import fs from "fs";
13
14
  import { EngramDatabase } from "./storage/database.js";
14
15
  import { KnowledgeGraph } from "./graph/knowledge-graph.js";
15
16
  import { createRetriever } from "./retrieval/colbert.js";
@@ -21,6 +22,94 @@ const DB_PATH = process.env.ENGRAM_DB_PATH
21
22
  ? path.resolve(process.env.ENGRAM_DB_PATH.replace("~", os.homedir()))
22
23
  : path.join(os.homedir(), ".engram");
23
24
  const DB_FILE = path.join(DB_PATH, "engram.db");
25
+ const PID_FILE = path.join(DB_PATH, "engram.pid");
26
+ // ============ Zombie Prevention ============
27
+ /**
28
+ * Kill any existing engram process and clean up stale PID file
29
+ */
30
+ function cleanupZombies() {
31
+ try {
32
+ if (fs.existsSync(PID_FILE)) {
33
+ const oldPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
34
+ if (oldPid && oldPid !== process.pid) {
35
+ try {
36
+ // Check if process exists
37
+ process.kill(oldPid, 0);
38
+ // It exists, kill it
39
+ console.error(`[Engram] Killing old instance (PID ${oldPid})`);
40
+ process.kill(oldPid, "SIGTERM");
41
+ }
42
+ catch {
43
+ // Process doesn't exist, that's fine
44
+ }
45
+ }
46
+ fs.unlinkSync(PID_FILE);
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error("[Engram] Error cleaning up zombies:", error);
51
+ }
52
+ }
53
+ /**
54
+ * Write our PID file
55
+ */
56
+ function writePidFile() {
57
+ try {
58
+ // Ensure directory exists
59
+ if (!fs.existsSync(DB_PATH)) {
60
+ fs.mkdirSync(DB_PATH, { recursive: true });
61
+ }
62
+ fs.writeFileSync(PID_FILE, String(process.pid));
63
+ }
64
+ catch (error) {
65
+ console.error("[Engram] Error writing PID file:", error);
66
+ }
67
+ }
68
+ /**
69
+ * Clean up on exit
70
+ */
71
+ function cleanup() {
72
+ try {
73
+ if (fs.existsSync(PID_FILE)) {
74
+ const storedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
75
+ if (storedPid === process.pid) {
76
+ fs.unlinkSync(PID_FILE);
77
+ }
78
+ }
79
+ if (webServer) {
80
+ webServer.stop();
81
+ }
82
+ if (db) {
83
+ db.close();
84
+ }
85
+ }
86
+ catch {
87
+ // Ignore cleanup errors
88
+ }
89
+ }
90
+ // Register signal handlers early
91
+ process.on("SIGTERM", () => {
92
+ console.error("[Engram] Received SIGTERM, shutting down...");
93
+ cleanup();
94
+ process.exit(0);
95
+ });
96
+ process.on("SIGINT", () => {
97
+ console.error("[Engram] Received SIGINT, shutting down...");
98
+ cleanup();
99
+ process.exit(0);
100
+ });
101
+ process.on("exit", cleanup);
102
+ // Detect when parent process (Claude) dies by monitoring stdin
103
+ process.stdin.on("end", () => {
104
+ console.error("[Engram] stdin closed, parent process likely died. Shutting down...");
105
+ cleanup();
106
+ process.exit(0);
107
+ });
108
+ process.stdin.on("close", () => {
109
+ console.error("[Engram] stdin closed, shutting down...");
110
+ cleanup();
111
+ process.exit(0);
112
+ });
24
113
  // ============ Initialize Components ============
25
114
  let db;
26
115
  let graph;
@@ -540,9 +629,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
540
629
  }
541
630
  case "memory_feedback": {
542
631
  const { recall_id, useful_memory_ids, need_more = false } = args;
543
- // Update the retrieval log with feedback
544
- const updated = db.updateRetrievalFeedback(recall_id, useful_memory_ids, need_more);
545
- if (!updated) {
632
+ // First, get the original recall to validate useful_memory_ids
633
+ const retrievalLog = db.getRetrievalLog(recall_id);
634
+ if (!retrievalLog) {
546
635
  return {
547
636
  content: [
548
637
  {
@@ -555,6 +644,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
555
644
  ],
556
645
  };
557
646
  }
647
+ // Validate: only accept IDs that were in the original recall
648
+ const originalIdSet = new Set(retrievalLog.memory_ids);
649
+ const validUsefulIds = useful_memory_ids.filter(id => originalIdSet.has(id));
650
+ const invalidIds = useful_memory_ids.filter(id => !originalIdSet.has(id));
651
+ if (invalidIds.length > 0) {
652
+ console.error(`[Engram] memory_feedback: ${invalidIds.length} IDs not in original recall, ignored: ${invalidIds.join(", ")}`);
653
+ }
654
+ // Update the retrieval log with validated feedback
655
+ const updated = db.updateRetrievalFeedback(recall_id, validUsefulIds, need_more);
656
+ if (!updated) {
657
+ // Should not happen since we already checked above, but handle gracefully
658
+ console.error(`[Engram] memory_feedback: failed to update retrieval log ${recall_id}`);
659
+ return {
660
+ content: [
661
+ {
662
+ type: "text",
663
+ text: JSON.stringify({
664
+ success: false,
665
+ error: `Failed to update feedback for: ${recall_id}`,
666
+ }),
667
+ },
668
+ ],
669
+ };
670
+ }
558
671
  // If need_more is set, do an expanded search
559
672
  if (need_more) {
560
673
  const expandedResponse = await search.expandSearch(recall_id);
@@ -576,7 +689,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
576
689
  text: JSON.stringify({
577
690
  success: true,
578
691
  feedback_recorded: true,
579
- useful_count: useful_memory_ids.length,
692
+ useful_count: validUsefulIds.length,
580
693
  expanded_search: true,
581
694
  additional_results: formatted,
582
695
  additional_count: formatted.length,
@@ -605,7 +718,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
605
718
  text: JSON.stringify({
606
719
  success: true,
607
720
  feedback_recorded: true,
608
- useful_count: useful_memory_ids.length,
721
+ useful_count: validUsefulIds.length,
609
722
  learning_applied: learningApplied > 0,
610
723
  connections_strengthened: learningApplied,
611
724
  }, null, 2),
@@ -632,10 +745,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
632
745
  });
633
746
  // ============ Main ============
634
747
  async function main() {
748
+ // Kill any zombie instances before starting
749
+ cleanupZombies();
750
+ writePidFile();
635
751
  await initialize();
636
752
  const transport = new StdioServerTransport();
637
753
  await server.connect(transport);
638
- console.error("[Engram] MCP server running on stdio");
754
+ console.error(`[Engram] MCP server running on stdio (PID ${process.pid})`);
639
755
  }
640
756
  main().catch((error) => {
641
757
  console.error("[Engram] Fatal error:", error);
@@ -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;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,KAAK,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAwDD,qBAAa,YAAY;IAIrB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;IALnB,OAAO,CAAC,SAAS,CAAS;gBAGhB,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAMvD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,oBAAoB,CAAC;IAuNhC;;;OAGG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAO,GACzC,OAAO,CAAC,oBAAoB,CAAC;IAyBhC;;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
+ {"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;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,KAAK,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAwDD,qBAAa,YAAY;IAIrB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;IALnB,OAAO,CAAC,SAAS,CAAS;gBAGhB,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAMvD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,oBAAoB,CAAC;IA2NhC;;;OAGG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,MAAM,CAAA;KAAO,GACzC,OAAO,CAAC,oBAAoB,CAAC;IAyBhC;;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":"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"}
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;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,cAAc,CAAC;IAChE,UAAU,EAAE,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC3C,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,IAAI,CAAC;IACjB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,IAAI,CAAC;IACjB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,SAAS,CAA8C;gBAEnD,MAAM,EAAE,MAAM;IAoB1B,OAAO,CAAC,UAAU;IAuMlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAqBrB,YAAY,CACV,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAuB,EAC/B,UAAU,GAAE,MAAY,EACxB,OAAO,GAAE;QACP,SAAS,CAAC,EAAE,IAAI,CAAC;QACjB,eAAe,CAAC,EAAE,MAAM,CAAC;KACrB,GACL,MAAM;IAkBT,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKpC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,YAAY,GAAG,UAAU,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI;IAyB9G,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAMjC;;;OAGG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAW7B,cAAc,CAAC,KAAK,GAAE,MAAa,EAAE,eAAe,GAAE,OAAe,EAAE,MAAM,GAAE,MAAU,GAAG,MAAM,EAAE;IAUpG;;OAEG;IACH,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GAAG,WAAW,EAC1B,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO;IAcV,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI;IAKtC,OAAO,CAAC,gBAAgB;IAOxB;;OAEG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE;IAOhD;;OAEG;IACH,yBAAyB,CAAC,KAAK,GAAE,MAAY,GAAG,OAAO,EAAE;IAOzD;;OAEG;IACH,wBAAwB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI;IAOpD;;OAEG;IACH,iBAAiB,CAAC,KAAK,GAAE,MAAW,GAAG,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAgBhH,OAAO,CAAC,YAAY;IAcpB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,KAAK,CAAC,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAkBhF,OAAO,CAAC,eAAe;IAavB,YAAY,CACV,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EACpB,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAW,GAC9C,MAAM;IAUT,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKpC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAU7C;;;OAGG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAY,GAAG,MAAM,GAAG,IAAI;IA+BvE;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA4C/B;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG;QAAE,iBAAiB,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE;IAgCxG;;OAEG;IACH,qBAAqB,IAAI,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAoCjF,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE;IAgB9D,YAAY,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,GAAE,MAAY,GAAG,MAAM,EAAE;IAiBlE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAMjC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;KAAE,GAAG,MAAM,GAAG,IAAI;IAc1F,cAAc,CACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,MAAM,GAAG,IAAW,EACpC,UAAU,GAAE,MAAY,GACvB,WAAW;IAUd,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAK9C,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,GAAE,OAAe,GAAG,WAAW,EAAE;IAYvF,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAOnC,cAAc,CACZ,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAW,GAChD,QAAQ;IAUX,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAKxC,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,GAAG,IAAI,GAAG,MAAe,GAAG,QAAQ,EAAE;IAiB5F,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IActF,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQnC,QAAQ,CACN,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAU,EACjB,aAAa,CAAC,EAAE,MAAM,EAAE,GACvB;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,SAAS,EAAE,QAAQ,EAAE,CAAC;QAAC,YAAY,EAAE,WAAW,EAAE,CAAA;KAAE;IA2C7E,YAAY,CACV,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,IAAI,CAAC;QACnB,SAAS,CAAC,EAAE,IAAI,CAAC;KACb,GACL,MAAM;IA4BT,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAKpC,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAY,GAAG,MAAM,EAAE;IAgBzD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE;IAU5C,yBAAyB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,GAAE,MAAY,GAAG,MAAM,EAAE;IAoBtE,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQjC,mBAAmB,CACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,aAAa;IAUhB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IAKlD,iBAAiB,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,GAAE,MAAY,GAAG,aAAa,EAAE;IAgB3E,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAU7D,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQxC;;;OAGG;IACH,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAqBzE,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAKlD;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,GAAE,MAAU,GAAG,gBAAgB,EAAE;IASnF;;OAEG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,WAAW,GAAE,MAAY,EAAE,KAAK,GAAE,MAAW,GAAG,MAAM,EAAE;IAoBnG;;;OAGG;IACH,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IA0B5C;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI;IAmCzC;;OAEG;IACH,gBAAgB,CAAC,aAAa,GAAE,MAAW,EAAE,WAAW,GAAE,MAAY,GAAG,MAAM;IAkB/E;;OAEG;IACH,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EAAE,GAClB,YAAY;IASf,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAKtD;;OAEG;IACH,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAG,OAAO;IAS1F;;OAEG;IACH,2BAA2B,CAAC,KAAK,GAAE,MAAY,GAAG,YAAY,EAAE;IAUhE;;OAEG;IACH,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;IAQ/C;;OAEG;IACH,oBAAoB,CAAC,OAAO,GAAE,MAAU,GAAG,MAAM;IAUjD,QAAQ,IAAI;QACV,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,cAAc,EAAE,MAAM,CAAC;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,uBAAuB,EAAE,MAAM,CAAC;KACjC;IA4BD,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,CAAC,IAAI;IASZ,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,iBAAiB;CAa1B"}
@@ -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;AAoQtD,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;IASD;;OAEG;IACH,aAAa,IAAI,IAAI;IASrB,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"}
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;AAwQtD,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;IASD;;OAEG;IACH,aAAa,IAAI,IAAI;IAYrB,YAAY,IAAI,OAAO;IAMvB,MAAM,IAAI,OAAO;IAIjB,cAAc,IAAI,MAAM;IAIxB,YAAY,IAAI,IAAI;YAKN,YAAY;IAgBpB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBzC,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC;YA6GrD,cAAc;YAkFd,WAAW;CAoQ1B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@199-bio/engram",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Give Claude a perfect memory. Local-first MCP server with hybrid search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -112,6 +112,7 @@ interface ConsolidateOptions {
112
112
 
113
113
  export class Consolidator {
114
114
  private client: Anthropic | null = null;
115
+ private cachedApiKey: string | null = null;
115
116
  private db: EngramDatabase;
116
117
  private graph: KnowledgeGraph | null = null;
117
118
  private search: HybridSearch | null = null;
@@ -125,14 +126,36 @@ export class Consolidator {
125
126
  this.graph = graph || null;
126
127
  this.search = search || null;
127
128
 
129
+ // Initial check
130
+ this.ensureClient();
131
+ }
132
+
133
+ /**
134
+ * Ensure client is configured with latest API key
135
+ * Lazy initialization: checks for new/updated API key each call
136
+ */
137
+ private ensureClient(): Anthropic | null {
128
138
  const apiKey = getAnthropicApiKey();
129
- if (apiKey) {
139
+
140
+ if (!apiKey) {
141
+ this.client = null;
142
+ this.cachedApiKey = null;
143
+ return null;
144
+ }
145
+
146
+ // Only recreate client if API key changed
147
+ if (apiKey !== this.cachedApiKey) {
148
+ console.error(`[Engram] Consolidator: API key ${this.cachedApiKey ? "updated" : "configured"}`);
130
149
  this.client = new Anthropic({ apiKey });
150
+ this.cachedApiKey = apiKey;
131
151
  }
152
+
153
+ return this.client;
132
154
  }
133
155
 
134
156
  isConfigured(): boolean {
135
- return this.client !== null;
157
+ // Re-check in case API key was added after startup
158
+ return this.ensureClient() !== null;
136
159
  }
137
160
 
138
161
  /**
@@ -144,7 +167,8 @@ export class Consolidator {
144
167
  contradictionsFound: number;
145
168
  memoriesProcessed: number;
146
169
  }> {
147
- if (!this.client) {
170
+ const client = this.ensureClient();
171
+ if (!client) {
148
172
  throw new Error("Consolidator not configured - set ANTHROPIC_API_KEY");
149
173
  }
150
174
 
@@ -221,7 +245,8 @@ export class Consolidator {
221
245
  private async consolidateBatch(
222
246
  memories: Memory[]
223
247
  ): Promise<ConsolidationResult | null> {
224
- if (!this.client) return null;
248
+ const client = this.ensureClient();
249
+ if (!client) return null;
225
250
 
226
251
  // Format memories for the prompt
227
252
  const memoriesText = memories
@@ -246,7 +271,7 @@ ${memoriesText}
246
271
  Create a detailed digest that preserves all important information. Respond with JSON only.`;
247
272
 
248
273
  try {
249
- const response = await this.client.messages.create({
274
+ const response = await client.messages.create({
250
275
  model: "claude-opus-4-5-20251101",
251
276
  max_tokens: 16000,
252
277
  temperature: 1, // Required for extended thinking
@@ -290,7 +315,8 @@ Create a detailed digest that preserves all important information. Respond with
290
315
  * Create an entity profile by consolidating all observations about an entity
291
316
  */
292
317
  async consolidateEntity(entityId: string): Promise<Digest | null> {
293
- if (!this.client) {
318
+ const client = this.ensureClient();
319
+ if (!client) {
294
320
  throw new Error("Consolidator not configured - set ANTHROPIC_API_KEY");
295
321
  }
296
322
 
@@ -335,7 +361,7 @@ ${memoriesText}
335
361
  Create a rich, detailed profile. Do not summarize away important nuances. Respond with JSON only.`;
336
362
 
337
363
  try {
338
- const response = await this.client.messages.create({
364
+ const response = await client.messages.create({
339
365
  model: "claude-opus-4-5-20251101",
340
366
  max_tokens: 16000,
341
367
  temperature: 1, // Required for extended thinking
@@ -444,7 +470,8 @@ Create a rich, detailed profile. Do not summarize away important nuances. Respon
444
470
  memoriesCreated: number;
445
471
  entitiesCreated: number;
446
472
  }> {
447
- if (!this.client) {
473
+ const client = this.ensureClient();
474
+ if (!client) {
448
475
  throw new Error("Consolidator not configured - set ANTHROPIC_API_KEY");
449
476
  }
450
477
 
@@ -537,7 +564,8 @@ Create a rich, detailed profile. Do not summarize away important nuances. Respon
537
564
  private async extractMemoriesFromEpisodes(
538
565
  episodes: Episode[]
539
566
  ): Promise<EpisodeExtractionResult | null> {
540
- if (!this.client) return null;
567
+ const client = this.ensureClient();
568
+ if (!client) return null;
541
569
 
542
570
  // Format conversation
543
571
  const conversationText = episodes
@@ -555,7 +583,7 @@ Respond with JSON only.`;
555
583
 
556
584
  try {
557
585
  // Use Haiku for speed/cost (no extended thinking needed)
558
- const response = await this.client.messages.create({
586
+ const response = await client.messages.create({
559
587
  model: "claude-haiku-4-5-20251201",
560
588
  max_tokens: 4000,
561
589
  messages: [
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from "@modelcontextprotocol/sdk/types.js";
15
15
  import path from "path";
16
16
  import os from "os";
17
+ import fs from "fs";
17
18
 
18
19
  import { EngramDatabase } from "./storage/database.js";
19
20
  import { KnowledgeGraph } from "./graph/knowledge-graph.js";
@@ -29,6 +30,99 @@ const DB_PATH = process.env.ENGRAM_DB_PATH
29
30
  : path.join(os.homedir(), ".engram");
30
31
 
31
32
  const DB_FILE = path.join(DB_PATH, "engram.db");
33
+ const PID_FILE = path.join(DB_PATH, "engram.pid");
34
+
35
+ // ============ Zombie Prevention ============
36
+
37
+ /**
38
+ * Kill any existing engram process and clean up stale PID file
39
+ */
40
+ function cleanupZombies(): void {
41
+ try {
42
+ if (fs.existsSync(PID_FILE)) {
43
+ const oldPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
44
+ if (oldPid && oldPid !== process.pid) {
45
+ try {
46
+ // Check if process exists
47
+ process.kill(oldPid, 0);
48
+ // It exists, kill it
49
+ console.error(`[Engram] Killing old instance (PID ${oldPid})`);
50
+ process.kill(oldPid, "SIGTERM");
51
+ } catch {
52
+ // Process doesn't exist, that's fine
53
+ }
54
+ }
55
+ fs.unlinkSync(PID_FILE);
56
+ }
57
+ } catch (error) {
58
+ console.error("[Engram] Error cleaning up zombies:", error);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Write our PID file
64
+ */
65
+ function writePidFile(): void {
66
+ try {
67
+ // Ensure directory exists
68
+ if (!fs.existsSync(DB_PATH)) {
69
+ fs.mkdirSync(DB_PATH, { recursive: true });
70
+ }
71
+ fs.writeFileSync(PID_FILE, String(process.pid));
72
+ } catch (error) {
73
+ console.error("[Engram] Error writing PID file:", error);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Clean up on exit
79
+ */
80
+ function cleanup(): void {
81
+ try {
82
+ if (fs.existsSync(PID_FILE)) {
83
+ const storedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
84
+ if (storedPid === process.pid) {
85
+ fs.unlinkSync(PID_FILE);
86
+ }
87
+ }
88
+ if (webServer) {
89
+ webServer.stop();
90
+ }
91
+ if (db) {
92
+ db.close();
93
+ }
94
+ } catch {
95
+ // Ignore cleanup errors
96
+ }
97
+ }
98
+
99
+ // Register signal handlers early
100
+ process.on("SIGTERM", () => {
101
+ console.error("[Engram] Received SIGTERM, shutting down...");
102
+ cleanup();
103
+ process.exit(0);
104
+ });
105
+
106
+ process.on("SIGINT", () => {
107
+ console.error("[Engram] Received SIGINT, shutting down...");
108
+ cleanup();
109
+ process.exit(0);
110
+ });
111
+
112
+ process.on("exit", cleanup);
113
+
114
+ // Detect when parent process (Claude) dies by monitoring stdin
115
+ process.stdin.on("end", () => {
116
+ console.error("[Engram] stdin closed, parent process likely died. Shutting down...");
117
+ cleanup();
118
+ process.exit(0);
119
+ });
120
+
121
+ process.stdin.on("close", () => {
122
+ console.error("[Engram] stdin closed, shutting down...");
123
+ cleanup();
124
+ process.exit(0);
125
+ });
32
126
 
33
127
  // ============ Initialize Components ============
34
128
 
@@ -624,17 +718,44 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
624
718
  need_more?: boolean;
625
719
  };
626
720
 
627
- // Update the retrieval log with feedback
628
- const updated = db.updateRetrievalFeedback(recall_id, useful_memory_ids, need_more);
721
+ // First, get the original recall to validate useful_memory_ids
722
+ const retrievalLog = db.getRetrievalLog(recall_id);
723
+ if (!retrievalLog) {
724
+ return {
725
+ content: [
726
+ {
727
+ type: "text" as const,
728
+ text: JSON.stringify({
729
+ success: false,
730
+ error: `Recall ID not found: ${recall_id}`,
731
+ }),
732
+ },
733
+ ],
734
+ };
735
+ }
736
+
737
+ // Validate: only accept IDs that were in the original recall
738
+ const originalIdSet = new Set(retrievalLog.memory_ids);
739
+ const validUsefulIds = useful_memory_ids.filter(id => originalIdSet.has(id));
740
+ const invalidIds = useful_memory_ids.filter(id => !originalIdSet.has(id));
741
+
742
+ if (invalidIds.length > 0) {
743
+ console.error(`[Engram] memory_feedback: ${invalidIds.length} IDs not in original recall, ignored: ${invalidIds.join(", ")}`);
744
+ }
745
+
746
+ // Update the retrieval log with validated feedback
747
+ const updated = db.updateRetrievalFeedback(recall_id, validUsefulIds, need_more);
629
748
 
630
749
  if (!updated) {
750
+ // Should not happen since we already checked above, but handle gracefully
751
+ console.error(`[Engram] memory_feedback: failed to update retrieval log ${recall_id}`);
631
752
  return {
632
753
  content: [
633
754
  {
634
755
  type: "text" as const,
635
756
  text: JSON.stringify({
636
757
  success: false,
637
- error: `Recall ID not found: ${recall_id}`,
758
+ error: `Failed to update feedback for: ${recall_id}`,
638
759
  }),
639
760
  },
640
761
  ],
@@ -664,7 +785,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
664
785
  text: JSON.stringify({
665
786
  success: true,
666
787
  feedback_recorded: true,
667
- useful_count: useful_memory_ids.length,
788
+ useful_count: validUsefulIds.length,
668
789
  expanded_search: true,
669
790
  additional_results: formatted,
670
791
  additional_count: formatted.length,
@@ -697,7 +818,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
697
818
  text: JSON.stringify({
698
819
  success: true,
699
820
  feedback_recorded: true,
700
- useful_count: useful_memory_ids.length,
821
+ useful_count: validUsefulIds.length,
701
822
  learning_applied: learningApplied > 0,
702
823
  connections_strengthened: learningApplied,
703
824
  }, null, 2),
@@ -726,12 +847,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
726
847
  // ============ Main ============
727
848
 
728
849
  async function main() {
850
+ // Kill any zombie instances before starting
851
+ cleanupZombies();
852
+ writePidFile();
853
+
729
854
  await initialize();
730
855
 
731
856
  const transport = new StdioServerTransport();
732
857
  await server.connect(transport);
733
858
 
734
- console.error("[Engram] MCP server running on stdio");
859
+ console.error(`[Engram] MCP server running on stdio (PID ${process.pid})`);
735
860
  }
736
861
 
737
862
  main().catch((error) => {
@@ -327,8 +327,12 @@ export class HybridSearch {
327
327
  // Log this retrieval for deferred learning
328
328
  try {
329
329
  this.db.createRetrievalLog(this.sessionId, recallId, query, allResultIds);
330
- } catch {
331
- // Ignore duplicate recall_id errors (can happen with rapid queries)
330
+ } catch (error) {
331
+ // Only ignore duplicate recall_id errors (UNIQUE constraint), log all others
332
+ const msg = error instanceof Error ? error.message : String(error);
333
+ if (!msg.includes("UNIQUE constraint")) {
334
+ console.error(`[Engram] Failed to create retrieval log: ${msg}`);
335
+ }
332
336
  }
333
337
 
334
338
  return {
@@ -1183,59 +1183,72 @@ export class EngramDatabase {
1183
1183
 
1184
1184
  /**
1185
1185
  * Record that memories were retrieved together (co-retrieval)
1186
+ * Uses cached statement and transaction for O(n²) efficiency
1186
1187
  */
1187
1188
  recordCoRetrieval(memoryIds: string[]): void {
1188
1189
  if (memoryIds.length < 2) return;
1189
1190
 
1190
- // Update all pairs
1191
- for (let i = 0; i < memoryIds.length; i++) {
1192
- for (let j = i + 1; j < memoryIds.length; j++) {
1193
- const [a, b] = memoryIds[i] < memoryIds[j]
1194
- ? [memoryIds[i], memoryIds[j]]
1195
- : [memoryIds[j], memoryIds[i]];
1196
-
1197
- this.db.prepare(`
1198
- INSERT INTO memory_connections (id, memory_a, memory_b, co_retrievals, last_fired)
1199
- VALUES (?, ?, ?, 1, CURRENT_TIMESTAMP)
1200
- ON CONFLICT(memory_a, memory_b) DO UPDATE SET
1201
- co_retrievals = co_retrievals + 1,
1202
- last_fired = CURRENT_TIMESTAMP
1203
- `).run(randomUUID(), a, b);
1191
+ // Cache statement once (not per iteration)
1192
+ const upsertStmt = this.stmt(`
1193
+ INSERT INTO memory_connections (id, memory_a, memory_b, co_retrievals, last_fired)
1194
+ VALUES (?, ?, ?, 1, CURRENT_TIMESTAMP)
1195
+ ON CONFLICT(memory_a, memory_b) DO UPDATE SET
1196
+ co_retrievals = co_retrievals + 1,
1197
+ last_fired = CURRENT_TIMESTAMP
1198
+ `);
1199
+
1200
+ // Wrap in transaction for batch efficiency
1201
+ this.db.transaction(() => {
1202
+ for (let i = 0; i < memoryIds.length; i++) {
1203
+ for (let j = i + 1; j < memoryIds.length; j++) {
1204
+ const [a, b] = memoryIds[i] < memoryIds[j]
1205
+ ? [memoryIds[i], memoryIds[j]]
1206
+ : [memoryIds[j], memoryIds[i]];
1207
+
1208
+ upsertStmt.run(randomUUID(), a, b);
1209
+ }
1204
1210
  }
1205
- }
1211
+ })();
1206
1212
  }
1207
1213
 
1208
1214
  /**
1209
1215
  * Record that memories were useful together (from LLM feedback)
1210
1216
  * This is what actually strengthens connections
1217
+ * Uses cached statement and transaction for O(n²) efficiency
1211
1218
  */
1212
1219
  recordCoUseful(memoryIds: string[]): void {
1213
1220
  if (memoryIds.length < 2) return;
1214
1221
 
1215
- for (let i = 0; i < memoryIds.length; i++) {
1216
- for (let j = i + 1; j < memoryIds.length; j++) {
1217
- const [a, b] = memoryIds[i] < memoryIds[j]
1218
- ? [memoryIds[i], memoryIds[j]]
1219
- : [memoryIds[j], memoryIds[i]];
1220
-
1221
- // Get current stats to calculate new strength
1222
- const conn = this.getOrCreateConnection(a, b);
1223
- const newCoUseful = conn.co_useful + 1;
1224
- const newCoRetrievals = Math.max(conn.co_retrievals, newCoUseful);
1225
-
1226
- // Calculate strength with diminishing returns
1227
- const usefulRatio = newCoUseful / Math.max(1, newCoRetrievals);
1228
- const diminished = Math.sqrt(usefulRatio);
1229
- const confidence = Math.min(1, newCoUseful / 3); // Require 3+ co-useful for strong connection
1230
- const newStrength = diminished * confidence;
1231
-
1232
- this.db.prepare(`
1233
- UPDATE memory_connections
1234
- SET co_useful = ?, strength = ?, last_fired = CURRENT_TIMESTAMP
1235
- WHERE memory_a = ? AND memory_b = ?
1236
- `).run(newCoUseful, newStrength, a, b);
1222
+ // Cache statement once (not per iteration)
1223
+ const updateStmt = this.stmt(`
1224
+ UPDATE memory_connections
1225
+ SET co_useful = ?, strength = ?, last_fired = CURRENT_TIMESTAMP
1226
+ WHERE memory_a = ? AND memory_b = ?
1227
+ `);
1228
+
1229
+ // Wrap in transaction for batch efficiency
1230
+ this.db.transaction(() => {
1231
+ for (let i = 0; i < memoryIds.length; i++) {
1232
+ for (let j = i + 1; j < memoryIds.length; j++) {
1233
+ const [a, b] = memoryIds[i] < memoryIds[j]
1234
+ ? [memoryIds[i], memoryIds[j]]
1235
+ : [memoryIds[j], memoryIds[i]];
1236
+
1237
+ // Get current stats to calculate new strength
1238
+ const conn = this.getOrCreateConnection(a, b);
1239
+ const newCoUseful = conn.co_useful + 1;
1240
+ const newCoRetrievals = Math.max(conn.co_retrievals, newCoUseful);
1241
+
1242
+ // Calculate strength with diminishing returns
1243
+ const usefulRatio = newCoUseful / Math.max(1, newCoRetrievals);
1244
+ const diminished = Math.sqrt(usefulRatio);
1245
+ const confidence = Math.min(1, newCoUseful / 3); // Require 3+ co-useful for strong connection
1246
+ const newStrength = diminished * confidence;
1247
+
1248
+ updateStmt.run(newCoUseful, newStrength, a, b);
1249
+ }
1237
1250
  }
1238
- }
1251
+ })();
1239
1252
  }
1240
1253
 
1241
1254
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Chat Handler for Engram Web Interface
3
- * Uses Claude Haiku 4.5 with tools for entity/memory management
3
+ * Uses Claude Opus 4.5 with tools for entity/memory management
4
4
  */
5
5
 
6
6
  import Anthropic from "@anthropic-ai/sdk";
@@ -9,22 +9,22 @@ import { KnowledgeGraph } from "../graph/knowledge-graph.js";
9
9
  import { HybridSearch } from "../retrieval/hybrid.js";
10
10
  import { getAnthropicApiKey } from "../settings.js";
11
11
 
12
- // Tool definitions for Claude
12
+ // Tool definitions for Claude - optimized for LLM consumption
13
13
  const TOOLS: Anthropic.Tool[] = [
14
14
  {
15
15
  name: "list_entities",
16
- description: "List all entities in the knowledge graph. Use this to see what people, organizations, and places are stored.",
16
+ description: "Returns array of {name, type, id}. Filters: type (person|organization|place), limit. Default limit=50.",
17
17
  input_schema: {
18
18
  type: "object" as const,
19
19
  properties: {
20
20
  type: {
21
21
  type: "string",
22
22
  enum: ["person", "organization", "place"],
23
- description: "Filter by entity type (optional)",
23
+ description: "Filter: person, organization, or place",
24
24
  },
25
25
  limit: {
26
- type: "number",
27
- description: "Maximum number of entities to return (default: 50)",
26
+ type: "integer",
27
+ description: "Max results (default: 50)",
28
28
  },
29
29
  },
30
30
  required: [],
@@ -32,13 +32,13 @@ const TOOLS: Anthropic.Tool[] = [
32
32
  },
33
33
  {
34
34
  name: "get_entity",
35
- description: "Get detailed information about an entity including its observations and relationships.",
35
+ description: "Returns {name, type, observations[], relationships_from[], relationships_to[]} or {error}.",
36
36
  input_schema: {
37
37
  type: "object" as const,
38
38
  properties: {
39
39
  name: {
40
40
  type: "string",
41
- description: "The entity name to look up",
41
+ description: "Exact entity name (case-sensitive)",
42
42
  },
43
43
  },
44
44
  required: ["name"],
@@ -46,13 +46,13 @@ const TOOLS: Anthropic.Tool[] = [
46
46
  },
47
47
  {
48
48
  name: "delete_entity",
49
- description: "Delete an entity and all its observations and relationships. Use this to remove incorrect or duplicate entities.",
49
+ description: "Permanently removes entity + all observations + all relationships. Returns {success, deleted} or {error}.",
50
50
  input_schema: {
51
51
  type: "object" as const,
52
52
  properties: {
53
53
  name: {
54
54
  type: "string",
55
- description: "The entity name to delete",
55
+ description: "Exact entity name to delete",
56
56
  },
57
57
  },
58
58
  required: ["name"],
@@ -60,17 +60,17 @@ const TOOLS: Anthropic.Tool[] = [
60
60
  },
61
61
  {
62
62
  name: "merge_entities",
63
- description: "Merge one entity into another. All observations and relationships from the source will be moved to the target, then the source is deleted. Use this to fix duplicates.",
63
+ description: "Moves all data from 'merge' into 'keep', then deletes 'merge'. Use for deduplication. Returns {success, kept, merged, observations_moved, relations_moved} or {error}.",
64
64
  input_schema: {
65
65
  type: "object" as const,
66
66
  properties: {
67
67
  keep: {
68
68
  type: "string",
69
- description: "The entity name to keep (target)",
69
+ description: "Entity name to preserve (target)",
70
70
  },
71
71
  merge: {
72
72
  type: "string",
73
- description: "The entity name to merge and delete (source)",
73
+ description: "Entity name to merge then delete (source)",
74
74
  },
75
75
  },
76
76
  required: ["keep", "merge"],
@@ -78,17 +78,17 @@ const TOOLS: Anthropic.Tool[] = [
78
78
  },
79
79
  {
80
80
  name: "rename_entity",
81
- description: "Rename an entity to a new name.",
81
+ description: "Changes entity name. Returns {success, old_name, new_name} or {error}.",
82
82
  input_schema: {
83
83
  type: "object" as const,
84
84
  properties: {
85
85
  old_name: {
86
86
  type: "string",
87
- description: "Current entity name",
87
+ description: "Current exact name",
88
88
  },
89
89
  new_name: {
90
90
  type: "string",
91
- description: "New entity name",
91
+ description: "New name",
92
92
  },
93
93
  },
94
94
  required: ["old_name", "new_name"],
@@ -96,7 +96,7 @@ const TOOLS: Anthropic.Tool[] = [
96
96
  },
97
97
  {
98
98
  name: "delete_relationship",
99
- description: "Delete a relationship between two entities.",
99
+ description: "Removes specific relationship. All 3 params must match exactly. Returns {success, deleted} or {error}.",
100
100
  input_schema: {
101
101
  type: "object" as const,
102
102
  properties: {
@@ -110,7 +110,7 @@ const TOOLS: Anthropic.Tool[] = [
110
110
  },
111
111
  type: {
112
112
  type: "string",
113
- description: "Relationship type (e.g., 'works_at', 'knows')",
113
+ description: "Relationship type (e.g., works_at, knows, lives_in)",
114
114
  },
115
115
  },
116
116
  required: ["from", "to", "type"],
@@ -118,17 +118,17 @@ const TOOLS: Anthropic.Tool[] = [
118
118
  },
119
119
  {
120
120
  name: "search_memories",
121
- description: "Search through stored memories.",
121
+ description: "Hybrid BM25+semantic search. Returns {results[{id, content, timestamp, score}], count}.",
122
122
  input_schema: {
123
123
  type: "object" as const,
124
124
  properties: {
125
125
  query: {
126
126
  type: "string",
127
- description: "Search query",
127
+ description: "Search query (keywords or natural language)",
128
128
  },
129
129
  limit: {
130
- type: "number",
131
- description: "Maximum results (default: 10)",
130
+ type: "integer",
131
+ description: "Max results (default: 10)",
132
132
  },
133
133
  },
134
134
  required: ["query"],
@@ -136,13 +136,13 @@ const TOOLS: Anthropic.Tool[] = [
136
136
  },
137
137
  {
138
138
  name: "delete_memory",
139
- description: "Delete a memory by its ID (soft-delete, can be recovered).",
139
+ description: "Soft-delete (recoverable). Returns {success, disabled_id} or {error}.",
140
140
  input_schema: {
141
141
  type: "object" as const,
142
142
  properties: {
143
143
  id: {
144
144
  type: "string",
145
- description: "The memory ID to delete",
145
+ description: "Memory UUID",
146
146
  },
147
147
  },
148
148
  required: ["id"],
@@ -150,13 +150,13 @@ const TOOLS: Anthropic.Tool[] = [
150
150
  },
151
151
  {
152
152
  name: "edit_memory",
153
- description: "Edit an existing memory's content or importance.",
153
+ description: "Updates content and/or importance. Returns {success, memory_id, updated_fields[]} or {error}.",
154
154
  input_schema: {
155
155
  type: "object" as const,
156
156
  properties: {
157
157
  id: {
158
158
  type: "string",
159
- description: "The memory ID to edit",
159
+ description: "Memory UUID",
160
160
  },
161
161
  content: {
162
162
  type: "string",
@@ -164,7 +164,9 @@ const TOOLS: Anthropic.Tool[] = [
164
164
  },
165
165
  importance: {
166
166
  type: "number",
167
- description: "New importance (0-1): 0.9=core identity, 0.8=major, 0.5=normal, 0.3=minor",
167
+ minimum: 0,
168
+ maximum: 1,
169
+ description: "0-1: 0.9=identity, 0.8=major, 0.5=normal, 0.3=minor",
168
170
  },
169
171
  },
170
172
  required: ["id"],
@@ -172,17 +174,19 @@ const TOOLS: Anthropic.Tool[] = [
172
174
  },
173
175
  {
174
176
  name: "create_memory",
175
- description: "Create a new memory. Use for storing user information, preferences, or facts.",
177
+ description: "Stores new memory. Returns {success, memory_id, content}.",
176
178
  input_schema: {
177
179
  type: "object" as const,
178
180
  properties: {
179
181
  content: {
180
182
  type: "string",
181
- description: "The information to store",
183
+ description: "Information to store",
182
184
  },
183
185
  importance: {
184
186
  type: "number",
185
- description: "0-1 score: 0.9=core identity, 0.8=major, 0.5=normal (default), 0.3=minor",
187
+ minimum: 0,
188
+ maximum: 1,
189
+ description: "0-1: 0.9=identity, 0.8=major, 0.5=normal (default), 0.3=minor",
186
190
  },
187
191
  },
188
192
  required: ["content"],
@@ -190,13 +194,13 @@ const TOOLS: Anthropic.Tool[] = [
190
194
  },
191
195
  {
192
196
  name: "create_entity",
193
- description: "Create a new entity (person, organization, or place).",
197
+ description: "Creates new entity. Returns {success, entity_id, name, type} or {error} if exists.",
194
198
  input_schema: {
195
199
  type: "object" as const,
196
200
  properties: {
197
201
  name: {
198
202
  type: "string",
199
- description: "The entity name",
203
+ description: "Entity name",
200
204
  },
201
205
  type: {
202
206
  type: "string",
@@ -209,7 +213,7 @@ const TOOLS: Anthropic.Tool[] = [
209
213
  },
210
214
  {
211
215
  name: "create_relationship",
212
- description: "Create a relationship between two entities.",
216
+ description: "Links two entities. Auto-creates entities as 'person' if missing. Returns {success, relationship}.",
213
217
  input_schema: {
214
218
  type: "object" as const,
215
219
  properties: {
@@ -223,7 +227,7 @@ const TOOLS: Anthropic.Tool[] = [
223
227
  },
224
228
  type: {
225
229
  type: "string",
226
- description: "Relationship type (e.g., 'works_at', 'lives_in', 'knows', 'sibling_of')",
230
+ description: "Relationship type (e.g., works_at, lives_in, knows, sibling_of, parent_of)",
227
231
  },
228
232
  },
229
233
  required: ["from", "to", "type"],
@@ -231,7 +235,7 @@ const TOOLS: Anthropic.Tool[] = [
231
235
  },
232
236
  {
233
237
  name: "find_duplicates",
234
- description: "Find potential duplicate entities that could be merged.",
238
+ description: "Detects similar entity names. Returns {groups[{keep, duplicates[]}], total_duplicates}.",
235
239
  input_schema: {
236
240
  type: "object" as const,
237
241
  properties: {},
@@ -240,7 +244,7 @@ const TOOLS: Anthropic.Tool[] = [
240
244
  },
241
245
  {
242
246
  name: "auto_tidy",
243
- description: "Automatically merge all detected duplicate entities.",
247
+ description: "Auto-merges all detected duplicates. Returns {entities_merged, observations_moved, relations_moved}.",
244
248
  input_schema: {
245
249
  type: "object" as const,
246
250
  properties: {},
@@ -301,6 +305,9 @@ export class ChatHandler {
301
305
  refreshClient(): void {
302
306
  const apiKey = getAnthropicApiKey();
303
307
  if (apiKey) {
308
+ if (!this.client) {
309
+ console.error("[Engram] ChatHandler: API key configured");
310
+ }
304
311
  this.client = new Anthropic({ apiKey });
305
312
  } else {
306
313
  this.client = null;
@@ -308,6 +315,8 @@ export class ChatHandler {
308
315
  }
309
316
 
310
317
  isConfigured(): boolean {
318
+ // Re-check API key in case it was added after startup
319
+ this.refreshClient();
311
320
  return this.client !== null;
312
321
  }
313
322
 
@@ -341,6 +350,7 @@ export class ChatHandler {
341
350
 
342
351
  // Queue-aware chat method
343
352
  async chat(userMessage: string): Promise<string> {
353
+ this.refreshClient();
344
354
  if (!this.client) {
345
355
  return "Chat is not configured. Set ANTHROPIC_API_KEY environment variable.";
346
356
  }
@@ -357,6 +367,7 @@ export class ChatHandler {
357
367
 
358
368
  // Streaming chat with callbacks for real-time updates
359
369
  async *chatStream(userMessage: string): AsyncGenerator<StreamEvent> {
370
+ this.refreshClient();
360
371
  if (!this.client) {
361
372
  yield { type: "error", content: "Chat is not configured. Set ANTHROPIC_API_KEY environment variable." };
362
373
  return;
@@ -377,11 +388,10 @@ export class ChatHandler {
377
388
  }
378
389
 
379
390
  let continueLoop = true;
380
- let fullResponse = "";
381
391
 
382
392
  while (continueLoop) {
383
393
  const stream = this.client.messages.stream({
384
- model: "claude-haiku-4-5-20241022",
394
+ model: "claude-opus-4-5-20251101",
385
395
  max_tokens: 1024,
386
396
  system: SYSTEM_PROMPT,
387
397
  tools: TOOLS,
@@ -402,25 +412,13 @@ export class ChatHandler {
402
412
  }
403
413
  } else if (event.type === "content_block_delta") {
404
414
  if (event.delta.type === "text_delta") {
405
- fullResponse += event.delta.text;
406
415
  yield { type: "text", content: event.delta.text };
407
416
  } else if (event.delta.type === "input_json_delta" && currentToolUse) {
408
417
  currentToolUse.input += event.delta.partial_json;
409
418
  }
410
419
  } else if (event.type === "content_block_stop") {
411
- if (currentToolUse) {
412
- // Execute the tool
413
- let toolInput: Record<string, unknown> = {};
414
- try {
415
- toolInput = JSON.parse(currentToolUse.input || "{}");
416
- } catch {
417
- toolInput = {};
418
- }
419
-
420
- const result = await this.executeTool(currentToolUse.name, toolInput);
421
- yield { type: "tool_end", tool: currentToolUse.name, result };
422
- currentToolUse = null;
423
- }
420
+ // Don't execute tools here - wait for finalMessage to avoid double execution
421
+ currentToolUse = null;
424
422
  }
425
423
  }
426
424
 
@@ -428,7 +426,7 @@ export class ChatHandler {
428
426
  const finalMessage = await stream.finalMessage();
429
427
 
430
428
  if (finalMessage.stop_reason === "tool_use") {
431
- // Process tool results and continue
429
+ // Process tool results (execute only once, here)
432
430
  const toolUseBlocks = finalMessage.content.filter(
433
431
  (block): block is Anthropic.ToolUseBlock => block.type === "tool_use"
434
432
  );
@@ -436,10 +434,13 @@ export class ChatHandler {
436
434
  const toolResults: Anthropic.ToolResultBlockParam[] = [];
437
435
  for (const toolUse of toolUseBlocks) {
438
436
  const result = await this.executeTool(toolUse.name, toolUse.input as Record<string, unknown>);
437
+ const isError = typeof result === "object" && result !== null && "error" in result;
438
+ yield { type: "tool_end", tool: toolUse.name, result };
439
439
  toolResults.push({
440
440
  type: "tool_result",
441
441
  tool_use_id: toolUse.id,
442
442
  content: JSON.stringify(result),
443
+ is_error: isError,
443
444
  });
444
445
  }
445
446
 
@@ -492,7 +493,7 @@ export class ChatHandler {
492
493
  }
493
494
 
494
495
  let response = await this.client.messages.create({
495
- model: "claude-haiku-4-5-20241022",
496
+ model: "claude-opus-4-5-20251101",
496
497
  max_tokens: 1024,
497
498
  system: SYSTEM_PROMPT,
498
499
  tools: TOOLS,
@@ -508,10 +509,12 @@ export class ChatHandler {
508
509
  const toolResults: Anthropic.ToolResultBlockParam[] = [];
509
510
  for (const toolUse of toolUseBlocks) {
510
511
  const result = await this.executeTool(toolUse.name, toolUse.input as Record<string, unknown>);
512
+ const isError = typeof result === "object" && result !== null && "error" in result;
511
513
  toolResults.push({
512
514
  type: "tool_result",
513
515
  tool_use_id: toolUse.id,
514
516
  content: JSON.stringify(result),
517
+ is_error: isError,
515
518
  });
516
519
  }
517
520
 
@@ -527,7 +530,7 @@ export class ChatHandler {
527
530
 
528
531
  // Continue the conversation
529
532
  response = await this.client.messages.create({
530
- model: "claude-haiku-4-5-20241022",
533
+ model: "claude-opus-4-5-20251101",
531
534
  max_tokens: 1024,
532
535
  system: SYSTEM_PROMPT,
533
536
  tools: TOOLS,