open-mem 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +223 -225
  74. package/dist/maintenance.js +130 -106
  75. package/dist/mcp.js +184 -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
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync as r,readFileSync as t}from"fs";var a={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 e(){let $={};if(process.env.OPEN_MEM_DB_PATH)$.dbPath=process.env.OPEN_MEM_DB_PATH;if(process.env.OPEN_MEM_PROVIDER)$.provider=process.env.OPEN_MEM_PROVIDER;if(process.env.OPEN_MEM_MODEL)$.model=process.env.OPEN_MEM_MODEL;if(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS)$.maxContextTokens=Number.parseInt(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS,10);if(process.env.OPEN_MEM_COMPRESSION==="false")$.compressionEnabled=!1;if(process.env.OPEN_MEM_CONTEXT_INJECTION==="false")$.contextInjectionEnabled=!1;if(process.env.OPEN_MEM_IGNORED_TOOLS)$.ignoredTools=process.env.OPEN_MEM_IGNORED_TOOLS.split(",").map((H)=>H.trim());if(process.env.OPEN_MEM_BATCH_SIZE)$.batchSize=Number.parseInt(process.env.OPEN_MEM_BATCH_SIZE,10);if(process.env.OPEN_MEM_RETENTION_DAYS)$.retentionDays=Number.parseInt(process.env.OPEN_MEM_RETENTION_DAYS,10);if(process.env.OPEN_MEM_LOG_LEVEL)$.logLevel=process.env.OPEN_MEM_LOG_LEVEL;if(process.env.OPEN_MEM_CONTEXT_SHOW_TOKEN_COSTS==="false")$.contextShowTokenCosts=!1;if(process.env.OPEN_MEM_CONTEXT_TYPES)$.contextObservationTypes=process.env.OPEN_MEM_CONTEXT_TYPES==="all"?"all":process.env.OPEN_MEM_CONTEXT_TYPES.split(",").map((H)=>H.trim());if(process.env.OPEN_MEM_CONTEXT_FULL_COUNT)$.contextFullObservationCount=Number.parseInt(process.env.OPEN_MEM_CONTEXT_FULL_COUNT,10);if(process.env.OPEN_MEM_MAX_OBSERVATIONS)$.maxObservations=Number.parseInt(process.env.OPEN_MEM_MAX_OBSERVATIONS,10);if(process.env.OPEN_MEM_CONTEXT_SHOW_LAST_SUMMARY==="false")$.contextShowLastSummary=!1;if(process.env.OPEN_MEM_RATE_LIMITING==="false")$.rateLimitingEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT==="false")$.folderContextEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH)$.folderContextMaxDepth=Number.parseInt(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH,10);if(process.env.OPEN_MEM_DAEMON==="true")$.daemonEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD==="true")$.dashboardEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD_PORT)$.dashboardPort=Number.parseInt(process.env.OPEN_MEM_DASHBOARD_PORT,10);if(process.env.OPEN_MEM_EMBEDDING_DIMENSION)$.embeddingDimension=Number.parseInt(process.env.OPEN_MEM_EMBEDDING_DIMENSION,10);if(process.env.OPEN_MEM_CONFLICT_RESOLUTION==="true")$.conflictResolutionEnabled=!0;if(process.env.OPEN_MEM_CONFLICT_BAND_LOW){let H=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_LOW);if(!Number.isNaN(H))$.conflictSimilarityBandLow=H}if(process.env.OPEN_MEM_CONFLICT_BAND_HIGH){let H=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_HIGH);if(!Number.isNaN(H))$.conflictSimilarityBandHigh=H}if(process.env.OPEN_MEM_USER_MEMORY==="true")$.userMemoryEnabled=!0;if(process.env.OPEN_MEM_USER_MEMORY_DB_PATH)$.userMemoryDbPath=process.env.OPEN_MEM_USER_MEMORY_DB_PATH;if(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS)$.userMemoryMaxContextTokens=Number.parseInt(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS,10);if(process.env.OPEN_MEM_RERANKING==="true")$.rerankingEnabled=!0;if(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES)$.rerankingMaxCandidates=Number.parseInt(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES,10);if(process.env.OPEN_MEM_ENTITY_EXTRACTION==="true")$.entityExtractionEnabled=!0;return $}function $$($){let H=`${$}/.open-mem/config.json`;if(!r(H))return{};try{let J=t(H,"utf-8"),Q=JSON.parse(J);if(!Q||typeof Q!=="object"||Array.isArray(Q))return{};return Q}catch{return{}}}function H$($){switch($){case"google":return 768;case"openai":return 1536;case"bedrock":return 1024;case"anthropic":return 0;default:return 768}}function _($,H){let J=$$($),Q=e(),Z={...a,...J,...Q,...H};if(!Z.dbPath.startsWith("/"))Z.dbPath=`${$}/${Z.dbPath}`;if(!process.env.OPEN_MEM_PROVIDER&&!H?.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=H$(Z.provider);return Z}import{Database as x}from"bun:sqlite";import{existsSync as z,mkdirSync as J$,unlinkSync as D}from"fs";import*as f from"sqlite-vec";class N{db;dbPath;_hasVectorExtension=!1;static enableExtensionSupport(){let $=["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib","/usr/local/opt/sqlite/lib/libsqlite3.dylib"];for(let H of $)try{if(z(H))return x.setCustomSQLite(H),!0}catch{return!1}return!1}constructor($){this.dbPath=$,this.db=this.open($),this.configure()}open($){let H=$.lastIndexOf("/");if(H>0){let J=$.substring(0,H);J$(J,{recursive:!0})}return new x($,{create:!0})}configure(){try{this.applyPragmas(),this.loadExtensions()}catch($){console.warn("[open-mem] Database configure failed, attempting recovery by removing WAL/SHM files:",$.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(H){console.warn("[open-mem] WAL/SHM cleanup insufficient, recreating database from scratch:",H.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(J){throw console.warn("[open-mem] All recovery attempts failed, filesystem may be broken:",J.message),$}}}}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{f.load(this.db),this._hasVectorExtension=!0}catch{this._hasVectorExtension=!1}}get hasVectorExtension(){return this._hasVectorExtension}deleteSidecarFiles(){for(let $ of["-wal","-shm"]){let H=this.dbPath+$;try{if(z(H))D(H)}catch{}}}deleteDatabaseFiles(){this.deleteSidecarFiles();try{if(z(this.dbPath))D(this.dbPath)}catch{}}ensureMigrationTable(){this.db.exec(`
3
+ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync as r,readFileSync as t}from"fs";var a={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 e(){let $={};if(process.env.OPEN_MEM_DB_PATH)$.dbPath=process.env.OPEN_MEM_DB_PATH;if(process.env.OPEN_MEM_PROVIDER)$.provider=process.env.OPEN_MEM_PROVIDER;if(process.env.OPEN_MEM_MODEL)$.model=process.env.OPEN_MEM_MODEL;if(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS)$.maxContextTokens=Number.parseInt(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS,10);if(process.env.OPEN_MEM_COMPRESSION==="false")$.compressionEnabled=!1;if(process.env.OPEN_MEM_CONTEXT_INJECTION==="false")$.contextInjectionEnabled=!1;if(process.env.OPEN_MEM_IGNORED_TOOLS)$.ignoredTools=process.env.OPEN_MEM_IGNORED_TOOLS.split(",").map((H)=>H.trim());if(process.env.OPEN_MEM_BATCH_SIZE)$.batchSize=Number.parseInt(process.env.OPEN_MEM_BATCH_SIZE,10);if(process.env.OPEN_MEM_RETENTION_DAYS)$.retentionDays=Number.parseInt(process.env.OPEN_MEM_RETENTION_DAYS,10);if(process.env.OPEN_MEM_LOG_LEVEL)$.logLevel=process.env.OPEN_MEM_LOG_LEVEL;if(process.env.OPEN_MEM_CONTEXT_SHOW_TOKEN_COSTS==="false")$.contextShowTokenCosts=!1;if(process.env.OPEN_MEM_CONTEXT_TYPES)$.contextObservationTypes=process.env.OPEN_MEM_CONTEXT_TYPES==="all"?"all":process.env.OPEN_MEM_CONTEXT_TYPES.split(",").map((H)=>H.trim());if(process.env.OPEN_MEM_CONTEXT_FULL_COUNT)$.contextFullObservationCount=Number.parseInt(process.env.OPEN_MEM_CONTEXT_FULL_COUNT,10);if(process.env.OPEN_MEM_MAX_OBSERVATIONS)$.maxObservations=Number.parseInt(process.env.OPEN_MEM_MAX_OBSERVATIONS,10);if(process.env.OPEN_MEM_CONTEXT_SHOW_LAST_SUMMARY==="false")$.contextShowLastSummary=!1;if(process.env.OPEN_MEM_RATE_LIMITING==="false")$.rateLimitingEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT==="false")$.folderContextEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH)$.folderContextMaxDepth=Number.parseInt(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH,10);if(process.env.OPEN_MEM_DAEMON==="true")$.daemonEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD==="true")$.dashboardEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD_PORT)$.dashboardPort=Number.parseInt(process.env.OPEN_MEM_DASHBOARD_PORT,10);if(process.env.OPEN_MEM_PLATFORM_OPENCODE==="false")$.platformOpenCodeEnabled=!1;if(process.env.OPEN_MEM_PLATFORM_CLAUDE_CODE==="true")$.platformClaudeCodeEnabled=!0;if(process.env.OPEN_MEM_PLATFORM_CURSOR==="true")$.platformCursorEnabled=!0;if(process.env.OPEN_MEM_MCP_COMPAT_MODE)$.mcpCompatibilityMode=process.env.OPEN_MEM_MCP_COMPAT_MODE;if(process.env.OPEN_MEM_MCP_PROTOCOL_VERSION)$.mcpProtocolVersion=process.env.OPEN_MEM_MCP_PROTOCOL_VERSION;if(process.env.OPEN_MEM_MCP_SUPPORTED_PROTOCOLS)$.mcpSupportedProtocolVersions=process.env.OPEN_MEM_MCP_SUPPORTED_PROTOCOLS.split(",").map((H)=>H.trim()).filter(Boolean);if(process.env.OPEN_MEM_EMBEDDING_DIMENSION)$.embeddingDimension=Number.parseInt(process.env.OPEN_MEM_EMBEDDING_DIMENSION,10);if(process.env.OPEN_MEM_CONFLICT_RESOLUTION==="true")$.conflictResolutionEnabled=!0;if(process.env.OPEN_MEM_CONFLICT_BAND_LOW){let H=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_LOW);if(!Number.isNaN(H))$.conflictSimilarityBandLow=H}if(process.env.OPEN_MEM_CONFLICT_BAND_HIGH){let H=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_HIGH);if(!Number.isNaN(H))$.conflictSimilarityBandHigh=H}if(process.env.OPEN_MEM_USER_MEMORY==="true")$.userMemoryEnabled=!0;if(process.env.OPEN_MEM_USER_MEMORY_DB_PATH)$.userMemoryDbPath=process.env.OPEN_MEM_USER_MEMORY_DB_PATH;if(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS)$.userMemoryMaxContextTokens=Number.parseInt(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS,10);if(process.env.OPEN_MEM_RERANKING==="true")$.rerankingEnabled=!0;if(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES)$.rerankingMaxCandidates=Number.parseInt(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES,10);if(process.env.OPEN_MEM_ENTITY_EXTRACTION==="true")$.entityExtractionEnabled=!0;return $}function $$($){let H=`${$}/.open-mem/config.json`;if(!r(H))return{};try{let J=t(H,"utf-8"),Q=JSON.parse(J);if(!Q||typeof Q!=="object"||Array.isArray(Q))return{};return Q}catch{return{}}}function H$($){switch($){case"google":return 768;case"openai":return 1536;case"bedrock":return 1024;case"anthropic":return 0;default:return 768}}function _($,H){let J=$$($),Q=e(),Z={...a,...J,...Q,...H};if(!Z.dbPath.startsWith("/"))Z.dbPath=`${$}/${Z.dbPath}`;if(!process.env.OPEN_MEM_PROVIDER&&!H?.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=H$(Z.provider);return Z}import{Database as I}from"bun:sqlite";import{existsSync as z,mkdirSync as J$,unlinkSync as x}from"fs";import*as f from"sqlite-vec";class N{db;dbPath;_hasVectorExtension=!1;static enableExtensionSupport(){let $=["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib","/usr/local/opt/sqlite/lib/libsqlite3.dylib"];for(let H of $)try{if(z(H))return I.setCustomSQLite(H),!0}catch{return!1}return!1}constructor($){this.dbPath=$,this.db=this.open($),this.configure()}open($){let H=$.lastIndexOf("/");if(H>0){let J=$.substring(0,H);J$(J,{recursive:!0})}return new I($,{create:!0})}configure(){try{this.applyPragmas(),this.loadExtensions()}catch($){console.warn("[open-mem] Database configure failed, attempting recovery by removing WAL/SHM files:",$.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(H){console.warn("[open-mem] WAL/SHM cleanup insufficient, recreating database from scratch:",H.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(J){throw console.warn("[open-mem] All recovery attempts failed, filesystem may be broken:",J.message),$}}}}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{f.load(this.db),this._hasVectorExtension=!0}catch{this._hasVectorExtension=!1}}get hasVectorExtension(){return this._hasVectorExtension}deleteSidecarFiles(){for(let $ of["-wal","-shm"]){let H=this.dbPath+$;try{if(z(H))x(H)}catch{}}}deleteDatabaseFiles(){this.deleteSidecarFiles();try{if(z(this.dbPath))x(this.dbPath)}catch{}}ensureMigrationTable(){this.db.exec(`
4
4
  CREATE TABLE IF NOT EXISTS _migrations (
5
5
  version INTEGER PRIMARY KEY,
6
6
  name TEXT NOT NULL,
7
7
  applied_at TEXT NOT NULL DEFAULT (datetime('now'))
8
8
  )
9
- `)}migrate($){this.ensureMigrationTable();let H=this.db.query("SELECT version FROM _migrations ORDER BY version").all(),J=new Set(H.map((Z)=>Z.version)),Q=$.filter((Z)=>!J.has(Z.version)).sort((Z,K)=>Z.version-K.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($,H){let J=this.db.query($);if(H)J.run(...H);else J.run()}get($,H){let J=this.db.query($);return H?J.get(...H):J.get()}all($,H){let J=this.db.query($);return H?J.all(...H):J.all()}exec($){this.db.exec($)}transaction($){return this.db.transaction($)()}close(){this.db.close()}get isOpen(){try{return this.db.query("SELECT 1").get(),!0}catch{return!1}}get raw(){return this.db}}function j($){return new N($)}import{randomUUID as Q$}from"crypto";import{embed as P$}from"ai";function q($,H){if($.length!==H.length||$.length===0)return 0;let J=0,Q=0,Z=0;for(let M=0;M<$.length;M++)J+=$[M]*H[M],Q+=$[M]*$[M],Z+=H[M]*H[M];let K=Math.sqrt(Q)*Math.sqrt(Z);if(K===0)return 0;return J/K}function Z$($){return $.replace(/[%_\\]/g,"\\$&")}class S{db;constructor($){this.db=$}create($){let H=Q$(),J=new Date().toISOString(),Q=$.discoveryTokens??0,Z=$.importance??3,K=$.scope??"project";return this.db.run(`INSERT INTO observations
9
+ `)}migrate($){this.ensureMigrationTable();let H=this.db.query("SELECT version FROM _migrations ORDER BY version").all(),J=new Set(H.map((Z)=>Z.version)),Q=$.filter((Z)=>!J.has(Z.version)).sort((Z,K)=>Z.version-K.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($,H){let J=this.db.query($);if(H)J.run(...H);else J.run()}get($,H){let J=this.db.query($);return H?J.get(...H):J.get()}all($,H){let J=this.db.query($);return H?J.all(...H):J.all()}exec($){this.db.exec($)}transaction($){return this.db.transaction($)()}close(){this.db.close()}get isOpen(){try{return this.db.query("SELECT 1").get(),!0}catch{return!1}}get raw(){return this.db}}function j($){return new N($)}import{randomUUID as Q$}from"crypto";import{embed as P$}from"ai";function y($,H){if($.length!==H.length||$.length===0)return 0;let J=0,Q=0,Z=0;for(let M=0;M<$.length;M++)J+=$[M]*H[M],Q+=$[M]*$[M],Z+=H[M]*H[M];let K=Math.sqrt(Q)*Math.sqrt(Z);if(K===0)return 0;return J/K}function Z$($){return $.replace(/[%_\\]/g,"\\$&")}class S{db;constructor($){this.db=$}create($){let H=Q$(),J=new Date().toISOString(),Q=$.discoveryTokens??0,Z=$.importance??3,K=$.scope??"project";return this.db.run(`INSERT INTO observations
10
10
  (id, session_id, scope, type, title, subtitle, facts, narrative,
11
11
  concepts, files_read, files_modified, raw_tool_output,
12
12
  tool_name, created_at, token_count, discovery_tokens, importance, revision_of, deleted_at)
@@ -14,12 +14,15 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
14
14
  (id, session_id, scope, type, title, subtitle, facts, narrative,
15
15
  concepts, files_read, files_modified, raw_tool_output,
16
16
  tool_name, created_at, token_count, discovery_tokens, importance, revision_of, deleted_at)
17
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[$.id,$.sessionId,$.scope??"project",$.type,$.title,$.subtitle,JSON.stringify($.facts),$.narrative,JSON.stringify($.concepts),JSON.stringify($.filesRead),JSON.stringify($.filesModified),$.rawToolOutput,$.toolName,$.createdAt,$.tokenCount,$.discoveryTokens??0,$.importance??3,$.revisionOf??null,$.deletedAt??null])}getById($){let H=this.db.get("SELECT * FROM observations WHERE id = ? AND superseded_by IS NULL AND deleted_at IS NULL",[$]);return H?this.mapRow(H):null}getBySession($){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",[$]).map((H)=>this.mapRow(H))}getCount($){if($)return this.db.get("SELECT COUNT(*) as count FROM observations WHERE session_id = ?",[$])?.count??0;return this.db.get("SELECT COUNT(*) as count FROM observations")?.count??0}getIndex($,H=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
17
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[$.id,$.sessionId,$.scope??"project",$.type,$.title,$.subtitle,JSON.stringify($.facts),$.narrative,JSON.stringify($.concepts),JSON.stringify($.filesRead),JSON.stringify($.filesModified),$.rawToolOutput,$.toolName,$.createdAt,$.tokenCount,$.discoveryTokens??0,$.importance??3,$.revisionOf??null,$.deletedAt??null])}getById($){let H=this.db.get("SELECT * FROM observations WHERE id = ? AND superseded_by IS NULL AND deleted_at IS NULL",[$]);return H?this.mapRow(H):null}getByIdIncludingArchived($){let H=this.db.get("SELECT * FROM observations WHERE id = ?",[$]);return H?this.mapRow(H):null}getBySession($){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",[$]).map((H)=>this.mapRow(H))}getCount($){if($)return this.db.get("SELECT COUNT(*) as count FROM observations WHERE session_id = ?",[$])?.count??0;return this.db.get("SELECT COUNT(*) as count FROM observations")?.count??0}getIndex($,H=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
18
18
  FROM observations o
19
19
  JOIN sessions s ON o.session_id = s.id
20
20
  WHERE s.project_path = ? AND o.superseded_by IS NULL AND o.deleted_at IS NULL
21
21
  ORDER BY o.created_at DESC
22
- LIMIT ?`,[$,H]).map((J)=>({id:J.id,sessionId:J.session_id,type:J.type,title:J.title,tokenCount:J.token_count,discoveryTokens:J.discovery_tokens??0,createdAt:J.created_at,importance:J.importance??3}))}search($){let H=!!$.projectPath,J=`
22
+ LIMIT ?`,[$,H]).map((J)=>({id:J.id,sessionId:J.session_id,type:J.type,title:J.title,tokenCount:J.token_count,discoveryTokens:J.discovery_tokens??0,createdAt:J.created_at,importance:J.importance??3}))}listByProject($,H={}){let{limit:J=50,offset:Q=0,type:Z,state:K,sessionId:M}=H,W=`SELECT o.*
23
+ FROM observations o
24
+ JOIN sessions s ON o.session_id = s.id
25
+ WHERE s.project_path = ?`,X=[$];if(M)W+=" AND o.session_id = ?",X.push(M);if(Z)W+=" AND o.type = ?",X.push(Z);if(K==="current")W+=" AND o.superseded_by IS NULL AND o.deleted_at IS NULL";else if(K==="superseded")W+=" AND o.superseded_by IS NOT NULL AND o.deleted_at IS NULL";else if(K==="tombstoned")W+=" AND o.deleted_at IS NOT NULL";else W+=" AND o.superseded_by IS NULL AND o.deleted_at IS NULL";return W+=" ORDER BY o.created_at DESC LIMIT ? OFFSET ?",X.push(J,Q),this.db.all(W,X).map((Y)=>this.mapRow(Y))}search($){let H=!!$.projectPath,J=`
23
26
  SELECT o.*, rank
24
27
  FROM observations o
25
28
  JOIN observations_fts fts ON o._rowid = fts.rowid
@@ -50,14 +53,14 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
50
53
  LIMIT ?`,[$,H]).map((J)=>{try{return{id:J.id,embedding:JSON.parse(J.embedding),title:J.title}}catch{return null}}).filter((J)=>J!==null)}findSimilar($,H,J,Q){let Z=this.db.all(`SELECT id, embedding FROM observations
51
54
  WHERE embedding IS NOT NULL AND type = ? AND superseded_by IS NULL AND deleted_at IS NULL
52
55
  ORDER BY created_at DESC
53
- LIMIT 200`,[H]),K=[];for(let M of Z)try{let X=JSON.parse(M.embedding);if(!Array.isArray(X)||X.length!==$.length)continue;let W=q($,X);if(W>=J)K.push({id:M.id,similarity:W})}catch{}return K.sort((M,X)=>X.similarity-M.similarity).slice(0,Q)}insertVecEmbedding($,H){let J=new Float32Array(H);this.db.run("BEGIN");try{this.db.run("DELETE FROM observation_embeddings WHERE observation_id = ?",[$]),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($){let H=this.db.all("SELECT id, embedding FROM observations WHERE embedding IS NOT NULL"),J=0,Q=0;for(let Z of H)try{let K=JSON.parse(Z.embedding);if(!Array.isArray(K)||K.length!==$){Q++;continue}this.insertVecEmbedding(Z.id,K),J++}catch{Q++}return{migrated:J,skipped:Q}}getVecEmbeddingMatches($,H){try{let J=new Float32Array($);return this.db.all(`SELECT observation_id, distance
56
+ LIMIT 200`,[H]),K=[];for(let M of Z)try{let W=JSON.parse(M.embedding);if(!Array.isArray(W)||W.length!==$.length)continue;let X=y($,W);if(X>=J)K.push({id:M.id,similarity:X})}catch{}return K.sort((M,W)=>W.similarity-M.similarity).slice(0,Q)}insertVecEmbedding($,H){let J=new Float32Array(H);this.db.run("BEGIN");try{this.db.run("DELETE FROM observation_embeddings WHERE observation_id = ?",[$]),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($){let H=this.db.all("SELECT id, embedding FROM observations WHERE embedding IS NOT NULL"),J=0,Q=0;for(let Z of H)try{let K=JSON.parse(Z.embedding);if(!Array.isArray(K)||K.length!==$){Q++;continue}this.insertVecEmbedding(Z.id,K),J++}catch{Q++}return{migrated:J,skipped:Q}}getVecEmbeddingMatches($,H){try{let J=new Float32Array($);return this.db.all(`SELECT observation_id, distance
54
57
  FROM observation_embeddings
55
58
  WHERE embedding MATCH ? AND k = ?`,[J,H]).map((Q)=>({observationId:Q.observation_id,distance:Q.distance}))}catch{return[]}}searchVecSubset($,H,J){if(H.length===0)return[];try{let Q=new Float32Array($),Z=Math.max(J*5,H.length),K=this.db.all(`SELECT observation_id, distance
56
59
  FROM observation_embeddings
57
- WHERE embedding MATCH ? AND k = ?`,[Q,Z]),M=new Set(H);return K.filter((X)=>M.has(X.observation_id)).slice(0,J).map((X)=>({observationId:X.observation_id,distance:X.distance}))}catch{return[]}}update($,H){let J=this.getById($);if(!J)return null;if(Object.keys(H).length===0)return J;let Q=this.create({sessionId:J.sessionId,scope:J.scope??"project",type:H.type??J.type,title:H.title??J.title,subtitle:H.subtitle??J.subtitle,facts:H.facts??J.facts,narrative:H.narrative??J.narrative,concepts:H.concepts??J.concepts,filesRead:H.filesRead??J.filesRead,filesModified:H.filesModified??J.filesModified,rawToolOutput:J.rawToolOutput,toolName:"mem-update",tokenCount:J.tokenCount,discoveryTokens:J.discoveryTokens,importance:H.importance??J.importance});return this.db.run("UPDATE observations SET revision_of = ? WHERE id = ?",[$,Q.id]),this.supersede($,Q.id),this.getById(Q.id)}supersede($,H){let J=new Date().toISOString();this.db.run("UPDATE observations SET superseded_by = ?, superseded_at = ? WHERE id = ?",[H,J,$])}delete($){if(this.db.all("SELECT id FROM observations WHERE id = ?",[$]).length===0)return!1;let J=new Date().toISOString();return this.db.run("UPDATE observations SET deleted_at = ? WHERE id = ?",[J,$]),this.deleteEmbeddingsForObservations([$]),!0}deleteOlderThan($){return this.db.all(`DELETE FROM observations
60
+ WHERE embedding MATCH ? AND k = ?`,[Q,Z]),M=new Set(H);return K.filter((W)=>M.has(W.observation_id)).slice(0,J).map((W)=>({observationId:W.observation_id,distance:W.distance}))}catch{return[]}}update($,H){let J=this.getById($);if(!J)return null;if(Object.keys(H).length===0)return J;let Q=this.create({sessionId:J.sessionId,scope:J.scope??"project",type:H.type??J.type,title:H.title??J.title,subtitle:H.subtitle??J.subtitle,facts:H.facts??J.facts,narrative:H.narrative??J.narrative,concepts:H.concepts??J.concepts,filesRead:H.filesRead??J.filesRead,filesModified:H.filesModified??J.filesModified,rawToolOutput:J.rawToolOutput,toolName:"memory.revise",tokenCount:J.tokenCount,discoveryTokens:J.discoveryTokens,importance:H.importance??J.importance});return this.db.run("UPDATE observations SET revision_of = ? WHERE id = ?",[$,Q.id]),this.supersede($,Q.id),this.getById(Q.id)}supersede($,H){let J=new Date().toISOString();this.db.run("UPDATE observations SET superseded_by = ?, superseded_at = ? WHERE id = ?",[H,J,$])}delete($){if(this.db.all("SELECT id FROM observations WHERE id = ?",[$]).length===0)return!1;let J=new Date().toISOString();return this.db.run("UPDATE observations SET deleted_at = ? WHERE id = ?",[J,$]),this.deleteEmbeddingsForObservations([$]),!0}getLineage($){let H=this.getByIdIncludingArchived($);if(!H)return[];let J=new Set([H.id]),Q=[H];while(Q[0].revisionOf){let Z=this.getByIdIncludingArchived(Q[0].revisionOf);if(!Z||J.has(Z.id))break;Q.unshift(Z),J.add(Z.id)}while(Q[Q.length-1].supersededBy){let Z=Q[Q.length-1].supersededBy;if(!Z)break;let K=this.getByIdIncludingArchived(Z);if(!K||J.has(K.id))break;Q.push(K),J.add(K.id)}return Q}deleteOlderThan($){return this.db.all(`DELETE FROM observations
58
61
  WHERE (created_at < datetime('now', '-' || ? || ' days') OR deleted_at IS NOT NULL)
59
62
  AND session_id NOT IN (SELECT id FROM sessions WHERE status != 'completed')
60
- RETURNING id`,[$]).length}deleteEmbeddingsForObservations($){if($.length===0)return;let H=$.map(()=>"?").join(",");try{this.db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${H})`,$)}catch{}this.db.run(`UPDATE observations SET embedding = NULL WHERE id IN (${H})`,$)}mapRow($){return{id:$.id,sessionId:$.session_id,scope:$.scope??"project",type:$.type,title:$.title,subtitle:$.subtitle,facts:JSON.parse($.facts),narrative:$.narrative,concepts:JSON.parse($.concepts),filesRead:JSON.parse($.files_read),filesModified:JSON.parse($.files_modified),rawToolOutput:$.raw_tool_output,toolName:$.tool_name,createdAt:$.created_at,tokenCount:$.token_count,discoveryTokens:$.discovery_tokens??0,importance:$.importance??3,revisionOf:$.revision_of??null,deletedAt:$.deleted_at??null,supersededBy:$.superseded_by??null,supersededAt:$.superseded_at??null}}}var K$=[{version:1,name:"create-core-tables",up:`
63
+ RETURNING id`,[$]).length}deleteEmbeddingsForObservations($){if($.length===0)return;let H=$.map(()=>"?").join(",");try{this.db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${H})`,$)}catch{}this.db.run(`UPDATE observations SET embedding = NULL WHERE id IN (${H})`,$)}mapRow($){return{id:$.id,sessionId:$.session_id,scope:$.scope??"project",type:$.type,title:$.title,subtitle:$.subtitle,facts:JSON.parse($.facts),narrative:$.narrative,concepts:JSON.parse($.concepts),filesRead:JSON.parse($.files_read),filesModified:JSON.parse($.files_modified),rawToolOutput:$.raw_tool_output,toolName:$.tool_name,createdAt:$.created_at,tokenCount:$.token_count,discoveryTokens:$.discovery_tokens??0,importance:$.importance??3,revisionOf:$.revision_of??null,deletedAt:$.deleted_at??null,supersededBy:$.superseded_by??null,supersededAt:$.superseded_at??null}}}var K$=[{version:1,name:"create-schema",up:`
61
64
  -- Sessions table
62
65
  CREATE TABLE IF NOT EXISTS sessions (
63
66
  _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -96,6 +99,15 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
96
99
  tool_name TEXT NOT NULL,
97
100
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
98
101
  token_count INTEGER NOT NULL DEFAULT 0,
102
+ discovery_tokens INTEGER NOT NULL DEFAULT 0,
103
+ embedding TEXT,
104
+ importance INTEGER NOT NULL DEFAULT 3,
105
+ superseded_by TEXT,
106
+ superseded_at TEXT,
107
+ scope TEXT NOT NULL DEFAULT 'project'
108
+ CHECK (scope IN ('project','user')),
109
+ revision_of TEXT,
110
+ deleted_at TEXT,
99
111
  FOREIGN KEY (session_id) REFERENCES sessions(id)
100
112
  );
101
113
 
@@ -105,6 +117,23 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
105
117
  ON observations(type);
106
118
  CREATE INDEX IF NOT EXISTS idx_observations_created
107
119
  ON observations(created_at DESC);
120
+ CREATE INDEX IF NOT EXISTS idx_observations_superseded
121
+ ON observations(superseded_by);
122
+ CREATE INDEX IF NOT EXISTS idx_observations_scope
123
+ ON observations(scope);
124
+ CREATE INDEX IF NOT EXISTS idx_observations_revision_of
125
+ ON observations(revision_of);
126
+ CREATE INDEX IF NOT EXISTS idx_observations_deleted_at
127
+ ON observations(deleted_at);
128
+
129
+ -- Clean up superseded_by when the superseding observation is deleted
130
+ CREATE TRIGGER IF NOT EXISTS trg_clear_superseded_by
131
+ AFTER DELETE ON observations
132
+ BEGIN
133
+ UPDATE observations
134
+ SET superseded_by = NULL, superseded_at = NULL
135
+ WHERE superseded_by = OLD.id;
136
+ END;
108
137
 
109
138
  -- Session summaries table
110
139
  CREATE TABLE IF NOT EXISTS session_summaries (
@@ -117,6 +146,11 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
117
146
  concepts TEXT NOT NULL DEFAULT '[]',
118
147
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
119
148
  token_count INTEGER NOT NULL DEFAULT 0,
149
+ request TEXT NOT NULL DEFAULT '',
150
+ investigated TEXT NOT NULL DEFAULT '',
151
+ learned TEXT NOT NULL DEFAULT '',
152
+ completed TEXT NOT NULL DEFAULT '',
153
+ next_steps TEXT NOT NULL DEFAULT '',
120
154
  FOREIGN KEY (session_id) REFERENCES sessions(id)
121
155
  );
122
156
 
@@ -140,8 +174,83 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
140
174
  ON pending_messages(status);
141
175
  CREATE INDEX IF NOT EXISTS idx_pending_session
142
176
  ON pending_messages(session_id);
143
- `},{version:2,name:"create-fts5-tables",up:`
144
- -- FTS5 for observations (title, subtitle, narrative, facts, concepts, files)
177
+
178
+ -- Embedding metadata
179
+ CREATE TABLE IF NOT EXISTS _embedding_meta (
180
+ key TEXT PRIMARY KEY,
181
+ value TEXT NOT NULL
182
+ );
183
+
184
+ -- Config audit events
185
+ CREATE TABLE IF NOT EXISTS config_audit_events (
186
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
187
+ id TEXT UNIQUE NOT NULL,
188
+ timestamp TEXT NOT NULL,
189
+ patch TEXT NOT NULL,
190
+ previous_values TEXT NOT NULL,
191
+ source TEXT NOT NULL
192
+ CHECK (source IN ('api','mode','rollback','rollback-failed'))
193
+ );
194
+ CREATE INDEX IF NOT EXISTS idx_config_audit_timestamp
195
+ ON config_audit_events(timestamp DESC);
196
+
197
+ -- Maintenance history
198
+ CREATE TABLE IF NOT EXISTS maintenance_history (
199
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
200
+ id TEXT UNIQUE NOT NULL,
201
+ timestamp TEXT NOT NULL,
202
+ action TEXT NOT NULL,
203
+ dry_run INTEGER NOT NULL DEFAULT 0,
204
+ result TEXT NOT NULL
205
+ );
206
+ CREATE INDEX IF NOT EXISTS idx_maintenance_history_timestamp
207
+ ON maintenance_history(timestamp DESC);
208
+
209
+ -- Entities table
210
+ CREATE TABLE IF NOT EXISTS entities (
211
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
212
+ id TEXT UNIQUE NOT NULL,
213
+ name TEXT NOT NULL,
214
+ entity_type TEXT NOT NULL
215
+ CHECK (entity_type IN ('technology','library','pattern','concept','file','person','project','other')),
216
+ first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
217
+ last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
218
+ mention_count INTEGER NOT NULL DEFAULT 1,
219
+ UNIQUE(name, entity_type)
220
+ );
221
+
222
+ CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
223
+ CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
224
+
225
+ -- Entity relations table
226
+ CREATE TABLE IF NOT EXISTS entity_relations (
227
+ _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
228
+ id TEXT UNIQUE NOT NULL,
229
+ source_entity_id TEXT NOT NULL,
230
+ target_entity_id TEXT NOT NULL,
231
+ relationship TEXT NOT NULL
232
+ CHECK (relationship IN ('uses','depends_on','implements','extends','related_to','replaces','configures')),
233
+ observation_id TEXT NOT NULL,
234
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
235
+ UNIQUE(source_entity_id, target_entity_id, relationship),
236
+ FOREIGN KEY (source_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
237
+ FOREIGN KEY (target_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
238
+ FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
239
+ );
240
+
241
+ CREATE INDEX IF NOT EXISTS idx_entity_relations_source ON entity_relations(source_entity_id);
242
+ CREATE INDEX IF NOT EXISTS idx_entity_relations_target ON entity_relations(target_entity_id);
243
+
244
+ -- Entity-Observation junction table
245
+ CREATE TABLE IF NOT EXISTS entity_observations (
246
+ entity_id TEXT NOT NULL,
247
+ observation_id TEXT NOT NULL,
248
+ PRIMARY KEY (entity_id, observation_id),
249
+ FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE,
250
+ FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
251
+ );
252
+
253
+ -- FTS5 for observations
145
254
  CREATE VIRTUAL TABLE IF NOT EXISTS observations_fts USING fts5(
146
255
  title,
147
256
  subtitle,
@@ -155,7 +264,6 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
155
264
  tokenize='porter unicode61'
156
265
  );
157
266
 
158
- -- Triggers to keep FTS5 in sync with observations table
159
267
  CREATE TRIGGER observations_ai AFTER INSERT ON observations BEGIN
160
268
  INSERT INTO observations_fts(
161
269
  rowid, title, subtitle, narrative, facts, concepts,
@@ -212,88 +320,14 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
212
320
  VALUES (new._rowid, new.summary, new.key_decisions, new.concepts);
213
321
  END;
214
322
 
215
- CREATE TRIGGER summaries_ad AFTER DELETE ON session_summaries BEGIN
216
- INSERT INTO summaries_fts(
217
- summaries_fts, rowid, summary, key_decisions, concepts
218
- )
219
- VALUES (
220
- 'delete', old._rowid, old.summary, old.key_decisions, old.concepts
221
- );
222
- END;
223
- `},{version:3,name:"add-structured-summary-columns",up:`
224
- ALTER TABLE session_summaries ADD COLUMN request TEXT NOT NULL DEFAULT '';
225
- ALTER TABLE session_summaries ADD COLUMN investigated TEXT NOT NULL DEFAULT '';
226
- ALTER TABLE session_summaries ADD COLUMN learned TEXT NOT NULL DEFAULT '';
227
- ALTER TABLE session_summaries ADD COLUMN completed TEXT NOT NULL DEFAULT '';
228
- ALTER TABLE session_summaries ADD COLUMN next_steps TEXT NOT NULL DEFAULT '';
229
- `},{version:4,name:"add-discovery-tokens",up:`
230
- ALTER TABLE observations ADD COLUMN discovery_tokens INTEGER NOT NULL DEFAULT 0;
231
- `},{version:5,name:"add-embedding-column",up:`
232
- ALTER TABLE observations ADD COLUMN embedding TEXT;
233
- `},{version:6,name:"create-embedding-meta-table",up:`
234
- CREATE TABLE IF NOT EXISTS _embedding_meta (
235
- key TEXT PRIMARY KEY,
236
- value TEXT NOT NULL
237
- );
238
- `},{version:7,name:"add-importance-column",up:`
239
- ALTER TABLE observations ADD COLUMN importance INTEGER NOT NULL DEFAULT 3;
240
- `},{version:8,name:"add-conflict-resolution-columns",up:`
241
- ALTER TABLE observations ADD COLUMN superseded_by TEXT;
242
- ALTER TABLE observations ADD COLUMN superseded_at TEXT;
243
- CREATE INDEX IF NOT EXISTS idx_observations_superseded ON observations(superseded_by);
244
-
245
- -- Clean up superseded_by when the superseding observation is deleted
246
- CREATE TRIGGER IF NOT EXISTS trg_clear_superseded_by
247
- AFTER DELETE ON observations
248
- BEGIN
249
- UPDATE observations
250
- SET superseded_by = NULL, superseded_at = NULL
251
- WHERE superseded_by = OLD.id;
323
+ CREATE TRIGGER summaries_ad AFTER DELETE ON session_summaries BEGIN
324
+ INSERT INTO summaries_fts(
325
+ summaries_fts, rowid, summary, key_decisions, concepts
326
+ )
327
+ VALUES (
328
+ 'delete', old._rowid, old.summary, old.key_decisions, old.concepts
329
+ );
252
330
  END;
253
- `},{version:9,name:"create-entity-graph-tables",up:`
254
- -- Entities table
255
- CREATE TABLE IF NOT EXISTS entities (
256
- _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
257
- id TEXT UNIQUE NOT NULL,
258
- name TEXT NOT NULL,
259
- entity_type TEXT NOT NULL
260
- CHECK (entity_type IN ('technology','library','pattern','concept','file','person','project','other')),
261
- first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
262
- last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
263
- mention_count INTEGER NOT NULL DEFAULT 1,
264
- UNIQUE(name, entity_type)
265
- );
266
-
267
- CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
268
- CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(entity_type);
269
-
270
- -- Entity relations table
271
- CREATE TABLE IF NOT EXISTS entity_relations (
272
- _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
273
- id TEXT UNIQUE NOT NULL,
274
- source_entity_id TEXT NOT NULL,
275
- target_entity_id TEXT NOT NULL,
276
- relationship TEXT NOT NULL
277
- CHECK (relationship IN ('uses','depends_on','implements','extends','related_to','replaces','configures')),
278
- observation_id TEXT NOT NULL,
279
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
280
- UNIQUE(source_entity_id, target_entity_id, relationship),
281
- FOREIGN KEY (source_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
282
- FOREIGN KEY (target_entity_id) REFERENCES entities(id) ON DELETE CASCADE,
283
- FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
284
- );
285
-
286
- CREATE INDEX IF NOT EXISTS idx_entity_relations_source ON entity_relations(source_entity_id);
287
- CREATE INDEX IF NOT EXISTS idx_entity_relations_target ON entity_relations(target_entity_id);
288
-
289
- -- Entity-Observation junction table
290
- CREATE TABLE IF NOT EXISTS entity_observations (
291
- entity_id TEXT NOT NULL,
292
- observation_id TEXT NOT NULL,
293
- PRIMARY KEY (entity_id, observation_id),
294
- FOREIGN KEY (entity_id) REFERENCES entities(id) ON DELETE CASCADE,
295
- FOREIGN KEY (observation_id) REFERENCES observations(id) ON DELETE CASCADE
296
- );
297
331
 
298
332
  -- FTS5 for entity search
299
333
  CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(
@@ -304,7 +338,6 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
304
338
  tokenize='porter unicode61'
305
339
  );
306
340
 
307
- -- FTS5 sync triggers
308
341
  CREATE TRIGGER entities_ai AFTER INSERT ON entities BEGIN
309
342
  INSERT INTO entities_fts(rowid, name, entity_type)
310
343
  VALUES (new._rowid, new.name, new.entity_type);
@@ -321,21 +354,12 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
321
354
  INSERT INTO entities_fts(rowid, name, entity_type)
322
355
  VALUES (new._rowid, new.name, new.entity_type);
323
356
  END;
324
- `},{version:10,name:"add-v1-revision-tombstone-columns",up:`
325
- ALTER TABLE observations ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'
326
- CHECK (scope IN ('project','user'));
327
- ALTER TABLE observations ADD COLUMN revision_of TEXT;
328
- ALTER TABLE observations ADD COLUMN deleted_at TEXT;
329
-
330
- CREATE INDEX IF NOT EXISTS idx_observations_scope ON observations(scope);
331
- CREATE INDEX IF NOT EXISTS idx_observations_revision_of ON observations(revision_of);
332
- CREATE INDEX IF NOT EXISTS idx_observations_deleted_at ON observations(deleted_at);
333
357
  `}];function u($,H){if($.migrate(K$),H?.hasVectorExtension&&H?.embeddingDimension&&H.embeddingDimension>0)M$($,H.embeddingDimension)}function M$($,H){if($.get("SELECT name FROM sqlite_master WHERE type='table' AND name='observation_embeddings'")){let Q=$.get("SELECT value FROM _embedding_meta WHERE key = 'dimension'");if(Q&&Number(Q.value)!==H){console.warn(`[open-mem] vec0 table exists with dimension ${Q.value}, but config specifies ${H}. Drop observation_embeddings to re-create with new dimension.`);return}}else $.exec(`CREATE VIRTUAL TABLE observation_embeddings USING vec0(
334
358
  observation_id TEXT PRIMARY KEY,
335
359
  embedding float[${H}] distance_metric=cosine
336
360
  )`);$.run("INSERT OR REPLACE INTO _embedding_meta (key, value) VALUES (?, ?)",["dimension",String(H)])}class T{db;constructor($){this.db=$}create($,H){let J=new Date().toISOString();return this.db.run(`INSERT INTO sessions (id, project_path, started_at, status)
337
- VALUES (?, ?, ?, 'active')`,[$,H,J]),this.getById($)}getOrCreate($,H){let J=this.getById($);if(J)return J;return this.create($,H)}getById($){let H=this.db.get("SELECT * FROM sessions WHERE id = ?",[$]);return H?this.mapRow(H):null}getRecent($,H=10){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC LIMIT ?",[$,H]).map((J)=>this.mapRow(J))}getAll($){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC",[$]).map((H)=>this.mapRow(H))}getActive(){return this.db.all("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at DESC").map(($)=>this.mapRow($))}updateStatus($,H){this.db.run("UPDATE sessions SET status = ? WHERE id = ?",[H,$])}markCompleted($){this.db.run("UPDATE sessions SET status = 'completed', ended_at = datetime('now') WHERE id = ?",[$])}incrementObservationCount($){this.db.run("UPDATE sessions SET observation_count = observation_count + 1 WHERE id = ?",[$])}setSummary($,H){this.db.run("UPDATE sessions SET summary_id = ? WHERE id = ?",[H,$])}mapRow($){return{id:$.id,projectPath:$.project_path,startedAt:$.started_at,endedAt:$.ended_at??null,status:$.status,observationCount:$.observation_count,summaryId:$.summary_id??null}}}import{existsSync as T$}from"fs";import{readdir as A$,readFile as E$,writeFile as k$}from"fs/promises";import{join as F$,resolve as R$}from"path";import{existsSync as w}from"fs";import{mkdir as W$,readFile as X$,rename as Y$,unlink as V$,writeFile as B$}from"fs/promises";import{dirname as k,isAbsolute as g,join as U,normalize as L$,relative as v,resolve as E,sep as A}from"path";var O="<!-- open-mem-context -->",V="<!-- /open-mem-context -->",N$={bugfix:"\uD83D\uDD34",feature:"\uD83D\uDFE3",refactor:"\uD83D\uDD04",change:"\u2705",discovery:"\uD83D\uDD35",decision:"\u2696\uFE0F"},y=new Map,O$=new Set(["node_modules",".git","dist","coverage",".open-mem","build","__pycache__",".next",".nuxt"]);async function b($,H,J=5){if(H.length===0)return;let Q=[];for(let M of H){for(let X of M.filesModified)Q.push(X);for(let X of M.filesRead)Q.push(X)}let Z=z$(Q,$,J);if(Z.size===0)return;let K=S$(H,Z,$);for(let[M,X]of K)try{let W=U$(M,X,$);await G$(M,W)}catch(W){console.error(`[open-mem] Failed to update AGENTS.md in ${M}:`,W)}}function U$($,H,J){let Q=[...H].sort((W,Y)=>Y.createdAt.localeCompare(W.createdAt)).slice(0,10),Z=v(J,$)||".",K=[];K.push(`## Recent Activity in \`${Z}/\` (auto-generated by open-mem)`),K.push(""),K.push("| Type | Title | Date |"),K.push("|------|-------|------|");for(let W of Q){let Y=N$[W.type]||"\uD83D\uDCDD",L=W.createdAt.split("T")[0],I=W.title.replace(/\|/g,"\\|");K.push(`| ${Y} ${W.type} | ${I} | ${L} |`)}let M=new Set;for(let W of Q)for(let Y of W.concepts)M.add(Y);if(M.size>0){let W=[...M].slice(0,10).join(", ");K.push(""),K.push(`**Key concepts:** ${W}`)}let X=Q.filter((W)=>W.type==="decision").map((W)=>W.title);if(X.length>0)K.push(""),K.push(`**Recent decisions:** ${X.slice(0,5).join("; ")}`);return K.join(`
338
- `)}async function G$($,H){if(!w($))return;let Q=(y.get($)??Promise.resolve()).then(async()=>{let Z=U($,"AGENTS.md"),K=U($,".AGENTS.md.tmp"),M="";try{M=await X$(Z,"utf-8")}catch{}let X=_$(M,H);try{await W$(k(K),{recursive:!0}),await B$(K,X,"utf-8"),await Y$(K,Z)}catch(W){try{await V$(K)}catch{}throw W}});return y.set($,Q.catch(()=>{})),Q}function _$($,H){if(!$)return`${O}
361
+ VALUES (?, ?, ?, 'active')`,[$,H,J]),this.getById($)}getOrCreate($,H){let J=this.getById($);if(J)return J;return this.create($,H)}getById($){let H=this.db.get("SELECT * FROM sessions WHERE id = ?",[$]);return H?this.mapRow(H):null}getRecent($,H=10){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC LIMIT ?",[$,H]).map((J)=>this.mapRow(J))}getAll($){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC",[$]).map((H)=>this.mapRow(H))}getActive(){return this.db.all("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at DESC").map(($)=>this.mapRow($))}updateStatus($,H){this.db.run("UPDATE sessions SET status = ? WHERE id = ?",[H,$])}markCompleted($){this.db.run("UPDATE sessions SET status = 'completed', ended_at = datetime('now') WHERE id = ?",[$])}incrementObservationCount($){this.db.run("UPDATE sessions SET observation_count = observation_count + 1 WHERE id = ?",[$])}setSummary($,H){this.db.run("UPDATE sessions SET summary_id = ? WHERE id = ?",[H,$])}mapRow($){return{id:$.id,projectPath:$.project_path,startedAt:$.started_at,endedAt:$.ended_at??null,status:$.status,observationCount:$.observation_count,summaryId:$.summary_id??null}}}import{existsSync as T$}from"fs";import{readdir as A$,readFile as E$,writeFile as k$}from"fs/promises";import{join as F$,resolve as R$}from"path";import{existsSync as g}from"fs";import{mkdir as W$,readFile as X$,rename as Y$,unlink as V$,writeFile as B$}from"fs/promises";import{dirname as k,isAbsolute as w,join as U,normalize as L$,relative as v,resolve as E,sep as A}from"path";var O="<!-- open-mem-context -->",V="<!-- /open-mem-context -->",N$={bugfix:"\uD83D\uDD34",feature:"\uD83D\uDFE3",refactor:"\uD83D\uDD04",change:"\u2705",discovery:"\uD83D\uDD35",decision:"\u2696\uFE0F"},q=new Map,O$=new Set(["node_modules",".git","dist","coverage",".open-mem","build","__pycache__",".next",".nuxt"]);async function b($,H,J=5){if(H.length===0)return;let Q=[];for(let M of H){for(let W of M.filesModified)Q.push(W);for(let W of M.filesRead)Q.push(W)}let Z=z$(Q,$,J);if(Z.size===0)return;let K=S$(H,Z,$);for(let[M,W]of K)try{let X=U$(M,W,$);await G$(M,X)}catch(X){console.error(`[open-mem] Failed to update AGENTS.md in ${M}:`,X)}}function U$($,H,J){let Q=[...H].sort((X,Y)=>Y.createdAt.localeCompare(X.createdAt)).slice(0,10),Z=v(J,$)||".",K=[];K.push(`## Recent Activity in \`${Z}/\` (auto-generated by open-mem)`),K.push(""),K.push("| Type | Title | Date |"),K.push("|------|-------|------|");for(let X of Q){let Y=N$[X.type]||"\uD83D\uDCDD",L=X.createdAt.split("T")[0],D=X.title.replace(/\|/g,"\\|");K.push(`| ${Y} ${X.type} | ${D} | ${L} |`)}let M=new Set;for(let X of Q)for(let Y of X.concepts)M.add(Y);if(M.size>0){let X=[...M].slice(0,10).join(", ");K.push(""),K.push(`**Key concepts:** ${X}`)}let W=Q.filter((X)=>X.type==="decision").map((X)=>X.title);if(W.length>0)K.push(""),K.push(`**Recent decisions:** ${W.slice(0,5).join("; ")}`);return K.join(`
362
+ `)}async function G$($,H){if(!g($))return;let Q=(q.get($)??Promise.resolve()).then(async()=>{let Z=U($,"AGENTS.md"),K=U($,".AGENTS.md.tmp"),M="";try{M=await X$(Z,"utf-8")}catch{}let W=_$(M,H);try{await W$(k(K),{recursive:!0}),await B$(K,W,"utf-8"),await Y$(K,Z)}catch(X){try{await V$(K)}catch{}throw X}});return q.set($,Q.catch(()=>{})),Q}function _$($,H){if(!$)return`${O}
339
363
  ${H}
340
364
  ${V}
341
365
  `;let J=$.indexOf(O),Q=$.indexOf(V);if(J!==-1&&Q!==-1&&Q>J){let Z=$.substring(0,J),K=$.substring(Q+V.length);return`${Z}${O}
@@ -345,12 +369,12 @@ ${V}${K}`}return`${$}
345
369
  ${O}
346
370
  ${H}
347
371
  ${V}
348
- `}function z$($,H,J){let Q=new Set,Z=E(H);for(let K of $){if(!K||!K.trim())continue;if(K.startsWith("~")||K.startsWith("http"))continue;let M=g(K)?K:U(H,K),X=k(M),W=E(X);if(!W.startsWith(Z+A)&&W!==Z)continue;if(W===Z)continue;let Y=v(Z,W);if(Y.split(A).length>J)continue;if(L$(Y).split(A).some((o)=>O$.has(o)))continue;if(!w(W))continue;Q.add(W)}return Q}function S$($,H,J){let Q=new Map;for(let Z of $){let K=[...Z.filesModified,...Z.filesRead],M=new Set;for(let X of K){if(!X)continue;let W=g(X)?X:U(J,X),Y=E(k(W));if(H.has(Y))M.add(Y)}for(let X of M){let W=Q.get(X)??[];W.push(Z),Q.set(X,W)}}return Q}var C$="<!-- open-mem-context -->",P="<!-- /open-mem-context -->";async function h($,H){let J;try{J=await A$($,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let Q of J){let Z=String(Q.name);if(Z===".git"||Z==="node_modules"||Z===".open-mem"||Z==="dist")continue;let K=F$($,Z);if(Q.isDirectory())await h(K,H);else if(Q.isFile()&&Z==="AGENTS.md")H.push(K)}}async function m($){let H=R$($),J=[];return await h(H,J),J}function I$($){let H=$.indexOf(C$),J=$.indexOf(P);if(H===-1||J===-1||J<=H)return $;let Q=$.slice(0,H).trimEnd(),Z=$.slice(J+P.length).trimStart();if(!Q&&!Z)return"";if(!Q)return`${Z}
372
+ `}function z$($,H,J){let Q=new Set,Z=E(H);for(let K of $){if(!K||!K.trim())continue;if(K.startsWith("~")||K.startsWith("http"))continue;let M=w(K)?K:U(H,K),W=k(M),X=E(W);if(!X.startsWith(Z+A)&&X!==Z)continue;if(X===Z)continue;let Y=v(Z,X);if(Y.split(A).length>J)continue;if(L$(Y).split(A).some((o)=>O$.has(o)))continue;if(!g(X))continue;Q.add(X)}return Q}function S$($,H,J){let Q=new Map;for(let Z of $){let K=[...Z.filesModified,...Z.filesRead],M=new Set;for(let W of K){if(!W)continue;let X=w(W)?W:U(J,W),Y=E(k(X));if(H.has(Y))M.add(Y)}for(let W of M){let X=Q.get(W)??[];X.push(Z),Q.set(W,X)}}return Q}var C$="<!-- open-mem-context -->",P="<!-- /open-mem-context -->";async function m($,H){let J;try{J=await A$($,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let Q of J){let Z=String(Q.name);if(Z===".git"||Z==="node_modules"||Z===".open-mem"||Z==="dist")continue;let K=F$($,Z);if(Q.isDirectory())await m(K,H);else if(Q.isFile()&&Z==="AGENTS.md")H.push(K)}}async function h($){let H=R$($),J=[];return await m(H,J),J}function D$($){let H=$.indexOf(C$),J=$.indexOf(P);if(H===-1||J===-1||J<=H)return $;let Q=$.slice(0,H).trimEnd(),Z=$.slice(J+P.length).trimStart();if(!Q&&!Z)return"";if(!Q)return`${Z}
349
373
  `;if(!Z)return`${Q}
350
374
  `;return`${Q}
351
375
 
352
376
  ${Z}
353
- `}async function c($,H=!1){let J=await m($),Q=0;for(let Z of J){let K=await E$(Z,"utf-8"),M=I$(K);if(M!==K){if(Q+=1,!H)await k$(Z,M,"utf-8")}}return{files:J,changed:Q}}async function p($,H,J,Q,Z=!1){let M=H.getAll($).flatMap((W)=>J.getBySession(W.id));if(Z){let W=new Set;for(let Y of M)for(let L of[...Y.filesRead,...Y.filesModified])W.add(L);return{observations:M.length,filesTouched:W.size}}if(!T$($))return{observations:0,filesTouched:0};await b($,M,Q);let X=await m($);return{observations:M.length,filesTouched:X.length}}import{spawnSync as l}from"child_process";import{dirname as x$,resolve as n}from"path";function D$($){try{let H=l("git",["rev-parse","--git-common-dir"],{cwd:$,encoding:"utf-8",timeout:5000});if(H.status!==0||!H.stdout)return null;let J=H.stdout.trim();if(J===".git")return null;let Q=l("git",["rev-parse","--git-dir"],{cwd:$,encoding:"utf-8",timeout:5000});if(Q.status!==0||!Q.stdout)return null;let Z=Q.stdout.trim(),K=n($,J),M=n($,Z);if(K===M)return null;let X=x$(K);if(X===K||X==="/")return null;return X}catch{return null}}function d($){return D$($)??$}var{positionals:i,values:C}=f$({args:Bun.argv.slice(2),options:{project:{type:"string",short:"p"},"dry-run":{type:"boolean",default:!1}},allowPositionals:!0,strict:!1}),B=i[0]??"help",R=i[1]??"",j$=typeof C.project==="string"?C.project:process.cwd(),G=d(j$);function s(){console.log(`Usage:
377
+ `}async function c($,H=!1){let J=await h($),Q=0;for(let Z of J){let K=await E$(Z,"utf-8"),M=D$(K);if(M!==K){if(Q+=1,!H)await k$(Z,M,"utf-8")}}return{files:J,changed:Q}}async function p($,H,J,Q,Z=!1){let M=H.getAll($).flatMap((X)=>J.getBySession(X.id));if(Z){let X=new Set;for(let Y of M)for(let L of[...Y.filesRead,...Y.filesModified])X.add(L);return{observations:M.length,filesTouched:X.size}}if(!T$($))return{observations:0,filesTouched:0};await b($,M,Q);let W=await h($);return{observations:M.length,filesTouched:W.length}}import{spawnSync as l}from"child_process";import{dirname as I$,resolve as n}from"path";function x$($){try{let H=l("git",["rev-parse","--git-common-dir"],{cwd:$,encoding:"utf-8",timeout:5000});if(H.status!==0||!H.stdout)return null;let J=H.stdout.trim();if(J===".git")return null;let Q=l("git",["rev-parse","--git-dir"],{cwd:$,encoding:"utf-8",timeout:5000});if(Q.status!==0||!Q.stdout)return null;let Z=Q.stdout.trim(),K=n($,J),M=n($,Z);if(K===M)return null;let W=I$(K);if(W===K||W==="/")return null;return W}catch{return null}}function d($){return x$($)??$}var{positionals:i,values:C}=f$({args:Bun.argv.slice(2),options:{project:{type:"string",short:"p"},"dry-run":{type:"boolean",default:!1}},allowPositionals:!0,strict:!1}),B=i[0]??"help",R=i[1]??"",j$=typeof C.project==="string"?C.project:process.cwd(),G=d(j$);function s(){console.log(`Usage:
354
378
  open-mem-maintenance reset-db --project <path>
355
379
  open-mem-maintenance folder-context clean --project <path> [--dry-run]
356
- open-mem-maintenance folder-context rebuild --project <path> [--dry-run]`)}async function q$(){if(B==="help"||B==="--help"||B==="-h"){s();return}if(B==="reset-db"){let $=_(G);F($.dbPath,{force:!0}),F(`${$.dbPath}-wal`,{force:!0}),F(`${$.dbPath}-shm`,{force:!0}),console.log(`Removed database files for ${$.dbPath}`);return}if(B==="folder-context"&&(R==="clean"||R==="rebuild")){let $=C["dry-run"]===!0;if(R==="clean"){let M=await c(G,$);console.log(`${$?"[dry-run] ":""}Scanned ${M.files.length} AGENTS.md files, changed ${M.changed}.`);return}N.enableExtensionSupport();let H=_(G),J=j(H.dbPath);u(J,{hasVectorExtension:J.hasVectorExtension,embeddingDimension:H.embeddingDimension});let Q=new T(J),Z=new S(J),K=await p(G,Q,Z,H.folderContextMaxDepth,$);J.close(),console.log(`${$?"[dry-run] ":""}Rebuilt context from ${K.observations} observations, touched ${K.filesTouched} files.`);return}s(),process.exitCode=1}q$();
380
+ open-mem-maintenance folder-context rebuild --project <path> [--dry-run]`)}async function y$(){if(B==="help"||B==="--help"||B==="-h"){s();return}if(B==="reset-db"){let $=_(G);F($.dbPath,{force:!0}),F(`${$.dbPath}-wal`,{force:!0}),F(`${$.dbPath}-shm`,{force:!0}),console.log(`Removed database files for ${$.dbPath}`);return}if(B==="folder-context"&&(R==="clean"||R==="rebuild")){let $=C["dry-run"]===!0;if(R==="clean"){let M=await c(G,$);console.log(`${$?"[dry-run] ":""}Scanned ${M.files.length} AGENTS.md files, changed ${M.changed}.`);return}N.enableExtensionSupport();let H=_(G),J=j(H.dbPath);u(J,{hasVectorExtension:J.hasVectorExtension,embeddingDimension:H.embeddingDimension});let Q=new T(J),Z=new S(J),K=await p(G,Q,Z,H.folderContextMaxDepth,$);J.close(),console.log(`${$?"[dry-run] ":""}Rebuilt context from ${K.observations} observations, touched ${K.filesTouched} files.`);return}s(),process.exitCode=1}y$();