open-mem 0.7.0 → 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.
Files changed (111) hide show
  1. package/CHANGELOG.md +23 -3
  2. package/README.md +77 -27
  3. package/dist/adapters/http/server.d.ts +15 -1
  4. package/dist/adapters/http/server.d.ts.map +1 -1
  5. package/dist/adapters/http/sse.d.ts +16 -1
  6. package/dist/adapters/http/sse.d.ts.map +1 -1
  7. package/dist/adapters/mcp/server.d.ts +25 -1
  8. package/dist/adapters/mcp/server.d.ts.map +1 -1
  9. package/dist/adapters/opencode/tools.d.ts.map +1 -1
  10. package/dist/adapters/platform/bridge-client.d.ts +32 -0
  11. package/dist/adapters/platform/bridge-client.d.ts.map +1 -0
  12. package/dist/adapters/platform/builtin.d.ts +6 -0
  13. package/dist/adapters/platform/builtin.d.ts.map +1 -0
  14. package/dist/adapters/platform/claude-code.d.ts +7 -0
  15. package/dist/adapters/platform/claude-code.d.ts.map +1 -0
  16. package/dist/adapters/platform/cursor.d.ts +7 -0
  17. package/dist/adapters/platform/cursor.d.ts.map +1 -0
  18. package/dist/adapters/platform/index.d.ts +9 -0
  19. package/dist/adapters/platform/index.d.ts.map +1 -0
  20. package/dist/adapters/platform/normalize.d.ts +14 -0
  21. package/dist/adapters/platform/normalize.d.ts.map +1 -0
  22. package/dist/adapters/platform/opencode.d.ts +7 -0
  23. package/dist/adapters/platform/opencode.d.ts.map +1 -0
  24. package/dist/adapters/platform/runtime.d.ts +27 -0
  25. package/dist/adapters/platform/runtime.d.ts.map +1 -0
  26. package/dist/adapters/platform/types.d.ts +53 -0
  27. package/dist/adapters/platform/types.d.ts.map +1 -0
  28. package/dist/ai/compressor.d.ts.map +1 -1
  29. package/dist/ai/conflict-evaluator.d.ts.map +1 -1
  30. package/dist/ai/entity-extractor.d.ts +2 -2
  31. package/dist/ai/entity-extractor.d.ts.map +1 -1
  32. package/dist/ai/parser.d.ts.map +1 -1
  33. package/dist/ai/prompts.d.ts.map +1 -1
  34. package/dist/ai/summarizer.d.ts.map +1 -1
  35. package/dist/claude-code.d.ts +3 -0
  36. package/dist/claude-code.d.ts.map +1 -0
  37. package/dist/claude-code.js +551 -0
  38. package/dist/config/store.d.ts.map +1 -1
  39. package/dist/config.d.ts.map +1 -1
  40. package/dist/context/builder.d.ts.map +1 -1
  41. package/dist/contracts/api.d.ts +129 -0
  42. package/dist/contracts/api.d.ts.map +1 -0
  43. package/dist/core/contracts.d.ts +67 -1
  44. package/dist/core/contracts.d.ts.map +1 -1
  45. package/dist/core/memory-engine.d.ts +33 -2
  46. package/dist/core/memory-engine.d.ts.map +1 -1
  47. package/dist/cursor.d.ts +3 -0
  48. package/dist/cursor.d.ts.map +1 -0
  49. package/dist/cursor.js +551 -0
  50. package/dist/daemon.js +146 -122
  51. package/dist/dashboard/assets/index-BTEnO15N.js +63 -0
  52. package/dist/dashboard/assets/index-o3hCx7_v.css +1 -0
  53. package/dist/dashboard/index.html +2 -2
  54. package/dist/db/config-audit.d.ts +10 -0
  55. package/dist/db/config-audit.d.ts.map +1 -0
  56. package/dist/db/entities.d.ts.map +1 -1
  57. package/dist/db/maintenance-history.d.ts +9 -0
  58. package/dist/db/maintenance-history.d.ts.map +1 -0
  59. package/dist/db/observations.d.ts +12 -0
  60. package/dist/db/observations.d.ts.map +1 -1
  61. package/dist/db/schema.d.ts +3 -1
  62. package/dist/db/schema.d.ts.map +1 -1
  63. package/dist/db/user-memory.d.ts.map +1 -1
  64. package/dist/hooks/chat-capture.d.ts +11 -0
  65. package/dist/hooks/chat-capture.d.ts.map +1 -1
  66. package/dist/hooks/context-inject.d.ts.map +1 -1
  67. package/dist/hooks/session-events.d.ts +10 -0
  68. package/dist/hooks/session-events.d.ts.map +1 -1
  69. package/dist/hooks/tool-capture.d.ts +12 -0
  70. package/dist/hooks/tool-capture.d.ts.map +1 -1
  71. package/dist/index.d.ts +5 -3
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +226 -225
  74. package/dist/maintenance.js +159 -132
  75. package/dist/mcp.js +187 -187
  76. package/dist/platform-worker.d.ts +3 -0
  77. package/dist/platform-worker.d.ts.map +1 -0
  78. package/dist/queue/processor.d.ts +26 -1
  79. package/dist/queue/processor.d.ts.map +1 -1
  80. package/dist/runtime/metrics.d.ts +43 -0
  81. package/dist/runtime/metrics.d.ts.map +1 -0
  82. package/dist/search/graph.d.ts.map +1 -1
  83. package/dist/search/orchestrator.d.ts +1 -0
  84. package/dist/search/orchestrator.d.ts.map +1 -1
  85. package/dist/search/reranker.d.ts +1 -1
  86. package/dist/search/reranker.d.ts.map +1 -1
  87. package/dist/store/ports.d.ts +9 -0
  88. package/dist/store/ports.d.ts.map +1 -1
  89. package/dist/store/sqlite/adapters.d.ts.map +1 -1
  90. package/dist/tools/recall.d.ts.map +1 -1
  91. package/dist/tools/save.d.ts +1 -1
  92. package/dist/tools/save.d.ts.map +1 -1
  93. package/dist/types.d.ts +64 -0
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/utils/agents-md.d.ts.map +1 -1
  96. package/dist/utils/folder-context-maintenance.d.ts.map +1 -1
  97. package/package.json +27 -6
  98. package/dist/dashboard/assets/index-9JxqY10c.css +0 -1
  99. package/dist/dashboard/assets/index-DWbZtwHB.js +0 -60
  100. package/dist/dashboard/dist/assets/index-9JxqY10c.css +0 -1
  101. package/dist/dashboard/dist/assets/index-CGCNZcwT.js +0 -60
  102. package/dist/dashboard/dist/assets/index-CI60x_dC.css +0 -1
  103. package/dist/dashboard/dist/assets/index-CTwrdVhA.js +0 -60
  104. package/dist/dashboard/dist/assets/index-DWbZtwHB.js +0 -60
  105. package/dist/dashboard/dist/index.html +0 -19
  106. package/dist/servers/http-server.d.ts +0 -22
  107. package/dist/servers/http-server.d.ts.map +0 -1
  108. package/dist/servers/mcp-server.d.ts +0 -27
  109. package/dist/servers/mcp-server.d.ts.map +0 -1
  110. package/dist/servers/sse-broadcaster.d.ts +0 -27
  111. package/dist/servers/sse-broadcaster.d.ts.map +0 -1
package/dist/daemon.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- var S=import.meta.require;import{parseArgs as HX}from"util";import{generateText as qJ}from"ai";var EJ=new Set(["decision","bugfix","feature","refactor","discovery","change"]);function V(J,X){let $=new RegExp(`<${X}[^>]*>([\\s\\S]*?)</${X}>`,"i"),Q=J.match($);return Q?Q[1].trim():""}function _(J,X){let $=new RegExp(`<${X}[^>]*>([\\s\\S]*?)</${X}>`,"gi"),Q=[];for(let Z of J.matchAll($)){let W=Z[1].trim();if(W)Q.push(W)}return Q}function s(J){let X=V(J,"observation");if(!X)return null;let $=V(X,"type").toLowerCase(),Q=EJ.has($)?$:"discovery",Z=V(X,"title")||"Untitled observation",W=V(X,"subtitle"),H=V(X,"narrative"),Y=_(V(X,"facts"),"fact"),K=_(V(X,"concepts"),"concept"),z=_(V(X,"files_read"),"file"),G=_(V(X,"files_modified"),"file"),U=V(X,"importance"),N=Number.parseInt(U,10),L=Number.isNaN(N)?3:Math.max(1,Math.min(5,N));return{type:Q,title:Z,subtitle:W,facts:Y,narrative:H,concepts:K,filesRead:z,filesModified:G,importance:L}}function i(J){let X=V(J,"session_summary");if(!X)return null;let $=V(X,"summary")||"No summary available",Q=_(V(X,"key_decisions"),"decision"),Z=_(V(X,"files_modified"),"file"),W=_(V(X,"concepts"),"concept"),H=V(X,"request")||void 0,Y=V(X,"investigated")||void 0,K=V(X,"learned")||void 0,z=V(X,"completed")||void 0,G=V(X,"next_steps")||void 0;return{summary:$,keyDecisions:Q,filesModified:Z,concepts:W,request:H,investigated:Y,learned:K,completed:z,nextSteps:G}}var DJ=new Set(["new_fact","update","duplicate"]);function r(J){let X=V(J,"evaluation");if(!X)return null;let $=V(X,"outcome").toLowerCase().trim();if(!DJ.has($))return null;let Q=$,Z=V(X,"reason");if(!Z)return null;let W=V(X,"supersedes"),H={outcome:Q,reason:Z};if(Q==="update"&&W)H.supersedesId=W;if(Q==="update"&&!H.supersedesId)return null;return H}var RJ=new Set(["technology","library","pattern","concept","file","person","project","other"]),jJ=new Set(["uses","depends_on","implements","extends","related_to","replaces","configures"]);function a(J){let X=V(J,"extraction");if(!X)return null;let $=V(X,"entities"),Q=V(X,"relations"),Z=_($,"entity"),W=[];for(let K of Z){let z=V(K,"name");if(!z)continue;let G=V(K,"type").toLowerCase(),U=RJ.has(G)?G:"other";W.push({name:z,entityType:U})}let H=_(Q,"relation"),Y=[];for(let K of H){let z=V(K,"source"),G=V(K,"target"),U=V(K,"relationship").toLowerCase();if(!z||!G||!U)continue;if(!jJ.has(U))continue;Y.push({sourceName:z,targetName:G,relationship:U})}return{entities:W,relations:Y}}function E(J){return Math.ceil(J.length/4)}function o(J,X,$){let Q=$?`<session_context>
3
+ var S=import.meta.require;import{parseArgs as KX}from"util";import{generateText as xJ}from"ai";var RJ=new Set(["decision","bugfix","feature","refactor","discovery","change"]);function z(J,X){let $=new RegExp(`<${X}[^>]*>([\\s\\S]*?)</${X}>`,"i"),Q=J.match($);return Q?Q[1].trim():""}function L(J,X){let $=new RegExp(`<${X}[^>]*>([\\s\\S]*?)</${X}>`,"gi"),Q=[];for(let Z of J.matchAll($)){let H=Z[1].trim();if(H)Q.push(H)}return Q}function r(J){let X=z(J,"observation");if(!X)return null;let $=z(X,"type").toLowerCase(),Q=RJ.has($)?$:"discovery",Z=z(X,"title")||"Untitled observation",H=z(X,"subtitle"),W=z(X,"narrative"),Y=L(z(X,"facts"),"fact"),K=L(z(X,"concepts"),"concept"),V=L(z(X,"files_read"),"file"),G=L(z(X,"files_modified"),"file"),U=z(X,"importance"),_=Number.parseInt(U,10),N=Number.isNaN(_)?3:Math.max(1,Math.min(5,_));return{type:Q,title:Z,subtitle:H,facts:Y,narrative:W,concepts:K,filesRead:V,filesModified:G,importance:N}}function a(J){let X=z(J,"session_summary");if(!X)return null;let $=z(X,"summary")||"No summary available",Q=L(z(X,"key_decisions"),"decision"),Z=L(z(X,"files_modified"),"file"),H=L(z(X,"concepts"),"concept"),W=z(X,"request")||void 0,Y=z(X,"investigated")||void 0,K=z(X,"learned")||void 0,V=z(X,"completed")||void 0,G=z(X,"next_steps")||void 0;return{summary:$,keyDecisions:Q,filesModified:Z,concepts:H,request:W,investigated:Y,learned:K,completed:V,nextSteps:G}}var jJ=new Set(["new_fact","update","duplicate"]);function o(J){let X=z(J,"evaluation");if(!X)return null;let $=z(X,"outcome").toLowerCase().trim();if(!jJ.has($))return null;let Q=$,Z=z(X,"reason");if(!Z)return null;let H=z(X,"supersedes"),W={outcome:Q,reason:Z};if(Q==="update"&&H)W.supersedesId=H;if(Q==="update"&&!W.supersedesId)return null;return W}var kJ=new Set(["technology","library","pattern","concept","file","person","project","other"]),OJ=new Set(["uses","depends_on","implements","extends","related_to","replaces","configures"]);function t(J){let X=z(J,"extraction");if(!X)return null;let $=z(X,"entities"),Q=z(X,"relations"),Z=L($,"entity"),H=[];for(let K of Z){let V=z(K,"name");if(!V)continue;let G=z(K,"type").toLowerCase(),U=kJ.has(G)?G:"other";H.push({name:V,entityType:U})}let W=L(Q,"relation"),Y=[];for(let K of W){let V=z(K,"source"),G=z(K,"target"),U=z(K,"relationship").toLowerCase();if(!V||!G||!U)continue;if(!OJ.has(U))continue;Y.push({sourceName:V,targetName:G,relationship:U})}return{entities:H,relations:Y}}function E(J){return Math.ceil(J.length/4)}function e(J,X,$){let Q=$?`<session_context>
4
4
  ${$}
5
5
  </session_context>
6
6
 
@@ -49,7 +49,7 @@ Respond with EXACTLY this XML format:
49
49
  <file>path/to/file/modified</file>
50
50
  </files_modified>
51
51
  </observation>
52
- </instructions>`}function t(J,X){let $=J.map((Q,Z)=>` <obs index="${Z+1}" type="${Q.type}">
52
+ </instructions>`}function JJ(J,X){let $=J.map((Q,Z)=>` <obs index="${Z+1}" type="${Q.type}">
53
53
  <title>${Q.title}</title>
54
54
  <narrative>${Q.narrative}</narrative>
55
55
  </obs>`).join(`
@@ -84,7 +84,7 @@ Respond with EXACTLY this XML format:
84
84
  <concept>key-concept</concept>
85
85
  </concepts>
86
86
  </session_summary>
87
- </instructions>`}function e(J,X){let $=X.map((Q)=>` <candidate id="${Q.id}">
87
+ </instructions>`}function XJ(J,X){let $=X.map((Q)=>` <candidate id="${Q.id}">
88
88
  <title>${Q.title}</title>
89
89
  <narrative>${Q.narrative}</narrative>
90
90
  <concepts>${Q.concepts.join(", ")}</concepts>
@@ -113,7 +113,7 @@ Respond with EXACTLY this XML format:
113
113
  <reason>Brief explanation</reason>
114
114
  </evaluation>
115
115
  </instructions>
116
- </conflict_evaluation>`}function JJ(J){let X=[...J.filesRead,...J.filesModified];return`<entity_extraction>
116
+ </conflict_evaluation>`}function $J(J){let X=[...J.filesRead,...J.filesModified];return`<entity_extraction>
117
117
  <observation>
118
118
  <title>${J.title}</title>
119
119
  <type>${J.type}</type>
@@ -142,48 +142,51 @@ Respond with EXACTLY this XML format:
142
142
  </relations>
143
143
  </extraction>
144
144
  </instructions>
145
- </entity_extraction>`}var kJ={"claude-sonnet-4-20250514":"us.anthropic.claude-sonnet-4-20250514-v1:0","claude-opus-4-20250514":"us.anthropic.claude-opus-4-20250514-v1:0","claude-3-5-sonnet-20241022":"us.anthropic.claude-3-5-sonnet-20241022-v2:0","claude-3-5-haiku-20241022":"us.anthropic.claude-3-5-haiku-20241022-v1:0","claude-3-haiku-20240307":"anthropic.claude-3-haiku-20240307-v1:0"};function OJ(J){if(J.includes("."))return J;return kJ[J]||`us.anthropic.${J}-v1:0`}function M(J){switch(J.provider){case"anthropic":{let{createAnthropic:X}=S("@ai-sdk/anthropic");return X({apiKey:J.apiKey})(J.model)}case"bedrock":{let{createAmazonBedrock:X}=S("@ai-sdk/amazon-bedrock");return X()(OJ(J.model))}case"openai":{let{createOpenAI:X}=S("@ai-sdk/openai");return X({apiKey:J.apiKey})(J.model)}case"google":{let{createGoogleGenerativeAI:X}=S("@ai-sdk/google");return X({apiKey:J.apiKey})(J.model)}default:throw Error(`Unknown provider: ${J.provider}. Supported: anthropic, bedrock, openai, google`)}}function XJ(J){try{switch(J.provider){case"google":{let{createGoogleGenerativeAI:X}=S("@ai-sdk/google");return X({apiKey:J.apiKey}).embedding("text-embedding-004")}case"openai":{let{createOpenAI:X}=S("@ai-sdk/openai");return X({apiKey:J.apiKey}).embedding("text-embedding-3-small")}case"bedrock":{let{createAmazonBedrock:X}=S("@ai-sdk/amazon-bedrock");return X().embedding("amazon.titan-embed-text-v2:0")}case"anthropic":return null;default:return null}}catch{return null}}var TJ={"gemini-2.5-flash-lite":10,"gemini-2.5-flash":10,"gemini-2.5-pro":5,"gemini-2.0-flash":15,"gemini-2.0-flash-lite":30,"gemini-3-flash":5},$J=0;async function A(J,X){if(!X)return;let $=TJ[J]||5,Q=Math.ceil(60000/$)+100,W=Date.now()-$J;if(W<Q){let H=Q-W;await new Promise((Y)=>setTimeout(Y,H))}$J=Date.now()}class j{model;config;_generate=qJ;constructor(J){this.config=J,this.model=null;let X=J.provider!=="bedrock";if(J.compressionEnabled&&(!X||J.apiKey))try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}static MAX_INPUT_LENGTH=50000;async compress(J,X,$){if(!this.config.compressionEnabled||!this.model)return null;if(X.length<this.config.minOutputLength)return null;let Q=E(X),Z=X.length>j.MAX_INPUT_LENGTH?`${X.substring(0,j.MAX_INPUT_LENGTH)}
145
+ </entity_extraction>`}var TJ={"claude-sonnet-4-20250514":"us.anthropic.claude-sonnet-4-20250514-v1:0","claude-opus-4-20250514":"us.anthropic.claude-opus-4-20250514-v1:0","claude-3-5-sonnet-20241022":"us.anthropic.claude-3-5-sonnet-20241022-v2:0","claude-3-5-haiku-20241022":"us.anthropic.claude-3-5-haiku-20241022-v1:0","claude-3-haiku-20240307":"anthropic.claude-3-haiku-20240307-v1:0"};function qJ(J){if(J.includes("."))return J;return TJ[J]||`us.anthropic.${J}-v1:0`}function M(J){switch(J.provider){case"anthropic":{let{createAnthropic:X}=S("@ai-sdk/anthropic");return X({apiKey:J.apiKey})(J.model)}case"bedrock":{let{createAmazonBedrock:X}=S("@ai-sdk/amazon-bedrock");return X()(qJ(J.model))}case"openai":{let{createOpenAI:X}=S("@ai-sdk/openai");return X({apiKey:J.apiKey})(J.model)}case"google":{let{createGoogleGenerativeAI:X}=S("@ai-sdk/google");return X({apiKey:J.apiKey})(J.model)}default:throw Error(`Unknown provider: ${J.provider}. Supported: anthropic, bedrock, openai, google`)}}function QJ(J){try{switch(J.provider){case"google":{let{createGoogleGenerativeAI:X}=S("@ai-sdk/google");return X({apiKey:J.apiKey}).embedding("text-embedding-004")}case"openai":{let{createOpenAI:X}=S("@ai-sdk/openai");return X({apiKey:J.apiKey}).embedding("text-embedding-3-small")}case"bedrock":{let{createAmazonBedrock:X}=S("@ai-sdk/amazon-bedrock");return X().embedding("amazon.titan-embed-text-v2:0")}case"anthropic":return null;default:return null}}catch{return null}}var PJ={"gemini-2.5-flash-lite":10,"gemini-2.5-flash":10,"gemini-2.5-pro":5,"gemini-2.0-flash":15,"gemini-2.0-flash-lite":30,"gemini-3-flash":5},ZJ=0;async function A(J,X){if(!X)return;let $=PJ[J]||5,Q=Math.ceil(60000/$)+100,H=Date.now()-ZJ;if(H<Q){let W=Q-H;await new Promise((Y)=>setTimeout(Y,W))}ZJ=Date.now()}class O{model;config;_generate=xJ;constructor(J){this.config=J,this.model=null;let X=J.provider!=="bedrock";if(J.compressionEnabled&&(!X||J.apiKey))try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}static MAX_INPUT_LENGTH=50000;async compress(J,X,$){if(!this.config.compressionEnabled||!this.model)return null;if(X.length<this.config.minOutputLength)return null;let Q=E(X),Z=X.length>O.MAX_INPUT_LENGTH?`${X.substring(0,O.MAX_INPUT_LENGTH)}
146
146
 
147
- [... truncated ...]`:X,W=o(J,Z,$),H=2;for(let Y=0;Y<=H;Y++)try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:K}=await this._generate({model:this.model,maxOutputTokens:this.config.maxTokensPerCompression,prompt:W}),z=s(K);if(z)z.discoveryTokens=Q;return z}catch(K){if(hJ(K)&&Y<H){let z=2**Y*1000;await QJ(z);continue}return null}return null}async compressBatch(J){let X=new Map;for(let $=0;$<J.length;$++){let Q=J[$],Z=await this.compress(Q.toolName,Q.toolOutput,Q.sessionContext);if(X.set(Q.callId,Z),$<J.length-1)await QJ(200)}return X}createFallbackObservation(J,X){let $=IJ(X),Q=PJ[J]??"discovery";return{type:Q,title:`${J} execution`,subtitle:X.substring(0,100).replace(/\n/g," "),facts:[],narrative:`Tool ${J} was executed. Output length: ${X.length} chars.`,concepts:[],filesRead:Q==="discovery"?$:[],filesModified:Q==="change"?$:[],discoveryTokens:E(X),importance:2}}async isAvailable(){if(!this.model)return!1;try{return await this._generate({model:this.model,maxOutputTokens:10,prompt:"ping"}),!0}catch{return!1}}}var PJ={Read:"discovery",Write:"change",Edit:"change",Bash:"change",Glob:"discovery",Grep:"discovery"},xJ=/(?:^|\s)((?:\.\/|\/|src\/|tests\/|lib\/)\S+\.\w+)/gm;function IJ(J){let X=[];for(let $ of J.matchAll(xJ))X.push($[1]);return[...new Set(X)]}function hJ(J){if(typeof J!=="object"||J===null)return!1;let X=J,$=X.status;if($===429||$===500||$===503)return!0;let Q=X.error;if(typeof Q==="object"&&Q!==null&&Q.type==="overloaded_error")return!0;return!1}function QJ(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as yJ}from"ai";class q{model;config;_generate=yJ;constructor(J){if(this.config=J,this.model=null,J.provider==="bedrock"||J.apiKey)try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async evaluate(J,X){if(!this.model||X.length===0)return null;let $=e(J,X),Q=2;for(let Z=0;Z<=Q;Z++)try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:W}=await this._generate({model:this.model,maxOutputTokens:512,prompt:$});return r(W)}catch(W){if(fJ(W)&&Z<Q){let H=2**Z*1000;await vJ(H);continue}return null}return null}}function fJ(J){if(typeof J!=="object"||J===null)return!1;let X=J,$=X.status;if($===429||$===500||$===503)return!0;let Q=X.error;if(typeof Q==="object"&&Q!==null&&Q.type==="overloaded_error")return!0;return!1}function vJ(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as wJ}from"ai";class P{model;config;_generate=wJ;constructor(J){if(this.config=J,this.model=null,J.provider==="bedrock"||J.apiKey)try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async extract(J){if(!this.model)return null;let X=JJ(J),$=2;for(let Q=0;Q<=$;Q++)try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:Z}=await this._generate({model:this.model,maxOutputTokens:1024,prompt:X});return a(Z)}catch(Z){if(uJ(Z)&&Q<$){let W=2**Q*1000;await bJ(W);continue}return null}return null}}function uJ(J){if(typeof J!=="object"||J===null)return!1;let X=J,$=X.status;if($===429||$===500||$===503)return!0;let Q=X.error;if(typeof Q==="object"&&Q!==null&&Q.type==="overloaded_error")return!0;return!1}function bJ(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as gJ}from"ai";class x{model;config;_generate=gJ;constructor(J){this.config=J,this.model=null;let X=J.provider!=="bedrock";if(J.compressionEnabled&&(!X||J.apiKey))try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async summarize(J,X){if(X.length===0)return null;if(!this.config.compressionEnabled||!this.model)return this.createFallbackSummary(X);let $=t(X.map((Q)=>({type:Q.type,title:Q.title,narrative:Q.narrative})),J);try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:Q}=await this._generate({model:this.model,maxOutputTokens:this.config.maxTokensPerCompression,prompt:$}),Z=i(Q);if(!Z)return this.createFallbackSummary(X);return Z}catch{return this.createFallbackSummary(X)}}createFallbackSummary(J){let X=new Set,$=new Set,Q=[];for(let Y of J){for(let K of Y.filesModified)X.add(K);for(let K of Y.concepts)$.add(K);if(Y.type==="decision")Q.push(Y.title)}let Z=new Map;for(let Y of J)Z.set(Y.type,(Z.get(Y.type)??0)+1);let W=Array.from(Z.entries()).map(([Y,K])=>`${K} ${Y}${K>1?"s":""}`).join(", "),H=Array.from($).slice(0,5).join(", ");return{summary:`Session with ${J.length} observations: ${W}. Files modified: ${X.size}. Key concepts: ${H}.`,keyDecisions:Q.slice(0,5),filesModified:Array.from(X),concepts:Array.from($)}}shouldSummarize(J){return J>=2}}import{existsSync as mJ,readFileSync as pJ}from"fs";var lJ={dbPath:".open-mem/memory.db",provider:"google",apiKey:void 0,model:"gemini-2.5-flash-lite",maxTokensPerCompression:1024,compressionEnabled:!0,contextInjectionEnabled:!0,maxContextTokens:4000,batchSize:5,batchIntervalMs:30000,ignoredTools:[],minOutputLength:50,maxIndexEntries:20,sensitivePatterns:[],retentionDays:90,maxDatabaseSizeMb:500,logLevel:"warn",contextShowTokenCosts:!0,contextObservationTypes:"all",contextFullObservationCount:3,maxObservations:50,contextShowLastSummary:!0,rateLimitingEnabled:!0,folderContextEnabled:!0,folderContextMaxDepth:5,daemonEnabled:!1,dashboardEnabled:!1,dashboardPort:3737,embeddingDimension:void 0,conflictResolutionEnabled:!1,conflictSimilarityBandLow:0.7,conflictSimilarityBandHigh:0.92,userMemoryEnabled:!1,userMemoryDbPath:"~/.config/open-mem/user-memory.db",userMemoryMaxContextTokens:1000,rerankingEnabled:!1,rerankingMaxCandidates:20,entityExtractionEnabled:!1};function cJ(){let J={};if(process.env.OPEN_MEM_DB_PATH)J.dbPath=process.env.OPEN_MEM_DB_PATH;if(process.env.OPEN_MEM_PROVIDER)J.provider=process.env.OPEN_MEM_PROVIDER;if(process.env.OPEN_MEM_MODEL)J.model=process.env.OPEN_MEM_MODEL;if(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS)J.maxContextTokens=Number.parseInt(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS,10);if(process.env.OPEN_MEM_COMPRESSION==="false")J.compressionEnabled=!1;if(process.env.OPEN_MEM_CONTEXT_INJECTION==="false")J.contextInjectionEnabled=!1;if(process.env.OPEN_MEM_IGNORED_TOOLS)J.ignoredTools=process.env.OPEN_MEM_IGNORED_TOOLS.split(",").map((X)=>X.trim());if(process.env.OPEN_MEM_BATCH_SIZE)J.batchSize=Number.parseInt(process.env.OPEN_MEM_BATCH_SIZE,10);if(process.env.OPEN_MEM_RETENTION_DAYS)J.retentionDays=Number.parseInt(process.env.OPEN_MEM_RETENTION_DAYS,10);if(process.env.OPEN_MEM_LOG_LEVEL)J.logLevel=process.env.OPEN_MEM_LOG_LEVEL;if(process.env.OPEN_MEM_CONTEXT_SHOW_TOKEN_COSTS==="false")J.contextShowTokenCosts=!1;if(process.env.OPEN_MEM_CONTEXT_TYPES)J.contextObservationTypes=process.env.OPEN_MEM_CONTEXT_TYPES==="all"?"all":process.env.OPEN_MEM_CONTEXT_TYPES.split(",").map((X)=>X.trim());if(process.env.OPEN_MEM_CONTEXT_FULL_COUNT)J.contextFullObservationCount=Number.parseInt(process.env.OPEN_MEM_CONTEXT_FULL_COUNT,10);if(process.env.OPEN_MEM_MAX_OBSERVATIONS)J.maxObservations=Number.parseInt(process.env.OPEN_MEM_MAX_OBSERVATIONS,10);if(process.env.OPEN_MEM_CONTEXT_SHOW_LAST_SUMMARY==="false")J.contextShowLastSummary=!1;if(process.env.OPEN_MEM_RATE_LIMITING==="false")J.rateLimitingEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT==="false")J.folderContextEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH)J.folderContextMaxDepth=Number.parseInt(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH,10);if(process.env.OPEN_MEM_DAEMON==="true")J.daemonEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD==="true")J.dashboardEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD_PORT)J.dashboardPort=Number.parseInt(process.env.OPEN_MEM_DASHBOARD_PORT,10);if(process.env.OPEN_MEM_EMBEDDING_DIMENSION)J.embeddingDimension=Number.parseInt(process.env.OPEN_MEM_EMBEDDING_DIMENSION,10);if(process.env.OPEN_MEM_CONFLICT_RESOLUTION==="true")J.conflictResolutionEnabled=!0;if(process.env.OPEN_MEM_CONFLICT_BAND_LOW){let X=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_LOW);if(!Number.isNaN(X))J.conflictSimilarityBandLow=X}if(process.env.OPEN_MEM_CONFLICT_BAND_HIGH){let X=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_HIGH);if(!Number.isNaN(X))J.conflictSimilarityBandHigh=X}if(process.env.OPEN_MEM_USER_MEMORY==="true")J.userMemoryEnabled=!0;if(process.env.OPEN_MEM_USER_MEMORY_DB_PATH)J.userMemoryDbPath=process.env.OPEN_MEM_USER_MEMORY_DB_PATH;if(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS)J.userMemoryMaxContextTokens=Number.parseInt(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS,10);if(process.env.OPEN_MEM_RERANKING==="true")J.rerankingEnabled=!0;if(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES)J.rerankingMaxCandidates=Number.parseInt(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES,10);if(process.env.OPEN_MEM_ENTITY_EXTRACTION==="true")J.entityExtractionEnabled=!0;return J}function dJ(J){let X=`${J}/.open-mem/config.json`;if(!mJ(X))return{};try{let $=pJ(X,"utf-8"),Q=JSON.parse($);if(!Q||typeof Q!=="object"||Array.isArray(Q))return{};return Q}catch{return{}}}function nJ(J){switch(J){case"google":return 768;case"openai":return 1536;case"bedrock":return 1024;case"anthropic":return 0;default:return 768}}function ZJ(J,X){let $=dJ(J),Q=cJ(),Z={...lJ,...$,...Q,...X};if(!Z.dbPath.startsWith("/"))Z.dbPath=`${J}/${Z.dbPath}`;if(!process.env.OPEN_MEM_PROVIDER&&!X?.provider){if(process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY)Z.provider="google";else if(process.env.ANTHROPIC_API_KEY)Z.provider="anthropic";else if(process.env.AWS_BEARER_TOKEN_BEDROCK||process.env.AWS_ACCESS_KEY_ID||process.env.AWS_PROFILE)Z.provider="bedrock"}if(!Z.apiKey)switch(Z.provider){case"google":Z.apiKey=process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY;break;case"anthropic":Z.apiKey=process.env.ANTHROPIC_API_KEY;break;case"openai":Z.apiKey=process.env.OPENAI_API_KEY;break;case"bedrock":break}if(Z.embeddingDimension===void 0)Z.embeddingDimension=nJ(Z.provider);return Z}import{existsSync as eX,mkdirSync as sJ,readFileSync as J2,unlinkSync as iJ,writeFileSync as rJ}from"fs";function WJ(J){let X=J.lastIndexOf("/");if(X>0){let $=J.substring(0,X);sJ($,{recursive:!0})}rJ(J,String(process.pid),"utf-8")}function HJ(J){try{iJ(J)}catch{}}function YJ(J){let X=J.lastIndexOf("/");if(X>=0)return`${J.substring(0,X)}/worker.pid`;return"worker.pid"}class I{queueProcessor;pollIntervalMs;timer=null;lastActiveAt=Date.now();processing=!1;constructor(J){this.queueProcessor=J.queueProcessor,this.pollIntervalMs=J.pollIntervalMs}start(){if(this.timer)return;this.lastActiveAt=Date.now(),this.timer=setInterval(async()=>{if(this.processing)return;this.processing=!0;try{if(await this.queueProcessor.processBatch()>0)this.lastActiveAt=Date.now()}catch{}finally{this.processing=!1}},this.pollIntervalMs)}stop(){if(this.timer)clearInterval(this.timer),this.timer=null}get isRunning(){return this.timer!==null}get idleMs(){return Date.now()-this.lastActiveAt}get shouldAutoExit(){return this.idleMs>=60000&&!process.send}handleMessage(J){if(J==="SHUTDOWN")this.stop();else if(J==="PROCESS_NOW"){if(!this.processing)this.processing=!0,this.queueProcessor.processBatch().then((X)=>{if(X>0)this.lastActiveAt=Date.now()}).catch(()=>{}).finally(()=>{this.processing=!1})}}}import{Database as KJ}from"bun:sqlite";import{existsSync as h,mkdirSync as aJ,unlinkSync as VJ}from"fs";import*as zJ from"sqlite-vec";class O{db;dbPath;_hasVectorExtension=!1;static enableExtensionSupport(){let J=["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib","/usr/local/opt/sqlite/lib/libsqlite3.dylib"];for(let X of J)try{if(h(X))return KJ.setCustomSQLite(X),!0}catch{return!1}return!1}constructor(J){this.dbPath=J,this.db=this.open(J),this.configure()}open(J){let X=J.lastIndexOf("/");if(X>0){let $=J.substring(0,X);aJ($,{recursive:!0})}return new KJ(J,{create:!0})}configure(){try{this.applyPragmas(),this.loadExtensions()}catch(J){console.warn("[open-mem] Database configure failed, attempting recovery by removing WAL/SHM files:",J.message);try{this.db.close()}catch{}this.deleteSidecarFiles();try{this.db=this.open(this.dbPath),this.applyPragmas(),this.loadExtensions(),console.warn("[open-mem] Recovery successful after removing WAL/SHM files");return}catch(X){console.warn("[open-mem] WAL/SHM cleanup insufficient, recreating database from scratch:",X.message);try{this.db.close()}catch{}this.deleteDatabaseFiles();try{this.db=this.open(this.dbPath),this.applyPragmas(),this.loadExtensions(),console.warn("[open-mem] Recovery successful after full database recreation");return}catch($){throw console.warn("[open-mem] All recovery attempts failed, filesystem may be broken:",$.message),J}}}}applyPragmas(){this.db.exec("PRAGMA journal_mode = WAL"),this.db.exec("PRAGMA synchronous = NORMAL"),this.db.exec("PRAGMA foreign_keys = ON"),this.db.exec("PRAGMA busy_timeout = 5000")}loadExtensions(){try{zJ.load(this.db),this._hasVectorExtension=!0}catch{this._hasVectorExtension=!1}}get hasVectorExtension(){return this._hasVectorExtension}deleteSidecarFiles(){for(let J of["-wal","-shm"]){let X=this.dbPath+J;try{if(h(X))VJ(X)}catch{}}}deleteDatabaseFiles(){this.deleteSidecarFiles();try{if(h(this.dbPath))VJ(this.dbPath)}catch{}}ensureMigrationTable(){this.db.exec(`
147
+ [... truncated ...]`:X,H=e(J,Z,$),W=2;for(let Y=0;Y<=W;Y++)try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:K}=await this._generate({model:this.model,maxOutputTokens:this.config.maxTokensPerCompression,prompt:H}),V=r(K);if(V)V.discoveryTokens=Q;return V}catch(K){if(fJ(K)&&Y<W){let V=2**Y*1000;await WJ(V);continue}return null}return null}async compressBatch(J){let X=new Map;for(let $=0;$<J.length;$++){let Q=J[$],Z=await this.compress(Q.toolName,Q.toolOutput,Q.sessionContext);if(X.set(Q.callId,Z),$<J.length-1)await WJ(200)}return X}createFallbackObservation(J,X){let $=yJ(X),Q=IJ[J]??"discovery";return{type:Q,title:`${J} execution`,subtitle:X.substring(0,100).replace(/\n/g," "),facts:[],narrative:`Tool ${J} was executed. Output length: ${X.length} chars.`,concepts:[],filesRead:Q==="discovery"?$:[],filesModified:Q==="change"?$:[],discoveryTokens:E(X),importance:2}}async isAvailable(){if(!this.model)return!1;try{return await this._generate({model:this.model,maxOutputTokens:10,prompt:"ping"}),!0}catch{return!1}}}var IJ={Read:"discovery",Write:"change",Edit:"change",Bash:"change",Glob:"discovery",Grep:"discovery"},hJ=/(?:^|\s)((?:\.\/|\/|src\/|tests\/|lib\/)\S+\.\w+)/gm;function yJ(J){let X=[];for(let $ of J.matchAll(hJ))X.push($[1]);return[...new Set(X)]}function fJ(J){if(typeof J!=="object"||J===null)return!1;let X=J,$=X.status;if($===429||$===500||$===503)return!0;let Q=X.error;if(typeof Q==="object"&&Q!==null&&Q.type==="overloaded_error")return!0;return!1}function WJ(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as wJ}from"ai";class x{model;config;_generate=wJ;constructor(J){if(this.config=J,this.model=null,J.provider==="bedrock"||J.apiKey)try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async evaluate(J,X){if(!this.model||X.length===0)return null;let $=XJ(J,X),Q=2;for(let Z=0;Z<=Q;Z++)try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:H}=await this._generate({model:this.model,maxOutputTokens:512,prompt:$});return o(H)}catch(H){if(vJ(H)&&Z<Q){let W=2**Z*1000;await bJ(W);continue}return null}return null}}function vJ(J){if(typeof J!=="object"||J===null)return!1;let X=J,$=X.status;if($===429||$===500||$===503)return!0;let Q=X.error;if(typeof Q==="object"&&Q!==null&&Q.type==="overloaded_error")return!0;return!1}function bJ(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as uJ}from"ai";class I{model;config;_generate=uJ;constructor(J){if(this.config=J,this.model=null,J.provider==="bedrock"||J.apiKey)try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async extract(J){if(!this.model)return null;let X=$J(J),$=2;for(let Q=0;Q<=$;Q++)try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:Z}=await this._generate({model:this.model,maxOutputTokens:1024,prompt:X});return t(Z)}catch(Z){if(gJ(Z)&&Q<$){let H=2**Q*1000;await mJ(H);continue}return null}return null}}function gJ(J){if(typeof J!=="object"||J===null)return!1;let X=J,$=X.status;if($===429||$===500||$===503)return!0;let Q=X.error;if(typeof Q==="object"&&Q!==null&&Q.type==="overloaded_error")return!0;return!1}function mJ(J){return new Promise((X)=>setTimeout(X,J))}import{generateText as pJ}from"ai";class h{model;config;_generate=pJ;constructor(J){this.config=J,this.model=null;let X=J.provider!=="bedrock";if(J.compressionEnabled&&(!X||J.apiKey))try{this.model=M({provider:J.provider,model:J.model,apiKey:J.apiKey})}catch{}}async summarize(J,X){if(X.length===0)return null;if(!this.config.compressionEnabled||!this.model)return this.createFallbackSummary(X);let $=JJ(X.map((Q)=>({type:Q.type,title:Q.title,narrative:Q.narrative})),J);try{if(this.config.provider==="google")await A(this.config.model,this.config.rateLimitingEnabled);let{text:Q}=await this._generate({model:this.model,maxOutputTokens:this.config.maxTokensPerCompression,prompt:$}),Z=a(Q);if(!Z)return this.createFallbackSummary(X);return Z}catch{return this.createFallbackSummary(X)}}createFallbackSummary(J){let X=new Set,$=new Set,Q=[];for(let Y of J){for(let K of Y.filesModified)X.add(K);for(let K of Y.concepts)$.add(K);if(Y.type==="decision")Q.push(Y.title)}let Z=new Map;for(let Y of J)Z.set(Y.type,(Z.get(Y.type)??0)+1);let H=Array.from(Z.entries()).map(([Y,K])=>`${K} ${Y}${K>1?"s":""}`).join(", "),W=Array.from($).slice(0,5).join(", ");return{summary:`Session with ${J.length} observations: ${H}. Files modified: ${X.size}. Key concepts: ${W}.`,keyDecisions:Q.slice(0,5),filesModified:Array.from(X),concepts:Array.from($)}}shouldSummarize(J){return J>=2}}import{existsSync as lJ,readFileSync as cJ}from"fs";var dJ={dbPath:".open-mem/memory.db",provider:"google",apiKey:void 0,model:"gemini-2.5-flash-lite",maxTokensPerCompression:1024,compressionEnabled:!0,contextInjectionEnabled:!0,maxContextTokens:4000,batchSize:5,batchIntervalMs:30000,ignoredTools:[],minOutputLength:50,maxIndexEntries:20,sensitivePatterns:[],retentionDays:90,maxDatabaseSizeMb:500,logLevel:"warn",contextShowTokenCosts:!0,contextObservationTypes:"all",contextFullObservationCount:3,maxObservations:50,contextShowLastSummary:!0,rateLimitingEnabled:!0,folderContextEnabled:!0,folderContextMaxDepth:5,daemonEnabled:!1,dashboardEnabled:!1,dashboardPort:3737,platformOpenCodeEnabled:!0,platformClaudeCodeEnabled:!1,platformCursorEnabled:!1,mcpCompatibilityMode:"strict",mcpProtocolVersion:"2024-11-05",mcpSupportedProtocolVersions:["2024-11-05"],embeddingDimension:void 0,conflictResolutionEnabled:!1,conflictSimilarityBandLow:0.7,conflictSimilarityBandHigh:0.92,userMemoryEnabled:!1,userMemoryDbPath:"~/.config/open-mem/user-memory.db",userMemoryMaxContextTokens:1000,rerankingEnabled:!1,rerankingMaxCandidates:20,entityExtractionEnabled:!1};function nJ(){let J={};if(process.env.OPEN_MEM_DB_PATH)J.dbPath=process.env.OPEN_MEM_DB_PATH;if(process.env.OPEN_MEM_PROVIDER)J.provider=process.env.OPEN_MEM_PROVIDER;if(process.env.OPEN_MEM_MODEL)J.model=process.env.OPEN_MEM_MODEL;if(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS)J.maxContextTokens=Number.parseInt(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS,10);if(process.env.OPEN_MEM_COMPRESSION==="false")J.compressionEnabled=!1;if(process.env.OPEN_MEM_CONTEXT_INJECTION==="false")J.contextInjectionEnabled=!1;if(process.env.OPEN_MEM_IGNORED_TOOLS)J.ignoredTools=process.env.OPEN_MEM_IGNORED_TOOLS.split(",").map((X)=>X.trim());if(process.env.OPEN_MEM_BATCH_SIZE)J.batchSize=Number.parseInt(process.env.OPEN_MEM_BATCH_SIZE,10);if(process.env.OPEN_MEM_RETENTION_DAYS)J.retentionDays=Number.parseInt(process.env.OPEN_MEM_RETENTION_DAYS,10);if(process.env.OPEN_MEM_LOG_LEVEL)J.logLevel=process.env.OPEN_MEM_LOG_LEVEL;if(process.env.OPEN_MEM_CONTEXT_SHOW_TOKEN_COSTS==="false")J.contextShowTokenCosts=!1;if(process.env.OPEN_MEM_CONTEXT_TYPES)J.contextObservationTypes=process.env.OPEN_MEM_CONTEXT_TYPES==="all"?"all":process.env.OPEN_MEM_CONTEXT_TYPES.split(",").map((X)=>X.trim());if(process.env.OPEN_MEM_CONTEXT_FULL_COUNT)J.contextFullObservationCount=Number.parseInt(process.env.OPEN_MEM_CONTEXT_FULL_COUNT,10);if(process.env.OPEN_MEM_MAX_OBSERVATIONS)J.maxObservations=Number.parseInt(process.env.OPEN_MEM_MAX_OBSERVATIONS,10);if(process.env.OPEN_MEM_CONTEXT_SHOW_LAST_SUMMARY==="false")J.contextShowLastSummary=!1;if(process.env.OPEN_MEM_RATE_LIMITING==="false")J.rateLimitingEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT==="false")J.folderContextEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH)J.folderContextMaxDepth=Number.parseInt(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH,10);if(process.env.OPEN_MEM_DAEMON==="true")J.daemonEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD==="true")J.dashboardEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD_PORT)J.dashboardPort=Number.parseInt(process.env.OPEN_MEM_DASHBOARD_PORT,10);if(process.env.OPEN_MEM_PLATFORM_OPENCODE==="false")J.platformOpenCodeEnabled=!1;if(process.env.OPEN_MEM_PLATFORM_CLAUDE_CODE==="true")J.platformClaudeCodeEnabled=!0;if(process.env.OPEN_MEM_PLATFORM_CURSOR==="true")J.platformCursorEnabled=!0;if(process.env.OPEN_MEM_MCP_COMPAT_MODE)J.mcpCompatibilityMode=process.env.OPEN_MEM_MCP_COMPAT_MODE;if(process.env.OPEN_MEM_MCP_PROTOCOL_VERSION)J.mcpProtocolVersion=process.env.OPEN_MEM_MCP_PROTOCOL_VERSION;if(process.env.OPEN_MEM_MCP_SUPPORTED_PROTOCOLS)J.mcpSupportedProtocolVersions=process.env.OPEN_MEM_MCP_SUPPORTED_PROTOCOLS.split(",").map((X)=>X.trim()).filter(Boolean);if(process.env.OPEN_MEM_EMBEDDING_DIMENSION)J.embeddingDimension=Number.parseInt(process.env.OPEN_MEM_EMBEDDING_DIMENSION,10);if(process.env.OPEN_MEM_CONFLICT_RESOLUTION==="true")J.conflictResolutionEnabled=!0;if(process.env.OPEN_MEM_CONFLICT_BAND_LOW){let X=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_LOW);if(!Number.isNaN(X))J.conflictSimilarityBandLow=X}if(process.env.OPEN_MEM_CONFLICT_BAND_HIGH){let X=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_HIGH);if(!Number.isNaN(X))J.conflictSimilarityBandHigh=X}if(process.env.OPEN_MEM_USER_MEMORY==="true")J.userMemoryEnabled=!0;if(process.env.OPEN_MEM_USER_MEMORY_DB_PATH)J.userMemoryDbPath=process.env.OPEN_MEM_USER_MEMORY_DB_PATH;if(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS)J.userMemoryMaxContextTokens=Number.parseInt(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS,10);if(process.env.OPEN_MEM_RERANKING==="true")J.rerankingEnabled=!0;if(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES)J.rerankingMaxCandidates=Number.parseInt(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES,10);if(process.env.OPEN_MEM_ENTITY_EXTRACTION==="true")J.entityExtractionEnabled=!0;return J}function sJ(J){let X=`${J}/.open-mem/config.json`;if(!lJ(X))return{};try{let $=cJ(X,"utf-8"),Q=JSON.parse($);if(!Q||typeof Q!=="object"||Array.isArray(Q))return{};return Q}catch{return{}}}function iJ(J){switch(J){case"google":return 768;case"openai":return 1536;case"bedrock":return 1024;case"anthropic":return 0;default:return 768}}function HJ(J,X){let $=sJ(J),Q=nJ(),Z={...dJ,...$,...Q,...X};if(!Z.dbPath.startsWith("/"))Z.dbPath=`${J}/${Z.dbPath}`;if(!process.env.OPEN_MEM_PROVIDER&&!X?.provider){if(process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY)Z.provider="google";else if(process.env.ANTHROPIC_API_KEY)Z.provider="anthropic";else if(process.env.AWS_BEARER_TOKEN_BEDROCK||process.env.AWS_ACCESS_KEY_ID||process.env.AWS_PROFILE)Z.provider="bedrock"}if(!Z.apiKey)switch(Z.provider){case"google":Z.apiKey=process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY;break;case"anthropic":Z.apiKey=process.env.ANTHROPIC_API_KEY;break;case"openai":Z.apiKey=process.env.OPENAI_API_KEY;break;case"bedrock":break}if(Z.embeddingDimension===void 0)Z.embeddingDimension=iJ(Z.provider);return Z}import{existsSync as J2,mkdirSync as rJ,readFileSync as X2,unlinkSync as aJ,writeFileSync as oJ}from"fs";function YJ(J){let X=J.lastIndexOf("/");if(X>0){let $=J.substring(0,X);rJ($,{recursive:!0})}oJ(J,String(process.pid),"utf-8")}function KJ(J){try{aJ(J)}catch{}}function VJ(J){let X=J.lastIndexOf("/");if(X>=0)return`${J.substring(0,X)}/worker.pid`;return"worker.pid"}class y{queueProcessor;pollIntervalMs;timer=null;lastActiveAt=Date.now();processing=!1;constructor(J){this.queueProcessor=J.queueProcessor,this.pollIntervalMs=J.pollIntervalMs}start(){if(this.timer)return;this.lastActiveAt=Date.now(),this.timer=setInterval(async()=>{if(this.processing)return;this.processing=!0;try{if(await this.queueProcessor.processBatch()>0)this.lastActiveAt=Date.now()}catch{}finally{this.processing=!1}},this.pollIntervalMs)}stop(){if(this.timer)clearInterval(this.timer),this.timer=null}get isRunning(){return this.timer!==null}get idleMs(){return Date.now()-this.lastActiveAt}get shouldAutoExit(){return this.idleMs>=60000&&!process.send}handleMessage(J){if(J==="SHUTDOWN")this.stop();else if(J==="PROCESS_NOW"){if(!this.processing)this.processing=!0,this.queueProcessor.processBatch().then((X)=>{if(X>0)this.lastActiveAt=Date.now()}).catch(()=>{}).finally(()=>{this.processing=!1})}}}import{Database as zJ}from"bun:sqlite";import{existsSync as f,mkdirSync as tJ,unlinkSync as BJ}from"fs";import*as GJ from"sqlite-vec";class q{db;dbPath;_hasVectorExtension=!1;static enableExtensionSupport(){let J=["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib","/usr/local/opt/sqlite/lib/libsqlite3.dylib"];for(let X of J)try{if(f(X))return zJ.setCustomSQLite(X),!0}catch{return!1}return!1}constructor(J){this.dbPath=J,this.db=this.open(J),this.configure()}open(J){let X=J.lastIndexOf("/");if(X>0){let $=J.substring(0,X);tJ($,{recursive:!0})}return new zJ(J,{create:!0})}configure(){try{this.applyPragmas(),this.loadExtensions()}catch(J){console.warn("[open-mem] Database configure failed, attempting recovery by removing WAL/SHM files:",J.message);try{this.db.close()}catch{}this.deleteSidecarFiles();try{this.db=this.open(this.dbPath),this.applyPragmas(),this.loadExtensions(),console.warn("[open-mem] Recovery successful after removing WAL/SHM files");return}catch(X){console.warn("[open-mem] WAL/SHM cleanup insufficient, recreating database from scratch:",X.message);try{this.db.close()}catch{}this.deleteDatabaseFiles();try{this.db=this.open(this.dbPath),this.applyPragmas(),this.loadExtensions(),console.warn("[open-mem] Recovery successful after full database recreation");return}catch($){throw console.warn("[open-mem] All recovery attempts failed, filesystem may be broken:",$.message),J}}}}applyPragmas(){this.db.exec("PRAGMA journal_mode = WAL"),this.db.exec("PRAGMA synchronous = NORMAL"),this.db.exec("PRAGMA foreign_keys = ON"),this.db.exec("PRAGMA busy_timeout = 5000")}loadExtensions(){try{GJ.load(this.db),this._hasVectorExtension=!0}catch{this._hasVectorExtension=!1}}get hasVectorExtension(){return this._hasVectorExtension}deleteSidecarFiles(){for(let J of["-wal","-shm"]){let X=this.dbPath+J;try{if(f(X))BJ(X)}catch{}}}deleteDatabaseFiles(){this.deleteSidecarFiles();try{if(f(this.dbPath))BJ(this.dbPath)}catch{}}ensureMigrationTable(){this.db.exec(`
148
148
  CREATE TABLE IF NOT EXISTS _migrations (
149
149
  version INTEGER PRIMARY KEY,
150
150
  name TEXT NOT NULL,
151
151
  applied_at TEXT NOT NULL DEFAULT (datetime('now'))
152
152
  )
153
- `)}migrate(J){this.ensureMigrationTable();let X=this.db.query("SELECT version FROM _migrations ORDER BY version").all(),$=new Set(X.map((Z)=>Z.version)),Q=J.filter((Z)=>!$.has(Z.version)).sort((Z,W)=>Z.version-W.version);for(let Z of Q)this.db.transaction(()=>{this.db.exec(Z.up),this.db.query("INSERT INTO _migrations (version, name) VALUES ($version, $name)").run({$version:Z.version,$name:Z.name})})()}run(J,X){let $=this.db.query(J);if(X)$.run(...X);else $.run()}get(J,X){let $=this.db.query(J);return X?$.get(...X):$.get()}all(J,X){let $=this.db.query(J);return X?$.all(...X):$.all()}exec(J){this.db.exec(J)}transaction(J){return this.db.transaction(J)()}close(){this.db.close()}get isOpen(){try{return this.db.query("SELECT 1").get(),!0}catch{return!1}}get raw(){return this.db}}function BJ(J){return new O(J)}import{randomUUID as GJ}from"crypto";class y{db;constructor(J){this.db=J}upsertEntity(J,X){let $=GJ(),Q=new Date().toISOString();this.db.run(`INSERT INTO entities (id, name, entity_type, first_seen_at, last_seen_at, mention_count)
153
+ `)}migrate(J){this.ensureMigrationTable();let X=this.db.query("SELECT version FROM _migrations ORDER BY version").all(),$=new Set(X.map((Z)=>Z.version)),Q=J.filter((Z)=>!$.has(Z.version)).sort((Z,H)=>Z.version-H.version);for(let Z of Q)this.db.transaction(()=>{this.db.exec(Z.up),this.db.query("INSERT INTO _migrations (version, name) VALUES ($version, $name)").run({$version:Z.version,$name:Z.name})})()}run(J,X){let $=this.db.query(J);if(X)$.run(...X);else $.run()}get(J,X){let $=this.db.query(J);return X?$.get(...X):$.get()}all(J,X){let $=this.db.query(J);return X?$.all(...X):$.all()}exec(J){this.db.exec(J)}transaction(J){return this.db.transaction(J)()}close(){this.db.close()}get isOpen(){try{return this.db.query("SELECT 1").get(),!0}catch{return!1}}get raw(){return this.db}}function UJ(J){return new q(J)}import{randomUUID as FJ}from"crypto";class w{db;constructor(J){this.db=J}upsertEntity(J,X){let $=FJ(),Q=new Date().toISOString();this.db.run(`INSERT INTO entities (id, name, entity_type, first_seen_at, last_seen_at, mention_count)
154
154
  VALUES (?, ?, ?, ?, ?, 1)
155
155
  ON CONFLICT(name, entity_type) DO UPDATE SET
156
156
  mention_count = mention_count + 1,
157
- last_seen_at = ?`,[$,J,X,Q,Q,Q]);let Z=this.db.get("SELECT * FROM entities WHERE name = ? AND entity_type = ?",[J,X]);if(!Z)throw Error(`Failed to upsert entity: ${J} (${X})`);return this.mapEntityRow(Z)}createRelation(J,X,$,Q){let Z=GJ(),W=new Date().toISOString();try{this.db.run(`INSERT OR IGNORE INTO entity_relations
157
+ last_seen_at = ?`,[$,J,X,Q,Q,Q]);let Z=this.db.get("SELECT * FROM entities WHERE name = ? AND entity_type = ?",[J,X]);if(!Z)throw Error(`Failed to upsert entity: ${J} (${X})`);return this.mapEntityRow(Z)}createRelation(J,X,$,Q){let Z=FJ(),H=new Date().toISOString();try{this.db.run(`INSERT OR IGNORE INTO entity_relations
158
158
  (id, source_entity_id, target_entity_id, relationship, observation_id, created_at)
159
- VALUES (?, ?, ?, ?, ?, ?)`,[Z,J,X,$,Q,W])}catch{return null}let H=this.db.get(`SELECT * FROM entity_relations
160
- WHERE source_entity_id = ? AND target_entity_id = ? AND relationship = ?`,[J,X,$]);return H?this.mapRelationRow(H):null}linkObservation(J,X){this.db.run("INSERT OR IGNORE INTO entity_observations (entity_id, observation_id) VALUES (?, ?)",[J,X])}findByName(J){try{return this.db.all(`SELECT e.*
159
+ VALUES (?, ?, ?, ?, ?, ?)`,[Z,J,X,$,Q,H])}catch{return null}let W=this.db.get(`SELECT * FROM entity_relations
160
+ WHERE source_entity_id = ? AND target_entity_id = ? AND relationship = ?`,[J,X,$]);return W?this.mapRelationRow(W):null}linkObservation(J,X){this.db.run("INSERT OR IGNORE INTO entity_observations (entity_id, observation_id) VALUES (?, ?)",[J,X])}findByName(J){try{return this.db.all(`SELECT e.*
161
161
  FROM entities e
162
162
  JOIN entities_fts fts ON e._rowid = fts.rowid
163
163
  WHERE entities_fts MATCH ?
164
164
  ORDER BY rank`,[J]).map(($)=>this.mapEntityRow($))}catch{return[]}}getRelationsFor(J){return this.db.all(`SELECT * FROM entity_relations
165
- WHERE source_entity_id = ? OR target_entity_id = ?`,[J,J]).map(($)=>this.mapRelationRow($))}traverseRelations(J,X=1){let $=Math.min(X,2),Q=100,Z=new Set,W=[{id:J,currentDepth:0}];Z.add(J);while(W.length>0){if(Z.size>=100)break;let H=W.shift();if(H.currentDepth>=$)continue;let Y=this.getRelationsFor(H.id);for(let K of Y){let z=K.sourceEntityId===H.id?K.targetEntityId:K.sourceEntityId;if(!Z.has(z))Z.add(z),W.push({id:z,currentDepth:H.currentDepth+1})}}return Z}getObservationsForEntity(J){return this.db.all("SELECT observation_id FROM entity_observations WHERE entity_id = ?",[J]).map(($)=>$.observation_id)}getById(J){let X=this.db.get("SELECT * FROM entities WHERE id = ?",[J]);return X?this.mapEntityRow(X):null}mapEntityRow(J){return{id:J.id,name:J.name,entityType:J.entity_type,firstSeenAt:J.first_seen_at,lastSeenAt:J.last_seen_at,mentionCount:J.mention_count}}mapRelationRow(J){return{id:J.id,sourceEntityId:J.source_entity_id,targetEntityId:J.target_entity_id,relationship:J.relationship,observationId:J.observation_id,createdAt:J.created_at}}}import{randomUUID as tJ}from"crypto";import{embed as oJ}from"ai";async function f(J,X){try{let{embedding:$}=await oJ({model:J,value:X});return $}catch{return null}}function UJ(J,X){if(J.length!==X.length||J.length===0)return 0;let $=0,Q=0,Z=0;for(let H=0;H<J.length;H++)$+=J[H]*X[H],Q+=J[H]*J[H],Z+=X[H]*X[H];let W=Math.sqrt(Q)*Math.sqrt(Z);if(W===0)return 0;return $/W}function v(J){let X=[J.title,J.narrative];if(J.concepts.length>0)X.push(J.concepts.join(", "));return X.join(`
166
- `)}function eJ(J){return J.replace(/[%_\\]/g,"\\$&")}class w{db;constructor(J){this.db=J}create(J){let X=tJ(),$=new Date().toISOString(),Q=J.discoveryTokens??0,Z=J.importance??3,W=J.scope??"project";return this.db.run(`INSERT INTO observations
165
+ WHERE source_entity_id = ? OR target_entity_id = ?`,[J,J]).map(($)=>this.mapRelationRow($))}traverseRelations(J,X=1){let $=Math.min(X,2),Q=100,Z=new Set,H=[{id:J,currentDepth:0}];Z.add(J);while(H.length>0){if(Z.size>=100)break;let W=H.shift();if(!W)continue;if(W.currentDepth>=$)continue;let Y=this.getRelationsFor(W.id);for(let K of Y){let V=K.sourceEntityId===W.id?K.targetEntityId:K.sourceEntityId;if(!Z.has(V))Z.add(V),H.push({id:V,currentDepth:W.currentDepth+1})}}return Z}getObservationsForEntity(J){return this.db.all("SELECT observation_id FROM entity_observations WHERE entity_id = ?",[J]).map(($)=>$.observation_id)}getById(J){let X=this.db.get("SELECT * FROM entities WHERE id = ?",[J]);return X?this.mapEntityRow(X):null}mapEntityRow(J){return{id:J.id,name:J.name,entityType:J.entity_type,firstSeenAt:J.first_seen_at,lastSeenAt:J.last_seen_at,mentionCount:J.mention_count}}mapRelationRow(J){return{id:J.id,sourceEntityId:J.source_entity_id,targetEntityId:J.target_entity_id,relationship:J.relationship,observationId:J.observation_id,createdAt:J.created_at}}}import{randomUUID as JX}from"crypto";import{embed as eJ}from"ai";async function v(J,X){try{let{embedding:$}=await eJ({model:J,value:X});return $}catch{return null}}function _J(J,X){if(J.length!==X.length||J.length===0)return 0;let $=0,Q=0,Z=0;for(let W=0;W<J.length;W++)$+=J[W]*X[W],Q+=J[W]*J[W],Z+=X[W]*X[W];let H=Math.sqrt(Q)*Math.sqrt(Z);if(H===0)return 0;return $/H}function b(J){let X=[J.title,J.narrative];if(J.concepts.length>0)X.push(J.concepts.join(", "));return X.join(`
166
+ `)}function XX(J){return J.replace(/[%_\\]/g,"\\$&")}class u{db;constructor(J){this.db=J}create(J){let X=JX(),$=new Date().toISOString(),Q=J.discoveryTokens??0,Z=J.importance??3,H=J.scope??"project";return this.db.run(`INSERT INTO observations
167
167
  (id, session_id, scope, type, title, subtitle, facts, narrative,
168
168
  concepts, files_read, files_modified, raw_tool_output,
169
169
  tool_name, created_at, token_count, discovery_tokens, importance, revision_of, deleted_at)
170
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[X,J.sessionId,W,J.type,J.title,J.subtitle,JSON.stringify(J.facts),J.narrative,JSON.stringify(J.concepts),JSON.stringify(J.filesRead),JSON.stringify(J.filesModified),J.rawToolOutput,J.toolName,$,J.tokenCount,Q,Z,null,null]),{...J,id:X,scope:W,createdAt:$,discoveryTokens:Q,importance:Z,revisionOf:null,deletedAt:null,supersededBy:null,supersededAt:null}}importObservation(J){this.db.run(`INSERT INTO observations
170
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[X,J.sessionId,H,J.type,J.title,J.subtitle,JSON.stringify(J.facts),J.narrative,JSON.stringify(J.concepts),JSON.stringify(J.filesRead),JSON.stringify(J.filesModified),J.rawToolOutput,J.toolName,$,J.tokenCount,Q,Z,null,null]),{...J,id:X,scope:H,createdAt:$,discoveryTokens:Q,importance:Z,revisionOf:null,deletedAt:null,supersededBy:null,supersededAt:null}}importObservation(J){this.db.run(`INSERT INTO observations
171
171
  (id, session_id, scope, type, title, subtitle, facts, narrative,
172
172
  concepts, files_read, files_modified, raw_tool_output,
173
173
  tool_name, created_at, token_count, discovery_tokens, importance, revision_of, deleted_at)
174
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[J.id,J.sessionId,J.scope??"project",J.type,J.title,J.subtitle,JSON.stringify(J.facts),J.narrative,JSON.stringify(J.concepts),JSON.stringify(J.filesRead),JSON.stringify(J.filesModified),J.rawToolOutput,J.toolName,J.createdAt,J.tokenCount,J.discoveryTokens??0,J.importance??3,J.revisionOf??null,J.deletedAt??null])}getById(J){let X=this.db.get("SELECT * FROM observations WHERE id = ? AND superseded_by IS NULL AND deleted_at IS NULL",[J]);return X?this.mapRow(X):null}getBySession(J){return this.db.all("SELECT * FROM observations WHERE session_id = ? AND superseded_by IS NULL AND deleted_at IS NULL ORDER BY created_at ASC",[J]).map((X)=>this.mapRow(X))}getCount(J){if(J)return this.db.get("SELECT COUNT(*) as count FROM observations WHERE session_id = ?",[J])?.count??0;return this.db.get("SELECT COUNT(*) as count FROM observations")?.count??0}getIndex(J,X=20){return this.db.all(`SELECT o.id, o.session_id, o.type, o.title, o.token_count, o.discovery_tokens, o.created_at, o.importance
174
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[J.id,J.sessionId,J.scope??"project",J.type,J.title,J.subtitle,JSON.stringify(J.facts),J.narrative,JSON.stringify(J.concepts),JSON.stringify(J.filesRead),JSON.stringify(J.filesModified),J.rawToolOutput,J.toolName,J.createdAt,J.tokenCount,J.discoveryTokens??0,J.importance??3,J.revisionOf??null,J.deletedAt??null])}getById(J){let X=this.db.get("SELECT * FROM observations WHERE id = ? AND superseded_by IS NULL AND deleted_at IS NULL",[J]);return X?this.mapRow(X):null}getByIdIncludingArchived(J){let X=this.db.get("SELECT * FROM observations WHERE id = ?",[J]);return X?this.mapRow(X):null}getBySession(J){return this.db.all("SELECT * FROM observations WHERE session_id = ? AND superseded_by IS NULL AND deleted_at IS NULL ORDER BY created_at ASC",[J]).map((X)=>this.mapRow(X))}getCount(J){if(J)return this.db.get("SELECT COUNT(*) as count FROM observations WHERE session_id = ?",[J])?.count??0;return this.db.get("SELECT COUNT(*) as count FROM observations")?.count??0}getIndex(J,X=20){return this.db.all(`SELECT o.id, o.session_id, o.type, o.title, o.token_count, o.discovery_tokens, o.created_at, o.importance
175
175
  FROM observations o
176
176
  JOIN sessions s ON o.session_id = s.id
177
177
  WHERE s.project_path = ? AND o.superseded_by IS NULL AND o.deleted_at IS NULL
178
178
  ORDER BY o.created_at DESC
179
- LIMIT ?`,[J,X]).map(($)=>({id:$.id,sessionId:$.session_id,type:$.type,title:$.title,tokenCount:$.token_count,discoveryTokens:$.discovery_tokens??0,createdAt:$.created_at,importance:$.importance??3}))}search(J){let X=!!J.projectPath,$=`
179
+ LIMIT ?`,[J,X]).map(($)=>({id:$.id,sessionId:$.session_id,type:$.type,title:$.title,tokenCount:$.token_count,discoveryTokens:$.discovery_tokens??0,createdAt:$.created_at,importance:$.importance??3}))}listByProject(J,X={}){let{limit:$=50,offset:Q=0,type:Z,state:H,sessionId:W}=X,Y=`SELECT o.*
180
+ FROM observations o
181
+ JOIN sessions s ON o.session_id = s.id
182
+ WHERE s.project_path = ?`,K=[J];if(W)Y+=" AND o.session_id = ?",K.push(W);if(Z)Y+=" AND o.type = ?",K.push(Z);if(H==="current")Y+=" AND o.superseded_by IS NULL AND o.deleted_at IS NULL";else if(H==="superseded")Y+=" AND o.superseded_by IS NOT NULL AND o.deleted_at IS NULL";else if(H==="tombstoned")Y+=" AND o.deleted_at IS NOT NULL";else Y+=" AND o.superseded_by IS NULL AND o.deleted_at IS NULL";return Y+=" ORDER BY o.created_at DESC LIMIT ? OFFSET ?",K.push($,Q),this.db.all(Y,K).map((V)=>this.mapRow(V))}search(J){let X=!!J.projectPath,$=`
180
183
  SELECT o.*, rank
181
184
  FROM observations o
182
185
  JOIN observations_fts fts ON o._rowid = fts.rowid
183
186
  ${X?"JOIN sessions s ON o.session_id = s.id":""}
184
187
  WHERE observations_fts MATCH ? AND o.superseded_by IS NULL AND o.deleted_at IS NULL
185
- `,Q=[J.query];if(X&&J.projectPath)$+=" AND s.project_path = ?",Q.push(J.projectPath);if(J.sessionId)$+=" AND o.session_id = ?",Q.push(J.sessionId);if(J.type)$+=" AND o.type = ?",Q.push(J.type);if(J.importanceMin!==void 0)$+=" AND o.importance >= ?",Q.push(J.importanceMin);if(J.importanceMax!==void 0)$+=" AND o.importance <= ?",Q.push(J.importanceMax);if(J.createdAfter)$+=" AND o.created_at >= ?",Q.push(J.createdAfter);if(J.createdBefore)$+=" AND o.created_at <= ?",Q.push(J.createdBefore);if(J.concepts&&J.concepts.length>0){let Z=J.concepts.map(()=>"EXISTS (SELECT 1 FROM json_each(o.concepts) WHERE LOWER(value) = LOWER(?))");$+=` AND (${Z.join(" OR ")})`;for(let W of J.concepts)Q.push(W)}if(J.files&&J.files.length>0){let Z=J.files.map(()=>`(EXISTS (SELECT 1 FROM json_each(o.files_read) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\')
186
- OR EXISTS (SELECT 1 FROM json_each(o.files_modified) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\'))`);$+=` AND (${Z.join(" OR ")})`;for(let W of J.files){let H=`%${eJ(W)}%`;Q.push(H,H)}}return $+=" ORDER BY rank LIMIT ? OFFSET ?",Q.push(J.limit??10),Q.push(J.offset??0),this.db.all($,Q).map((Z)=>({observation:this.mapRow(Z),rank:Z.rank,snippet:Z.title}))}searchByConcept(J,X=10,$){let Q=!!$,Z=`SELECT o.*
188
+ `,Q=[J.query];if(X&&J.projectPath)$+=" AND s.project_path = ?",Q.push(J.projectPath);if(J.sessionId)$+=" AND o.session_id = ?",Q.push(J.sessionId);if(J.type)$+=" AND o.type = ?",Q.push(J.type);if(J.importanceMin!==void 0)$+=" AND o.importance >= ?",Q.push(J.importanceMin);if(J.importanceMax!==void 0)$+=" AND o.importance <= ?",Q.push(J.importanceMax);if(J.createdAfter)$+=" AND o.created_at >= ?",Q.push(J.createdAfter);if(J.createdBefore)$+=" AND o.created_at <= ?",Q.push(J.createdBefore);if(J.concepts&&J.concepts.length>0){let Z=J.concepts.map(()=>"EXISTS (SELECT 1 FROM json_each(o.concepts) WHERE LOWER(value) = LOWER(?))");$+=` AND (${Z.join(" OR ")})`;for(let H of J.concepts)Q.push(H)}if(J.files&&J.files.length>0){let Z=J.files.map(()=>`(EXISTS (SELECT 1 FROM json_each(o.files_read) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\')
189
+ OR EXISTS (SELECT 1 FROM json_each(o.files_modified) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\'))`);$+=` AND (${Z.join(" OR ")})`;for(let H of J.files){let W=`%${XX(H)}%`;Q.push(W,W)}}return $+=" ORDER BY rank LIMIT ? OFFSET ?",Q.push(J.limit??10),Q.push(J.offset??0),this.db.all($,Q).map((Z)=>({observation:this.mapRow(Z),rank:Z.rank,snippet:Z.title}))}searchByConcept(J,X=10,$){let Q=!!$,Z=`SELECT o.*
187
190
  FROM observations o
188
191
  JOIN observations_fts fts ON o._rowid = fts.rowid
189
192
  ${Q?"JOIN sessions s ON o.session_id = s.id":""}
@@ -191,7 +194,7 @@ Respond with EXACTLY this XML format:
191
194
  AND o.superseded_by IS NULL AND o.deleted_at IS NULL
192
195
  ${Q?"AND s.project_path = ?":""}
193
196
  ORDER BY rank
194
- LIMIT ?`,W=[`concepts:${J}`];if(Q&&$)W.push($);return W.push(X),this.db.all(Z,W).map((H)=>this.mapRow(H))}searchByFile(J,X=10,$){let Q=!!$,Z=`SELECT o.*
197
+ LIMIT ?`,H=[`concepts:${J}`];if(Q&&$)H.push($);return H.push(X),this.db.all(Z,H).map((W)=>this.mapRow(W))}searchByFile(J,X=10,$){let Q=!!$,Z=`SELECT o.*
195
198
  FROM observations o
196
199
  JOIN observations_fts fts ON o._rowid = fts.rowid
197
200
  ${Q?"JOIN sessions s ON o.session_id = s.id":""}
@@ -199,7 +202,7 @@ Respond with EXACTLY this XML format:
199
202
  AND o.superseded_by IS NULL AND o.deleted_at IS NULL
200
203
  ${Q?"AND s.project_path = ?":""}
201
204
  ORDER BY rank
202
- LIMIT ?`,W=[`files_read:"${J.replace(/"/g,'""')}" OR files_modified:"${J.replace(/"/g,'""')}"`];if(Q&&$)W.push($);return W.push(X),this.db.all(Z,W).map((H)=>this.mapRow(H))}setEmbedding(J,X){this.db.run("UPDATE observations SET embedding = ? WHERE id = ?",[JSON.stringify(X),J])}getWithEmbeddings(J,X){return this.db.all(`SELECT o.id, o.embedding, o.title
205
+ LIMIT ?`,H=[`files_read:"${J.replace(/"/g,'""')}" OR files_modified:"${J.replace(/"/g,'""')}"`];if(Q&&$)H.push($);return H.push(X),this.db.all(Z,H).map((W)=>this.mapRow(W))}setEmbedding(J,X){this.db.run("UPDATE observations SET embedding = ? WHERE id = ?",[JSON.stringify(X),J])}getWithEmbeddings(J,X){return this.db.all(`SELECT o.id, o.embedding, o.title
203
206
  FROM observations o
204
207
  JOIN sessions s ON o.session_id = s.id
205
208
  WHERE s.project_path = ? AND o.embedding IS NOT NULL AND o.superseded_by IS NULL AND o.deleted_at IS NULL
@@ -207,14 +210,14 @@ Respond with EXACTLY this XML format:
207
210
  LIMIT ?`,[J,X]).map(($)=>{try{return{id:$.id,embedding:JSON.parse($.embedding),title:$.title}}catch{return null}}).filter(($)=>$!==null)}findSimilar(J,X,$,Q){let Z=this.db.all(`SELECT id, embedding FROM observations
208
211
  WHERE embedding IS NOT NULL AND type = ? AND superseded_by IS NULL AND deleted_at IS NULL
209
212
  ORDER BY created_at DESC
210
- LIMIT 200`,[X]),W=[];for(let H of Z)try{let Y=JSON.parse(H.embedding);if(!Array.isArray(Y)||Y.length!==J.length)continue;let K=UJ(J,Y);if(K>=$)W.push({id:H.id,similarity:K})}catch{}return W.sort((H,Y)=>Y.similarity-H.similarity).slice(0,Q)}insertVecEmbedding(J,X){let $=new Float32Array(X);this.db.run("BEGIN");try{this.db.run("DELETE FROM observation_embeddings WHERE observation_id = ?",[J]),this.db.run("INSERT INTO observation_embeddings (observation_id, embedding) VALUES (?, ?)",[J,$]),this.db.run("COMMIT")}catch(Q){throw this.db.run("ROLLBACK"),Q}}migrateExistingEmbeddings(J){let X=this.db.all("SELECT id, embedding FROM observations WHERE embedding IS NOT NULL"),$=0,Q=0;for(let Z of X)try{let W=JSON.parse(Z.embedding);if(!Array.isArray(W)||W.length!==J){Q++;continue}this.insertVecEmbedding(Z.id,W),$++}catch{Q++}return{migrated:$,skipped:Q}}getVecEmbeddingMatches(J,X){try{let $=new Float32Array(J);return this.db.all(`SELECT observation_id, distance
213
+ LIMIT 200`,[X]),H=[];for(let W of Z)try{let Y=JSON.parse(W.embedding);if(!Array.isArray(Y)||Y.length!==J.length)continue;let K=_J(J,Y);if(K>=$)H.push({id:W.id,similarity:K})}catch{}return H.sort((W,Y)=>Y.similarity-W.similarity).slice(0,Q)}insertVecEmbedding(J,X){let $=new Float32Array(X);this.db.run("BEGIN");try{this.db.run("DELETE FROM observation_embeddings WHERE observation_id = ?",[J]),this.db.run("INSERT INTO observation_embeddings (observation_id, embedding) VALUES (?, ?)",[J,$]),this.db.run("COMMIT")}catch(Q){throw this.db.run("ROLLBACK"),Q}}migrateExistingEmbeddings(J){let X=this.db.all("SELECT id, embedding FROM observations WHERE embedding IS NOT NULL"),$=0,Q=0;for(let Z of X)try{let H=JSON.parse(Z.embedding);if(!Array.isArray(H)||H.length!==J){Q++;continue}this.insertVecEmbedding(Z.id,H),$++}catch{Q++}return{migrated:$,skipped:Q}}getVecEmbeddingMatches(J,X){try{let $=new Float32Array(J);return this.db.all(`SELECT observation_id, distance
211
214
  FROM observation_embeddings
212
- WHERE embedding MATCH ? AND k = ?`,[$,X]).map((Q)=>({observationId:Q.observation_id,distance:Q.distance}))}catch{return[]}}searchVecSubset(J,X,$){if(X.length===0)return[];try{let Q=new Float32Array(J),Z=Math.max($*5,X.length),W=this.db.all(`SELECT observation_id, distance
215
+ WHERE embedding MATCH ? AND k = ?`,[$,X]).map((Q)=>({observationId:Q.observation_id,distance:Q.distance}))}catch{return[]}}searchVecSubset(J,X,$){if(X.length===0)return[];try{let Q=new Float32Array(J),Z=Math.max($*5,X.length),H=this.db.all(`SELECT observation_id, distance
213
216
  FROM observation_embeddings
214
- WHERE embedding MATCH ? AND k = ?`,[Q,Z]),H=new Set(X);return W.filter((Y)=>H.has(Y.observation_id)).slice(0,$).map((Y)=>({observationId:Y.observation_id,distance:Y.distance}))}catch{return[]}}update(J,X){let $=this.getById(J);if(!$)return null;if(Object.keys(X).length===0)return $;let Q=this.create({sessionId:$.sessionId,scope:$.scope??"project",type:X.type??$.type,title:X.title??$.title,subtitle:X.subtitle??$.subtitle,facts:X.facts??$.facts,narrative:X.narrative??$.narrative,concepts:X.concepts??$.concepts,filesRead:X.filesRead??$.filesRead,filesModified:X.filesModified??$.filesModified,rawToolOutput:$.rawToolOutput,toolName:"mem-update",tokenCount:$.tokenCount,discoveryTokens:$.discoveryTokens,importance:X.importance??$.importance});return this.db.run("UPDATE observations SET revision_of = ? WHERE id = ?",[J,Q.id]),this.supersede(J,Q.id),this.getById(Q.id)}supersede(J,X){let $=new Date().toISOString();this.db.run("UPDATE observations SET superseded_by = ?, superseded_at = ? WHERE id = ?",[X,$,J])}delete(J){if(this.db.all("SELECT id FROM observations WHERE id = ?",[J]).length===0)return!1;let $=new Date().toISOString();return this.db.run("UPDATE observations SET deleted_at = ? WHERE id = ?",[$,J]),this.deleteEmbeddingsForObservations([J]),!0}deleteOlderThan(J){return this.db.all(`DELETE FROM observations
217
+ WHERE embedding MATCH ? AND k = ?`,[Q,Z]),W=new Set(X);return H.filter((Y)=>W.has(Y.observation_id)).slice(0,$).map((Y)=>({observationId:Y.observation_id,distance:Y.distance}))}catch{return[]}}update(J,X){let $=this.getById(J);if(!$)return null;if(Object.keys(X).length===0)return $;let Q=this.create({sessionId:$.sessionId,scope:$.scope??"project",type:X.type??$.type,title:X.title??$.title,subtitle:X.subtitle??$.subtitle,facts:X.facts??$.facts,narrative:X.narrative??$.narrative,concepts:X.concepts??$.concepts,filesRead:X.filesRead??$.filesRead,filesModified:X.filesModified??$.filesModified,rawToolOutput:$.rawToolOutput,toolName:"memory.revise",tokenCount:$.tokenCount,discoveryTokens:$.discoveryTokens,importance:X.importance??$.importance});return this.db.run("UPDATE observations SET revision_of = ? WHERE id = ?",[J,Q.id]),this.supersede(J,Q.id),this.getById(Q.id)}supersede(J,X){let $=new Date().toISOString();this.db.run("UPDATE observations SET superseded_by = ?, superseded_at = ? WHERE id = ?",[X,$,J])}delete(J){if(this.db.all("SELECT id FROM observations WHERE id = ?",[J]).length===0)return!1;let $=new Date().toISOString();return this.db.run("UPDATE observations SET deleted_at = ? WHERE id = ?",[$,J]),this.deleteEmbeddingsForObservations([J]),!0}getLineage(J){let X=this.getByIdIncludingArchived(J);if(!X)return[];let $=new Set([X.id]),Q=[X];while(Q[0].revisionOf){let Z=this.getByIdIncludingArchived(Q[0].revisionOf);if(!Z||$.has(Z.id))break;Q.unshift(Z),$.add(Z.id)}while(Q[Q.length-1].supersededBy){let Z=Q[Q.length-1].supersededBy;if(!Z)break;let H=this.getByIdIncludingArchived(Z);if(!H||$.has(H.id))break;Q.push(H),$.add(H.id)}return Q}deleteOlderThan(J){return this.db.all(`DELETE FROM observations
215
218
  WHERE (created_at < datetime('now', '-' || ? || ' days') OR deleted_at IS NOT NULL)
216
219
  AND session_id NOT IN (SELECT id FROM sessions WHERE status != 'completed')
217
- RETURNING id`,[J]).length}deleteEmbeddingsForObservations(J){if(J.length===0)return;let X=J.map(()=>"?").join(",");try{this.db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${X})`,J)}catch{}this.db.run(`UPDATE observations SET embedding = NULL WHERE id IN (${X})`,J)}mapRow(J){return{id:J.id,sessionId:J.session_id,scope:J.scope??"project",type:J.type,title:J.title,subtitle:J.subtitle,facts:JSON.parse(J.facts),narrative:J.narrative,concepts:JSON.parse(J.concepts),filesRead:JSON.parse(J.files_read),filesModified:JSON.parse(J.files_modified),rawToolOutput:J.raw_tool_output,toolName:J.tool_name,createdAt:J.created_at,tokenCount:J.token_count,discoveryTokens:J.discovery_tokens??0,importance:J.importance??3,revisionOf:J.revision_of??null,deletedAt:J.deleted_at??null,supersededBy:J.superseded_by??null,supersededAt:J.superseded_at??null}}}import{randomUUID as JX}from"crypto";class u{db;constructor(J){this.db=J}create(J){let X=JX(),$=new Date().toISOString();return this.db.run(`INSERT INTO pending_messages
220
+ RETURNING id`,[J]).length}deleteEmbeddingsForObservations(J){if(J.length===0)return;let X=J.map(()=>"?").join(",");try{this.db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${X})`,J)}catch{}this.db.run(`UPDATE observations SET embedding = NULL WHERE id IN (${X})`,J)}mapRow(J){return{id:J.id,sessionId:J.session_id,scope:J.scope??"project",type:J.type,title:J.title,subtitle:J.subtitle,facts:JSON.parse(J.facts),narrative:J.narrative,concepts:JSON.parse(J.concepts),filesRead:JSON.parse(J.files_read),filesModified:JSON.parse(J.files_modified),rawToolOutput:J.raw_tool_output,toolName:J.tool_name,createdAt:J.created_at,tokenCount:J.token_count,discoveryTokens:J.discovery_tokens??0,importance:J.importance??3,revisionOf:J.revision_of??null,deletedAt:J.deleted_at??null,supersededBy:J.superseded_by??null,supersededAt:J.superseded_at??null}}}import{randomUUID as $X}from"crypto";class g{db;constructor(J){this.db=J}create(J){let X=$X(),$=new Date().toISOString();return this.db.run(`INSERT INTO pending_messages
218
221
  (id, session_id, tool_name, tool_output, call_id, created_at)
219
222
  VALUES (?, ?, ?, ?, ?, ?)`,[X,J.sessionId,J.toolName,J.toolOutput,J.callId,$]),{...J,id:X,createdAt:$,status:"pending",retryCount:0,error:null}}getPending(J=10){return this.db.all("SELECT * FROM pending_messages WHERE status = 'pending' ORDER BY created_at ASC LIMIT ?",[J]).map((X)=>this.mapRow(X))}getByStatus(J){return this.db.all("SELECT * FROM pending_messages WHERE status = ? ORDER BY created_at ASC",[J]).map((X)=>this.mapRow(X))}markProcessing(J){this.db.run("UPDATE pending_messages SET status = 'processing' WHERE id = ?",[J])}markCompleted(J){this.db.run("UPDATE pending_messages SET status = 'completed' WHERE id = ?",[J])}markFailed(J,X){this.db.run("UPDATE pending_messages SET status = 'failed', error = ?, retry_count = retry_count + 1 WHERE id = ?",[X,J])}resetStale(J=5){return this.db.all(`UPDATE pending_messages SET status = 'pending'
220
223
  WHERE status = 'processing'
@@ -222,7 +225,7 @@ Respond with EXACTLY this XML format:
222
225
  RETURNING id`,[`-${J}`]).length}deleteCompletedOlderThan(J){return this.db.all(`DELETE FROM pending_messages
223
226
  WHERE status = 'completed'
224
227
  AND created_at < datetime('now', '-' || ? || ' days')
225
- RETURNING id`,[J]).length}mapRow(J){return{id:J.id,sessionId:J.session_id,toolName:J.tool_name,toolOutput:J.tool_output,callId:J.call_id,createdAt:J.created_at,status:J.status,retryCount:J.retry_count,error:J.error??null}}}var XX=[{version:1,name:"create-core-tables",up:`
228
+ RETURNING id`,[J]).length}mapRow(J){return{id:J.id,sessionId:J.session_id,toolName:J.tool_name,toolOutput:J.tool_output,callId:J.call_id,createdAt:J.created_at,status:J.status,retryCount:J.retry_count,error:J.error??null}}}var QX=[{version:1,name:"create-schema",up:`
226
229
  -- Sessions table
227
230
  CREATE TABLE IF NOT EXISTS sessions (
228
231
  _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -261,6 +264,15 @@ Respond with EXACTLY this XML format:
261
264
  tool_name TEXT NOT NULL,
262
265
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
263
266
  token_count INTEGER NOT NULL DEFAULT 0,
267
+ discovery_tokens INTEGER NOT NULL DEFAULT 0,
268
+ embedding TEXT,
269
+ importance INTEGER NOT NULL DEFAULT 3,
270
+ superseded_by TEXT,
271
+ superseded_at TEXT,
272
+ scope TEXT NOT NULL DEFAULT 'project'
273
+ CHECK (scope IN ('project','user')),
274
+ revision_of TEXT,
275
+ deleted_at TEXT,
264
276
  FOREIGN KEY (session_id) REFERENCES sessions(id)
265
277
  );
266
278
 
@@ -270,6 +282,23 @@ Respond with EXACTLY this XML format:
270
282
  ON observations(type);
271
283
  CREATE INDEX IF NOT EXISTS idx_observations_created
272
284
  ON observations(created_at DESC);
285
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded
286
+ ON observations(superseded_by);
287
+ CREATE INDEX IF NOT EXISTS idx_observations_scope
288
+ ON observations(scope);
289
+ CREATE INDEX IF NOT EXISTS idx_observations_revision_of
290
+ ON observations(revision_of);
291
+ CREATE INDEX IF NOT EXISTS idx_observations_deleted_at
292
+ ON observations(deleted_at);
293
+
294
+ -- Clean up superseded_by when the superseding observation is deleted
295
+ CREATE TRIGGER IF NOT EXISTS trg_clear_superseded_by
296
+ AFTER DELETE ON observations
297
+ BEGIN
298
+ UPDATE observations
299
+ SET superseded_by = NULL, superseded_at = NULL
300
+ WHERE superseded_by = OLD.id;
301
+ END;
273
302
 
274
303
  -- Session summaries table
275
304
  CREATE TABLE IF NOT EXISTS session_summaries (
@@ -282,6 +311,11 @@ Respond with EXACTLY this XML format:
282
311
  concepts TEXT NOT NULL DEFAULT '[]',
283
312
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
284
313
  token_count INTEGER NOT NULL DEFAULT 0,
314
+ request TEXT NOT NULL DEFAULT '',
315
+ investigated TEXT NOT NULL DEFAULT '',
316
+ learned TEXT NOT NULL DEFAULT '',
317
+ completed TEXT NOT NULL DEFAULT '',
318
+ next_steps TEXT NOT NULL DEFAULT '',
285
319
  FOREIGN KEY (session_id) REFERENCES sessions(id)
286
320
  );
287
321
 
@@ -305,8 +339,83 @@ Respond with EXACTLY this XML format:
305
339
  ON pending_messages(status);
306
340
  CREATE INDEX IF NOT EXISTS idx_pending_session
307
341
  ON pending_messages(session_id);
308
- `},{version:2,name:"create-fts5-tables",up:`
309
- -- FTS5 for observations (title, subtitle, narrative, facts, concepts, files)
342
+
343
+ -- Embedding metadata
344
+ CREATE TABLE IF NOT EXISTS _embedding_meta (
345
+ key TEXT PRIMARY KEY,
346
+ value TEXT NOT NULL
347
+ );
348
+
349
+ -- Config audit events
350
+ CREATE TABLE IF NOT EXISTS config_audit_events (
351
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
352
+ id TEXT UNIQUE NOT NULL,
353
+ timestamp TEXT NOT NULL,
354
+ patch TEXT NOT NULL,
355
+ previous_values TEXT NOT NULL,
356
+ source TEXT NOT NULL
357
+ CHECK (source IN ('api','mode','rollback','rollback-failed'))
358
+ );
359
+ CREATE INDEX IF NOT EXISTS idx_config_audit_timestamp
360
+ ON config_audit_events(timestamp DESC);
361
+
362
+ -- Maintenance history
363
+ CREATE TABLE IF NOT EXISTS maintenance_history (
364
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
365
+ id TEXT UNIQUE NOT NULL,
366
+ timestamp TEXT NOT NULL,
367
+ action TEXT NOT NULL,
368
+ dry_run INTEGER NOT NULL DEFAULT 0,
369
+ result TEXT NOT NULL
370
+ );
371
+ CREATE INDEX IF NOT EXISTS idx_maintenance_history_timestamp
372
+ ON maintenance_history(timestamp DESC);
373
+
374
+ -- Entities table
375
+ CREATE TABLE IF NOT EXISTS entities (
376
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
377
+ id TEXT UNIQUE NOT NULL,
378
+ name TEXT NOT NULL,
379
+ entity_type TEXT NOT NULL
380
+ CHECK (entity_type IN ('technology','library','pattern','concept','file','person','project','other')),
381
+ first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
382
+ last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
383
+ mention_count INTEGER NOT NULL DEFAULT 1,
384
+ UNIQUE(name, entity_type)
385
+ );
386
+
387
+ CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
388
+ CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
389
+
390
+ -- Entity relations table
391
+ CREATE TABLE IF NOT EXISTS entity_relations (
392
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
393
+ id TEXT UNIQUE NOT NULL,
394
+ source_entity_id TEXT NOT NULL,
395
+ target_entity_id TEXT NOT NULL,
396
+ relationship TEXT NOT NULL
397
+ CHECK (relationship IN ('uses','depends_on','implements','extends','related_to','replaces','configures')),
398
+ observation_id TEXT NOT NULL,
399
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
400
+ UNIQUE(source_entity_id, target_entity_id, relationship),
401
+ FOREIGN KEY (source_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
402
+ FOREIGN KEY (target_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
403
+ FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
404
+ );
405
+
406
+ CREATE INDEX IF NOT EXISTS idx_entity_relations_source ON entity_relations(source_entity_id);
407
+ CREATE INDEX IF NOT EXISTS idx_entity_relations_target ON entity_relations(target_entity_id);
408
+
409
+ -- Entity-Observation junction table
410
+ CREATE TABLE IF NOT EXISTS entity_observations (
411
+ entity_id TEXT NOT NULL,
412
+ observation_id TEXT NOT NULL,
413
+ PRIMARY KEY (entity_id, observation_id),
414
+ FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE,
415
+ FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
416
+ );
417
+
418
+ -- FTS5 for observations
310
419
  CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
311
420
  title,
312
421
  subtitle,
@@ -320,7 +429,6 @@ Respond with EXACTLY this XML format:
320
429
  tokenize='porter unicode61'
321
430
  );
322
431
 
323
- -- Triggers to keep FTS5 in sync with observations table
324
432
  CREATE TRIGGER observations_ai AFTER INSERT ON observations BEGIN
325
433
  INSERT INTO observations_fts(
326
434
  rowid, title, subtitle, narrative, facts, concepts,
@@ -377,88 +485,14 @@ Respond with EXACTLY this XML format:
377
485
  VALUES (new._rowid, new.summary, new.key_decisions, new.concepts);
378
486
  END;
379
487
 
380
- CREATE TRIGGER summaries_ad AFTER DELETE ON session_summaries BEGIN
381
- INSERT INTO summaries_fts(
382
- summaries_fts, rowid, summary, key_decisions, concepts
383
- )
384
- VALUES (
385
- 'delete', old._rowid, old.summary, old.key_decisions, old.concepts
386
- );
387
- END;
388
- `},{version:3,name:"add-structured-summary-columns",up:`
389
- ALTER TABLE session_summaries ADD COLUMN request TEXT NOT NULL DEFAULT '';
390
- ALTER TABLE session_summaries ADD COLUMN investigated TEXT NOT NULL DEFAULT '';
391
- ALTER TABLE session_summaries ADD COLUMN learned TEXT NOT NULL DEFAULT '';
392
- ALTER TABLE session_summaries ADD COLUMN completed TEXT NOT NULL DEFAULT '';
393
- ALTER TABLE session_summaries ADD COLUMN next_steps TEXT NOT NULL DEFAULT '';
394
- `},{version:4,name:"add-discovery-tokens",up:`
395
- ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER NOT NULL DEFAULT 0;
396
- `},{version:5,name:"add-embedding-column",up:`
397
- ALTER TABLE observations ADD COLUMN embedding TEXT;
398
- `},{version:6,name:"create-embedding-meta-table",up:`
399
- CREATE TABLE IF NOT EXISTS _embedding_meta (
400
- key TEXT PRIMARY KEY,
401
- value TEXT NOT NULL
402
- );
403
- `},{version:7,name:"add-importance-column",up:`
404
- ALTER TABLE observations ADD COLUMN importance INTEGER NOT NULL DEFAULT 3;
405
- `},{version:8,name:"add-conflict-resolution-columns",up:`
406
- ALTER TABLE observations ADD COLUMN superseded_by TEXT;
407
- ALTER TABLE observations ADD COLUMN superseded_at TEXT;
408
- CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
409
-
410
- -- Clean up superseded_by when the superseding observation is deleted
411
- CREATE TRIGGER IF NOT EXISTS trg_clear_superseded_by
412
- AFTER DELETE ON observations
413
- BEGIN
414
- UPDATE observations
415
- SET superseded_by = NULL, superseded_at = NULL
416
- WHERE superseded_by = OLD.id;
488
+ CREATE TRIGGER summaries_ad AFTER DELETE ON session_summaries BEGIN
489
+ INSERT INTO summaries_fts(
490
+ summaries_fts, rowid, summary, key_decisions, concepts
491
+ )
492
+ VALUES (
493
+ 'delete', old._rowid, old.summary, old.key_decisions, old.concepts
494
+ );
417
495
  END;
418
- `},{version:9,name:"create-entity-graph-tables",up:`
419
- -- Entities table
420
- CREATE TABLE IF NOT EXISTS entities (
421
- _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
422
- id TEXT UNIQUE NOT NULL,
423
- name TEXT NOT NULL,
424
- entity_type TEXT NOT NULL
425
- CHECK (entity_type IN ('technology','library','pattern','concept','file','person','project','other')),
426
- first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
427
- last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
428
- mention_count INTEGER NOT NULL DEFAULT 1,
429
- UNIQUE(name, entity_type)
430
- );
431
-
432
- CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
433
- CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
434
-
435
- -- Entity relations table
436
- CREATE TABLE IF NOT EXISTS entity_relations (
437
- _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
438
- id TEXT UNIQUE NOT NULL,
439
- source_entity_id TEXT NOT NULL,
440
- target_entity_id TEXT NOT NULL,
441
- relationship TEXT NOT NULL
442
- CHECK (relationship IN ('uses','depends_on','implements','extends','related_to','replaces','configures')),
443
- observation_id TEXT NOT NULL,
444
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
445
- UNIQUE(source_entity_id, target_entity_id, relationship),
446
- FOREIGN KEY (source_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
447
- FOREIGN KEY (target_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
448
- FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
449
- );
450
-
451
- CREATE INDEX IF NOT EXISTS idx_entity_relations_source ON entity_relations(source_entity_id);
452
- CREATE INDEX IF NOT EXISTS idx_entity_relations_target ON entity_relations(target_entity_id);
453
-
454
- -- Entity-Observation junction table
455
- CREATE TABLE IF NOT EXISTS entity_observations (
456
- entity_id TEXT NOT NULL,
457
- observation_id TEXT NOT NULL,
458
- PRIMARY KEY (entity_id, observation_id),
459
- FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE,
460
- FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
461
- );
462
496
 
463
497
  -- FTS5 for entity search
464
498
  CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
@@ -469,7 +503,6 @@ Respond with EXACTLY this XML format:
469
503
  tokenize='porter unicode61'
470
504
  );
471
505
 
472
- -- FTS5 sync triggers
473
506
  CREATE TRIGGER entities_ai AFTER INSERT ON entities BEGIN
474
507
  INSERT INTO entities_fts(rowid, name, entity_type)
475
508
  VALUES (new._rowid, new.name, new.entity_type);
@@ -486,20 +519,11 @@ Respond with EXACTLY this XML format:
486
519
  INSERT INTO entities_fts(rowid, name, entity_type)
487
520
  VALUES (new._rowid, new.name, new.entity_type);
488
521
  END;
489
- `},{version:10,name:"add-v1-revision-tombstone-columns",up:`
490
- ALTER TABLE observations ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'
491
- CHECK (scope IN ('project','user'));
492
- ALTER TABLE observations ADD COLUMN revision_of TEXT;
493
- ALTER TABLE observations ADD COLUMN deleted_at TEXT;
494
-
495
- CREATE INDEX IF NOT EXISTS idx_observations_scope ON observations(scope);
496
- CREATE INDEX IF NOT EXISTS idx_observations_revision_of ON observations(revision_of);
497
- CREATE INDEX IF NOT EXISTS idx_observations_deleted_at ON observations(deleted_at);
498
- `}];function FJ(J,X){if(J.migrate(XX),X?.hasVectorExtension&&X?.embeddingDimension&&X.embeddingDimension>0)$X(J,X.embeddingDimension)}function $X(J,X){if(J.get("SELECT name FROM sqlite_master WHERE type='table' AND name='observation_embeddings'")){let Q=J.get("SELECT value FROM _embedding_meta WHERE key = 'dimension'");if(Q&&Number(Q.value)!==X){console.warn(`[open-mem] vec0 table exists with dimension ${Q.value}, but config specifies ${X}. Drop observation_embeddings to re-create with new dimension.`);return}}else J.exec(`CREATE VIRTUAL TABLE observation_embeddings USING vec0(
522
+ `}];function NJ(J,X){if(J.migrate(QX),X?.hasVectorExtension&&X?.embeddingDimension&&X.embeddingDimension>0)ZX(J,X.embeddingDimension)}function ZX(J,X){if(J.get("SELECT name FROM sqlite_master WHERE type='table' AND name='observation_embeddings'")){let Q=J.get("SELECT value FROM _embedding_meta WHERE key = 'dimension'");if(Q&&Number(Q.value)!==X){console.warn(`[open-mem] vec0 table exists with dimension ${Q.value}, but config specifies ${X}. Drop observation_embeddings to re-create with new dimension.`);return}}else J.exec(`CREATE VIRTUAL TABLE observation_embeddings USING vec0(
499
523
  observation_id TEXT PRIMARY KEY,
500
524
  embedding float[${X}] distance_metric=cosine
501
- )`);J.run("INSERT OR REPLACE INTO _embedding_meta (key, value) VALUES (?, ?)",["dimension",String(X)])}class b{db;constructor(J){this.db=J}create(J,X){let $=new Date().toISOString();return this.db.run(`INSERT INTO sessions (id, project_path, started_at, status)
502
- VALUES (?, ?, ?, 'active')`,[J,X,$]),this.getById(J)}getOrCreate(J,X){let $=this.getById(J);if($)return $;return this.create(J,X)}getById(J){let X=this.db.get("SELECT * FROM sessions WHERE id = ?",[J]);return X?this.mapRow(X):null}getRecent(J,X=10){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC LIMIT ?",[J,X]).map(($)=>this.mapRow($))}getAll(J){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC",[J]).map((X)=>this.mapRow(X))}getActive(){return this.db.all("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at DESC").map((J)=>this.mapRow(J))}updateStatus(J,X){this.db.run("UPDATE sessions SET status = ? WHERE id = ?",[X,J])}markCompleted(J){this.db.run("UPDATE sessions SET status = 'completed', ended_at = datetime('now') WHERE id = ?",[J])}incrementObservationCount(J){this.db.run("UPDATE sessions SET observation_count = observation_count + 1 WHERE id = ?",[J])}setSummary(J,X){this.db.run("UPDATE sessions SET summary_id = ? WHERE id = ?",[X,J])}mapRow(J){return{id:J.id,projectPath:J.project_path,startedAt:J.started_at,endedAt:J.ended_at??null,status:J.status,observationCount:J.observation_count,summaryId:J.summary_id??null}}}import{randomUUID as QX}from"crypto";class g{db;constructor(J){this.db=J}create(J){let X=QX(),$=new Date().toISOString();return this.db.run(`INSERT INTO session_summaries
525
+ )`);J.run("INSERT OR REPLACE INTO _embedding_meta (key, value) VALUES (?, ?)",["dimension",String(X)])}class m{db;constructor(J){this.db=J}create(J,X){let $=new Date().toISOString();return this.db.run(`INSERT INTO sessions (id, project_path, started_at, status)
526
+ VALUES (?, ?, ?, 'active')`,[J,X,$]),this.getById(J)}getOrCreate(J,X){let $=this.getById(J);if($)return $;return this.create(J,X)}getById(J){let X=this.db.get("SELECT * FROM sessions WHERE id = ?",[J]);return X?this.mapRow(X):null}getRecent(J,X=10){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC LIMIT ?",[J,X]).map(($)=>this.mapRow($))}getAll(J){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC",[J]).map((X)=>this.mapRow(X))}getActive(){return this.db.all("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at DESC").map((J)=>this.mapRow(J))}updateStatus(J,X){this.db.run("UPDATE sessions SET status = ? WHERE id = ?",[X,J])}markCompleted(J){this.db.run("UPDATE sessions SET status = 'completed', ended_at = datetime('now') WHERE id = ?",[J])}incrementObservationCount(J){this.db.run("UPDATE sessions SET observation_count = observation_count + 1 WHERE id = ?",[J])}setSummary(J,X){this.db.run("UPDATE sessions SET summary_id = ? WHERE id = ?",[X,J])}mapRow(J){return{id:J.id,projectPath:J.project_path,startedAt:J.started_at,endedAt:J.ended_at??null,status:J.status,observationCount:J.observation_count,summaryId:J.summary_id??null}}}import{randomUUID as WX}from"crypto";class p{db;constructor(J){this.db=J}create(J){let X=WX(),$=new Date().toISOString();return this.db.run(`INSERT INTO session_summaries
503
527
  (id, session_id, summary, key_decisions, files_modified,
504
528
  concepts, created_at, token_count,
505
529
  request, investigated, learned, completed, next_steps)
@@ -512,4 +536,4 @@ Respond with EXACTLY this XML format:
512
536
  JOIN summaries_fts fts ON ss._rowid = fts.rowid
513
537
  WHERE summaries_fts MATCH ?
514
538
  ORDER BY rank
515
- LIMIT ?`,[J,X]).map(($)=>this.mapRow($))}mapRow(J){return{id:J.id,sessionId:J.session_id,summary:J.summary,keyDecisions:JSON.parse(J.key_decisions),filesModified:JSON.parse(J.files_modified),concepts:JSON.parse(J.concepts),createdAt:J.created_at,tokenCount:J.token_count,request:J.request||void 0,investigated:J.investigated||void 0,learned:J.learned||void 0,completed:J.completed||void 0,nextSteps:J.next_steps||void 0}}}class m{config;compressor;summarizer;pendingRepo;observationRepo;sessionRepo;summaryRepo;embeddingModel;conflictEvaluator;entityExtractor;entityRepo;processing=!1;timer=null;mode="in-process";onEnqueue=null;constructor(J,X,$,Q,Z,W,H,Y=null,K=null,z=null,G=null){this.config=J;this.compressor=X;this.summarizer=$;this.pendingRepo=Q;this.observationRepo=Z;this.sessionRepo=W;this.summaryRepo=H;this.embeddingModel=Y;this.conflictEvaluator=K;this.entityExtractor=z;this.entityRepo=G}setMode(J){if(this.mode=J,J==="enqueue-only")this.stop()}getMode(){return this.mode}setOnEnqueue(J){this.onEnqueue=J}enqueue(J,X,$,Q){if(this.pendingRepo.create({sessionId:J,toolName:X,toolOutput:$,callId:Q}),this.mode==="enqueue-only")this.onEnqueue?.()}async processBatch(){if(this.mode==="enqueue-only")return 0;if(this.processing)return 0;this.processing=!0;let J=0;try{this.pendingRepo.resetStale(5);let X=this.pendingRepo.getPending(this.config.batchSize);if(X.length===0)return 0;for(let $ of X)try{this.pendingRepo.markProcessing($.id);let Z=await this.compressor.compress($.toolName,$.toolOutput)??this.compressor.createFallbackObservation($.toolName,$.toolOutput),W=!1,H=null;if(this.embeddingModel)try{let K=v({title:Z.title,narrative:Z.narrative,concepts:Z.concepts}),z=await f(this.embeddingModel,K);if(z){let G=this.config.conflictResolutionEnabled&&this.conflictEvaluator,U=this.config.conflictSimilarityBandLow??0.7,N=this.config.conflictSimilarityBandHigh??0.92;if(G){let L=this.observationRepo.findSimilar(z,Z.type,U,5),T=L.find((k)=>k.similarity>N);if(T)console.log(`[open-mem] Dedup: skipping duplicate of ${T.id} (similarity: ${T.similarity.toFixed(3)})`),W=!0;else{let k=L.filter((R)=>R.similarity>=U&&R.similarity<=N);if(k.length>0)try{let R=k.map((F)=>{let D=this.observationRepo.getById(F.id);return D?{id:D.id,title:D.title,narrative:D.narrative,concepts:D.concepts,type:D.type}:null}).filter((F)=>F!==null);if(R.length>0){let F=await this.conflictEvaluator.evaluate({title:Z.title,narrative:Z.narrative,concepts:Z.concepts,type:Z.type},R);if(F&&F.outcome==="duplicate")console.log(`[open-mem] Conflict eval: duplicate (${F.reason})`),W=!0;else if(F&&F.outcome==="update"&&F.supersedesId)console.log(`[open-mem] Conflict eval: update supersedes ${F.supersedesId} (${F.reason})`),H=F.supersedesId}}catch{}}}else{let L=this.observationRepo.findSimilar(z,Z.type,0.92,1);if(L.length>0)console.log(`[open-mem] Dedup: skipping duplicate of ${L[0].id} (similarity: ${L[0].similarity.toFixed(3)})`),W=!0}}}catch{}if(W){this.pendingRepo.markCompleted($.id);continue}let Y=this.observationRepo.create({sessionId:$.sessionId,type:Z.type,title:Z.title,subtitle:Z.subtitle,facts:Z.facts,narrative:Z.narrative,concepts:Z.concepts,filesRead:Z.filesRead,filesModified:Z.filesModified,rawToolOutput:$.toolOutput,toolName:$.toolName,tokenCount:E(`${Z.title} ${Z.narrative} ${Z.facts.join(" ")}`),discoveryTokens:Z.discoveryTokens??E($.toolOutput),importance:Z.importance??3});if(this.embeddingModel)try{let K=v({title:Y.title,narrative:Y.narrative,concepts:Y.concepts}),z=await f(this.embeddingModel,K);if(z)this.observationRepo.setEmbedding(Y.id,z)}catch{}if(H)try{this.observationRepo.supersede(H,Y.id),console.log(`[open-mem] Superseded observation ${H} with ${Y.id}`)}catch(K){console.error(`[open-mem] Failed to supersede ${H}:`,K)}if(this.config.entityExtractionEnabled&&this.entityExtractor&&this.entityRepo)try{let K=await this.entityExtractor.extract({title:Y.title,narrative:Y.narrative,concepts:Y.concepts,facts:Y.facts,filesRead:Y.filesRead,filesModified:Y.filesModified,type:Y.type});if(K){let z=new Map;for(let G of K.entities){let U=this.entityRepo.upsertEntity(G.name,G.entityType);z.set(G.name,U.id),this.entityRepo.linkObservation(U.id,Y.id)}for(let G of K.relations){let U=z.get(G.sourceName),N=z.get(G.targetName);if(U&&N)this.entityRepo.createRelation(U,N,G.relationship,Y.id)}}}catch{}this.sessionRepo.incrementObservationCount($.sessionId),this.pendingRepo.markCompleted($.id),J++}catch(Q){this.pendingRepo.markFailed($.id,String(Q))}return J}finally{this.processing=!1}}async summarizeSession(J){let X=this.observationRepo.getBySession(J);if(!this.summarizer.shouldSummarize(X.length))return;if(this.summaryRepo.getBySessionId(J))return;let Q=await this.summarizer.summarize(J,X);if(!Q)return;let Z=this.summaryRepo.create({sessionId:J,summary:Q.summary,keyDecisions:Q.keyDecisions,filesModified:Q.filesModified,concepts:Q.concepts,tokenCount:E(Q.summary)});this.sessionRepo.setSummary(J,Z.id)}start(){if(this.mode==="enqueue-only")return;if(this.timer)return;this.timer=setInterval(async()=>{try{await this.processBatch()}catch{}},this.config.batchIntervalMs)}stop(){if(this.timer)clearInterval(this.timer),this.timer=null}get isRunning(){return this.timer!==null}get isProcessing(){return this.processing}getStats(){return{pending:this.pendingRepo.getPending(1000).length,processing:this.processing}}}import{spawnSync as _J}from"child_process";import{dirname as ZX,resolve as NJ}from"path";function WX(J){try{let X=_J("git",["rev-parse","--git-common-dir"],{cwd:J,encoding:"utf-8",timeout:5000});if(X.status!==0||!X.stdout)return null;let $=X.stdout.trim();if($===".git")return null;let Q=_J("git",["rev-parse","--git-dir"],{cwd:J,encoding:"utf-8",timeout:5000});if(Q.status!==0||!Q.stdout)return null;let Z=Q.stdout.trim(),W=NJ(J,$),H=NJ(J,Z);if(W===H)return null;let Y=ZX(W);if(Y===W||Y==="/")return null;return Y}catch{return null}}function LJ(J){return WX(J)??J}var YX=5000,{values:p}=HX({options:{project:{type:"string",short:"p"},"poll-interval":{type:"string"}},strict:!1}),CJ=typeof p.project==="string"?p.project:null;if(!CJ)console.error("Usage: open-mem-daemon --project <path> [--poll-interval <ms>]"),process.exit(1);var MJ=p["poll-interval"],l=typeof MJ==="string"?Number.parseInt(MJ,10):YX;if(Number.isNaN(l)||l<100)console.error("--poll-interval must be a number >= 100"),process.exit(1);var KX=LJ(CJ),B=ZJ(KX);O.enableExtensionSupport();var C=BJ(B.dbPath);FJ(C,{hasVectorExtension:C.hasVectorExtension,embeddingDimension:B.embeddingDimension});var VX=new u(C),zX=new w(C),BX=new b(C),GX=new g(C),UX=new j(B),FX=new x(B),c=B.provider!=="bedrock",_X=B.compressionEnabled&&(!c||B.apiKey)?XJ({provider:B.provider,model:B.model,apiKey:B.apiKey}):null,NX=B.conflictResolutionEnabled&&(!c||B.apiKey)?new q({provider:B.provider,apiKey:B.apiKey,model:B.model,rateLimitingEnabled:B.rateLimitingEnabled}):null,LX=B.entityExtractionEnabled&&(!c||B.apiKey)?new P({provider:B.provider,apiKey:B.apiKey,model:B.model,rateLimitingEnabled:B.rateLimitingEnabled}):null,MX=new y(C),AX=new m(B,UX,FX,VX,zX,BX,GX,_X,NX,LX,MX),SJ=YJ(B.dbPath);WJ(SJ);var d=new I({queueProcessor:AX,pollIntervalMs:l});if(process.send)process.on("message",(J)=>{d.handleMessage(J)});var AJ=!1,n=()=>{if(AJ)return;AJ=!0,d.stop(),HJ(SJ),C.close()};process.on("SIGTERM",()=>{n(),process.exit(0)});process.on("SIGINT",()=>{n(),process.exit(0)});process.on("beforeExit",n);d.start();
539
+ LIMIT ?`,[J,X]).map(($)=>this.mapRow($))}mapRow(J){return{id:J.id,sessionId:J.session_id,summary:J.summary,keyDecisions:JSON.parse(J.key_decisions),filesModified:JSON.parse(J.files_modified),concepts:JSON.parse(J.concepts),createdAt:J.created_at,tokenCount:J.token_count,request:J.request||void 0,investigated:J.investigated||void 0,learned:J.learned||void 0,completed:J.completed||void 0,nextSteps:J.next_steps||void 0}}}class l{config;compressor;summarizer;pendingRepo;observationRepo;sessionRepo;summaryRepo;embeddingModel;conflictEvaluator;entityExtractor;entityRepo;observer;processing=!1;timer=null;mode="in-process";onEnqueue=null;constructor(J,X,$,Q,Z,H,W,Y=null,K=null,V=null,G=null,U=null){this.config=J;this.compressor=X;this.summarizer=$;this.pendingRepo=Q;this.observationRepo=Z;this.sessionRepo=H;this.summaryRepo=W;this.embeddingModel=Y;this.conflictEvaluator=K;this.entityExtractor=V;this.entityRepo=G;this.observer=U}setMode(J){if(this.mode=J,J==="enqueue-only")this.stop()}getMode(){return this.mode}setOnEnqueue(J){this.onEnqueue=J}enqueue(J,X,$,Q){if(this.pendingRepo.create({sessionId:J,toolName:X,toolOutput:$,callId:Q}),this.observer?.onEnqueue?.({sessionId:J,toolName:X,createdAt:new Date().toISOString()}),this.mode==="enqueue-only")this.onEnqueue?.()}async processBatch(){if(this.mode==="enqueue-only")return 0;if(this.processing)return 0;this.processing=!0;let J=0,X=0,$=Date.now();try{this.pendingRepo.resetStale(5);let Q=this.pendingRepo.getPending(this.config.batchSize);if(this.observer?.onBatchStart?.({pending:Q.length,mode:this.mode,startedAt:new Date($).toISOString()}),Q.length===0)return 0;for(let Z of Q)try{this.pendingRepo.markProcessing(Z.id);let W=await this.compressor.compress(Z.toolName,Z.toolOutput)??this.compressor.createFallbackObservation(Z.toolName,Z.toolOutput),Y=!1,K=null;if(this.embeddingModel)try{let G=b({title:W.title,narrative:W.narrative,concepts:W.concepts}),U=await v(this.embeddingModel,G);if(U){let _=this.config.conflictResolutionEnabled&&this.conflictEvaluator,N=this.config.conflictSimilarityBandLow??0.7,j=this.config.conflictSimilarityBandHigh??0.92;if(_){let D=this.observationRepo.findSimilar(U,W.type,N,5),P=D.find((T)=>T.similarity>j);if(P)console.log(`[open-mem] Dedup: skipping duplicate of ${P.id} (similarity: ${P.similarity.toFixed(3)})`),Y=!0;else{let T=D.filter((k)=>k.similarity>=N&&k.similarity<=j);if(T.length>0)try{let k=T.map((F)=>{let R=this.observationRepo.getById(F.id);return R?{id:R.id,title:R.title,narrative:R.narrative,concepts:R.concepts,type:R.type}:null}).filter((F)=>F!==null);if(k.length>0&&this.conflictEvaluator){let F=await this.conflictEvaluator.evaluate({title:W.title,narrative:W.narrative,concepts:W.concepts,type:W.type},k);if(F&&F.outcome==="duplicate")console.log(`[open-mem] Conflict eval: duplicate (${F.reason})`),Y=!0;else if(F&&F.outcome==="update"&&F.supersedesId)console.log(`[open-mem] Conflict eval: update supersedes ${F.supersedesId} (${F.reason})`),K=F.supersedesId}}catch{}}}else{let D=this.observationRepo.findSimilar(U,W.type,0.92,1);if(D.length>0)console.log(`[open-mem] Dedup: skipping duplicate of ${D[0].id} (similarity: ${D[0].similarity.toFixed(3)})`),Y=!0}}}catch{}if(Y){this.pendingRepo.markCompleted(Z.id);continue}let V=this.observationRepo.create({sessionId:Z.sessionId,type:W.type,title:W.title,subtitle:W.subtitle,facts:W.facts,narrative:W.narrative,concepts:W.concepts,filesRead:W.filesRead,filesModified:W.filesModified,rawToolOutput:Z.toolOutput,toolName:Z.toolName,tokenCount:E(`${W.title} ${W.narrative} ${W.facts.join(" ")}`),discoveryTokens:W.discoveryTokens??E(Z.toolOutput),importance:W.importance??3});if(this.embeddingModel)try{let G=b({title:V.title,narrative:V.narrative,concepts:V.concepts}),U=await v(this.embeddingModel,G);if(U)this.observationRepo.setEmbedding(V.id,U)}catch{}if(K)try{this.observationRepo.supersede(K,V.id),console.log(`[open-mem] Superseded observation ${K} with ${V.id}`)}catch(G){console.error(`[open-mem] Failed to supersede ${K}:`,G)}if(this.config.entityExtractionEnabled&&this.entityExtractor&&this.entityRepo)try{let G=await this.entityExtractor.extract({title:V.title,narrative:V.narrative,concepts:V.concepts,facts:V.facts,filesRead:V.filesRead,filesModified:V.filesModified,type:V.type});if(G){let U=new Map;for(let _ of G.entities){let N=this.entityRepo.upsertEntity(_.name,_.entityType);U.set(_.name,N.id),this.entityRepo.linkObservation(N.id,V.id)}for(let _ of G.relations){let N=U.get(_.sourceName),j=U.get(_.targetName);if(N&&j)this.entityRepo.createRelation(N,j,_.relationship,V.id)}}}catch{}this.sessionRepo.incrementObservationCount(Z.sessionId),this.pendingRepo.markCompleted(Z.id),J++}catch(H){this.pendingRepo.markFailed(Z.id,String(H)),X++,this.observer?.onItemFailed?.({pendingId:Z.id,error:String(H),failedAt:new Date().toISOString()})}return J}finally{this.observer?.onBatchEnd?.({processed:J,failed:X,durationMs:Date.now()-$,finishedAt:new Date().toISOString()}),this.processing=!1}}async summarizeSession(J){let X=this.observationRepo.getBySession(J);if(!this.summarizer.shouldSummarize(X.length))return;if(this.summaryRepo.getBySessionId(J))return;let Q=await this.summarizer.summarize(J,X);if(!Q)return;let Z=this.summaryRepo.create({sessionId:J,summary:Q.summary,keyDecisions:Q.keyDecisions,filesModified:Q.filesModified,concepts:Q.concepts,tokenCount:E(Q.summary)});this.sessionRepo.setSummary(J,Z.id)}start(){if(this.mode==="enqueue-only")return;if(this.timer)return;this.timer=setInterval(async()=>{try{await this.processBatch()}catch{}},this.config.batchIntervalMs)}stop(){if(this.timer)clearInterval(this.timer),this.timer=null}get isRunning(){return this.timer!==null}get isProcessing(){return this.processing}getStats(){return{pending:this.pendingRepo.getPending(1000).length,processing:this.processing}}}import{spawnSync as LJ}from"child_process";import{dirname as HX,resolve as MJ}from"path";function YX(J){try{let X=LJ("git",["rev-parse","--git-common-dir"],{cwd:J,encoding:"utf-8",timeout:5000});if(X.status!==0||!X.stdout)return null;let $=X.stdout.trim();if($===".git")return null;let Q=LJ("git",["rev-parse","--git-dir"],{cwd:J,encoding:"utf-8",timeout:5000});if(Q.status!==0||!Q.stdout)return null;let Z=Q.stdout.trim(),H=MJ(J,$),W=MJ(J,Z);if(H===W)return null;let Y=HX(H);if(Y===H||Y==="/")return null;return Y}catch{return null}}function AJ(J){return YX(J)??J}var VX=5000,{values:c}=KX({options:{project:{type:"string",short:"p"},"poll-interval":{type:"string"}},strict:!1}),EJ=typeof c.project==="string"?c.project:null;if(!EJ)console.error("Usage: open-mem-daemon --project <path> [--poll-interval <ms>]"),process.exit(1);var CJ=c["poll-interval"],d=typeof CJ==="string"?Number.parseInt(CJ,10):VX;if(Number.isNaN(d)||d<100)console.error("--poll-interval must be a number >= 100"),process.exit(1);var zX=AJ(EJ),B=HJ(zX);q.enableExtensionSupport();var C=UJ(B.dbPath);NJ(C,{hasVectorExtension:C.hasVectorExtension,embeddingDimension:B.embeddingDimension});var BX=new g(C),GX=new u(C),UX=new m(C),FX=new p(C),_X=new O(B),NX=new h(B),n=B.provider!=="bedrock",LX=B.compressionEnabled&&(!n||B.apiKey)?QJ({provider:B.provider,model:B.model,apiKey:B.apiKey}):null,MX=B.conflictResolutionEnabled&&(!n||B.apiKey)?new x({provider:B.provider,apiKey:B.apiKey,model:B.model,rateLimitingEnabled:B.rateLimitingEnabled}):null,AX=B.entityExtractionEnabled&&(!n||B.apiKey)?new I({provider:B.provider,apiKey:B.apiKey,model:B.model,rateLimitingEnabled:B.rateLimitingEnabled}):null,CX=new w(C),SX=new l(B,_X,NX,BX,GX,UX,FX,LX,MX,AX,CX),DJ=VJ(B.dbPath);YJ(DJ);var s=new y({queueProcessor:SX,pollIntervalMs:d});if(process.send)process.on("message",(J)=>{s.handleMessage(J)});var SJ=!1,i=()=>{if(SJ)return;SJ=!0,s.stop(),KJ(DJ),C.close()};process.on("SIGTERM",()=>{i(),process.exit(0)});process.on("SIGINT",()=>{i(),process.exit(0)});process.on("beforeExit",i);s.start();