open-mem 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,66 +1,66 @@
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,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(`
3
+ import{rmSync as T}from"fs";import{parseArgs as jH}from"util";import{existsSync as a,readFileSync as t}from"fs";var e={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 HH(){let H={};if(process.env.OPEN_MEM_DB_PATH)H.dbPath=process.env.OPEN_MEM_DB_PATH;if(process.env.OPEN_MEM_PROVIDER)H.provider=process.env.OPEN_MEM_PROVIDER;if(process.env.OPEN_MEM_MODEL)H.model=process.env.OPEN_MEM_MODEL;if(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS)H.maxContextTokens=Number.parseInt(process.env.OPEN_MEM_MAX_CONTEXT_TOKENS,10);if(process.env.OPEN_MEM_COMPRESSION==="false")H.compressionEnabled=!1;if(process.env.OPEN_MEM_CONTEXT_INJECTION==="false")H.contextInjectionEnabled=!1;if(process.env.OPEN_MEM_IGNORED_TOOLS)H.ignoredTools=process.env.OPEN_MEM_IGNORED_TOOLS.split(",").map((J)=>J.trim());if(process.env.OPEN_MEM_BATCH_SIZE)H.batchSize=Number.parseInt(process.env.OPEN_MEM_BATCH_SIZE,10);if(process.env.OPEN_MEM_RETENTION_DAYS)H.retentionDays=Number.parseInt(process.env.OPEN_MEM_RETENTION_DAYS,10);if(process.env.OPEN_MEM_LOG_LEVEL)H.logLevel=process.env.OPEN_MEM_LOG_LEVEL;if(process.env.OPEN_MEM_CONTEXT_SHOW_TOKEN_COSTS==="false")H.contextShowTokenCosts=!1;if(process.env.OPEN_MEM_CONTEXT_TYPES)H.contextObservationTypes=process.env.OPEN_MEM_CONTEXT_TYPES==="all"?"all":process.env.OPEN_MEM_CONTEXT_TYPES.split(",").map((J)=>J.trim());if(process.env.OPEN_MEM_CONTEXT_FULL_COUNT)H.contextFullObservationCount=Number.parseInt(process.env.OPEN_MEM_CONTEXT_FULL_COUNT,10);if(process.env.OPEN_MEM_MAX_OBSERVATIONS)H.maxObservations=Number.parseInt(process.env.OPEN_MEM_MAX_OBSERVATIONS,10);if(process.env.OPEN_MEM_CONTEXT_SHOW_LAST_SUMMARY==="false")H.contextShowLastSummary=!1;if(process.env.OPEN_MEM_RATE_LIMITING==="false")H.rateLimitingEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT==="false")H.folderContextEnabled=!1;if(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH)H.folderContextMaxDepth=Number.parseInt(process.env.OPEN_MEM_FOLDER_CONTEXT_MAX_DEPTH,10);if(process.env.OPEN_MEM_DAEMON==="true")H.daemonEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD==="true")H.dashboardEnabled=!0;if(process.env.OPEN_MEM_DASHBOARD_PORT)H.dashboardPort=Number.parseInt(process.env.OPEN_MEM_DASHBOARD_PORT,10);if(process.env.OPEN_MEM_PLATFORM_OPENCODE==="false")H.platformOpenCodeEnabled=!1;if(process.env.OPEN_MEM_PLATFORM_CLAUDE_CODE==="true")H.platformClaudeCodeEnabled=!0;if(process.env.OPEN_MEM_PLATFORM_CURSOR==="true")H.platformCursorEnabled=!0;if(process.env.OPEN_MEM_MCP_COMPAT_MODE)H.mcpCompatibilityMode=process.env.OPEN_MEM_MCP_COMPAT_MODE;if(process.env.OPEN_MEM_MCP_PROTOCOL_VERSION)H.mcpProtocolVersion=process.env.OPEN_MEM_MCP_PROTOCOL_VERSION;if(process.env.OPEN_MEM_MCP_SUPPORTED_PROTOCOLS)H.mcpSupportedProtocolVersions=process.env.OPEN_MEM_MCP_SUPPORTED_PROTOCOLS.split(",").map((J)=>J.trim()).filter(Boolean);if(process.env.OPEN_MEM_EMBEDDING_DIMENSION)H.embeddingDimension=Number.parseInt(process.env.OPEN_MEM_EMBEDDING_DIMENSION,10);if(process.env.OPEN_MEM_CONFLICT_RESOLUTION==="true")H.conflictResolutionEnabled=!0;if(process.env.OPEN_MEM_CONFLICT_BAND_LOW){let J=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_LOW);if(!Number.isNaN(J))H.conflictSimilarityBandLow=J}if(process.env.OPEN_MEM_CONFLICT_BAND_HIGH){let J=Number.parseFloat(process.env.OPEN_MEM_CONFLICT_BAND_HIGH);if(!Number.isNaN(J))H.conflictSimilarityBandHigh=J}if(process.env.OPEN_MEM_USER_MEMORY==="true")H.userMemoryEnabled=!0;if(process.env.OPEN_MEM_USER_MEMORY_DB_PATH)H.userMemoryDbPath=process.env.OPEN_MEM_USER_MEMORY_DB_PATH;if(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS)H.userMemoryMaxContextTokens=Number.parseInt(process.env.OPEN_MEM_USER_MEMORY_MAX_TOKENS,10);if(process.env.OPEN_MEM_RERANKING==="true")H.rerankingEnabled=!0;if(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES)H.rerankingMaxCandidates=Number.parseInt(process.env.OPEN_MEM_RERANKING_MAX_CANDIDATES,10);if(process.env.OPEN_MEM_ENTITY_EXTRACTION==="true")H.entityExtractionEnabled=!0;return H}function JH(H){let J=`${H}/.open-mem/config.json`;if(!a(J))return{};try{let Q=t(J,"utf-8"),Z=JSON.parse(Q);if(!Z||typeof Z!=="object"||Array.isArray(Z))return{};return Z}catch{return{}}}function QH(H){switch(H){case"google":return 768;case"openai":return 1536;case"bedrock":return 1024;case"anthropic":return 0;default:return 768}}function G(H,J){let Q=JH(H),Z=HH(),$={...e,...Q,...Z,...J};if(!$.dbPath.startsWith("/"))$.dbPath=`${H}/${$.dbPath}`;if(!process.env.OPEN_MEM_PROVIDER&&!J?.provider){if(process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY)$.provider="google";else if(process.env.ANTHROPIC_API_KEY)$.provider="anthropic";else if(process.env.AWS_BEARER_TOKEN_BEDROCK||process.env.AWS_ACCESS_KEY_ID||process.env.AWS_PROFILE)$.provider="bedrock"}if(!$.apiKey)switch($.provider){case"google":$.apiKey=process.env.GOOGLE_GENERATIVE_AI_API_KEY||process.env.GEMINI_API_KEY;break;case"anthropic":$.apiKey=process.env.ANTHROPIC_API_KEY;break;case"openai":$.apiKey=process.env.OPENAI_API_KEY;break;case"bedrock":break}if($.embeddingDimension===void 0)$.embeddingDimension=QH($.provider);return $}import{Database as q}from"bun:sqlite";import{existsSync as _,mkdirSync as ZH,unlinkSync as j}from"fs";import*as y from"sqlite-vec";class z{db;dbPath;_hasVectorExtension=!1;static enableExtensionSupport(){let H=["/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib","/usr/local/opt/sqlite/lib/libsqlite3.dylib"];for(let J of H)try{if(_(J))return q.setCustomSQLite(J),!0}catch{return!1}return!1}constructor(H){this.dbPath=H,this.db=this.open(H),this.configure()}open(H){let J=H.lastIndexOf("/");if(J>0){let Q=H.substring(0,J);ZH(Q,{recursive:!0})}return new q(H,{create:!0})}configure(){try{this.applyPragmas(),this.loadExtensions()}catch(H){console.warn("[open-mem] Database configure failed, attempting recovery by removing WAL/SHM files:",H.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(J){console.warn("[open-mem] WAL/SHM cleanup insufficient, recreating database from scratch:",J.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(Q){throw console.warn("[open-mem] All recovery attempts failed, filesystem may be broken:",Q.message),H}}}}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{y.load(this.db),this._hasVectorExtension=!0}catch{this._hasVectorExtension=!1}}get hasVectorExtension(){return this._hasVectorExtension}deleteSidecarFiles(){for(let H of["-wal","-shm"]){let J=this.dbPath+H;try{if(_(J))j(J)}catch{}}}deleteDatabaseFiles(){this.deleteSidecarFiles();try{if(_(this.dbPath))j(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 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
9
+ `)}migrate(H){this.ensureMigrationTable();let J=this.db.query("SELECT version FROM _migrations ORDER BY version").all(),Q=new Set(J.map(($)=>$.version)),Z=H.filter(($)=>!Q.has($.version)).sort(($,K)=>$.version-K.version);for(let $ of Z)this.db.transaction(()=>{this.db.exec($.up),this.db.query("INSERT INTO _migrations (version, name) VALUES ($version, $name)").run({$version:$.version,$name:$.name})})()}run(H,J){let Q=this.db.query(H);if(J)Q.run(...J);else Q.run()}get(H,J){let Q=this.db.query(H);return J?Q.get(...J):Q.get()}all(H,J){let Q=this.db.query(H);return J?Q.all(...J):Q.all()}exec(H){this.db.exec(H)}transaction(H){return this.db.transaction(H)()}close(){this.db.close()}get isOpen(){try{return this.db.query("SELECT 1").get(),!0}catch{return!1}}get raw(){return this.db}}function w(H){return new z(H)}import{randomUUID as $H}from"crypto";import{embed as hH}from"ai";function v(H,J){if(H.length!==J.length||H.length===0)return 0;let Q=0,Z=0,$=0;for(let W=0;W<H.length;W++)Q+=H[W]*J[W],Z+=H[W]*H[W],$+=J[W]*J[W];let K=Math.sqrt(Z)*Math.sqrt($);if(K===0)return 0;return Q/K}function KH(H){return H.replace(/[%_\\]/g,"\\$&")}class E{db;constructor(H){this.db=H}create(H){let J=$H(),Q=new Date().toISOString(),Z=H.discoveryTokens??0,$=H.importance??3,K=H.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)
13
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[H,$.sessionId,K,$.type,$.title,$.subtitle,JSON.stringify($.facts),$.narrative,JSON.stringify($.concepts),JSON.stringify($.filesRead),JSON.stringify($.filesModified),$.rawToolOutput,$.toolName,J,$.tokenCount,Q,Z,null,null]),{...$,id:H,scope:K,createdAt:J,discoveryTokens:Q,importance:Z,revisionOf:null,deletedAt:null,supersededBy:null,supersededAt:null}}importObservation($){this.db.run(`INSERT INTO observations
13
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[J,H.sessionId,K,H.type,H.title,H.subtitle,JSON.stringify(H.facts),H.narrative,JSON.stringify(H.concepts),JSON.stringify(H.filesRead),JSON.stringify(H.filesModified),H.rawToolOutput,H.toolName,Q,H.tokenCount,Z,$,null,null]),{...H,id:J,scope:K,createdAt:Q,discoveryTokens:Z,importance:$,revisionOf:null,deletedAt:null,supersededBy:null,supersededAt:null}}importObservation(H){this.db.run(`INSERT INTO observations
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}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
17
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[H.id,H.sessionId,H.scope??"project",H.type,H.title,H.subtitle,JSON.stringify(H.facts),H.narrative,JSON.stringify(H.concepts),JSON.stringify(H.filesRead),JSON.stringify(H.filesModified),H.rawToolOutput,H.toolName,H.createdAt,H.tokenCount,H.discoveryTokens??0,H.importance??3,H.revisionOf??null,H.deletedAt??null])}getById(H){let J=this.db.get("SELECT * FROM observations WHERE id = ? AND superseded_by IS NULL AND deleted_at IS NULL",[H]);return J?this.mapRow(J):null}getByIdIncludingArchived(H){let J=this.db.get("SELECT * FROM observations WHERE id = ?",[H]);return J?this.mapRow(J):null}getBySession(H){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",[H]).map((J)=>this.mapRow(J))}getCount(H){if(H)return this.db.get("SELECT COUNT(*) as count FROM observations WHERE session_id = ?",[H])?.count??0;return this.db.get("SELECT COUNT(*) as count FROM observations")?.count??0}getIndex(H,J=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}))}listByProject($,H={}){let{limit:J=50,offset:Q=0,type:Z,state:K,sessionId:M}=H,W=`SELECT o.*
22
+ LIMIT ?`,[H,J]).map((Q)=>({id:Q.id,sessionId:Q.session_id,type:Q.type,title:Q.title,tokenCount:Q.token_count,discoveryTokens:Q.discovery_tokens??0,createdAt:Q.created_at,importance:Q.importance??3}))}listByProject(H,J={}){let{limit:Q=50,offset:Z=0,type:$,state:K,sessionId:W}=J,X=`SELECT o.*
23
23
  FROM observations o
24
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=`
25
+ WHERE s.project_path = ?`,Y=[H];if(W)X+=" AND o.session_id = ?",Y.push(W);if($)X+=" AND o.type = ?",Y.push($);if(K==="current")X+=" AND o.superseded_by IS NULL AND o.deleted_at IS NULL";else if(K==="superseded")X+=" AND o.superseded_by IS NOT NULL AND o.deleted_at IS NULL";else if(K==="tombstoned")X+=" AND o.deleted_at IS NOT NULL";else X+=" AND o.superseded_by IS NULL AND o.deleted_at IS NULL";return X+=" ORDER BY o.created_at DESC LIMIT ? OFFSET ?",Y.push(Q,Z),this.db.all(X,Y).map((V)=>this.mapRow(V))}search(H){let J=!!H.projectPath,Q=`
26
26
  SELECT o.*, rank
27
27
  FROM observations o
28
28
  JOIN observations_fts fts ON o._rowid = fts.rowid
29
- ${H?"JOIN sessions s ON o.session_id = s.id":""}
29
+ ${J?"JOIN sessions s ON o.session_id = s.id":""}
30
30
  WHERE observations_fts MATCH ? AND o.superseded_by IS NULL AND o.deleted_at IS NULL
31
- `,Q=[$.query];if(H&&$.projectPath)J+=" AND s.project_path = ?",Q.push($.projectPath);if($.sessionId)J+=" AND o.session_id = ?",Q.push($.sessionId);if($.type)J+=" AND o.type = ?",Q.push($.type);if($.importanceMin!==void 0)J+=" AND o.importance >= ?",Q.push($.importanceMin);if($.importanceMax!==void 0)J+=" AND o.importance <= ?",Q.push($.importanceMax);if($.createdAfter)J+=" AND o.created_at >= ?",Q.push($.createdAfter);if($.createdBefore)J+=" AND o.created_at <= ?",Q.push($.createdBefore);if($.concepts&&$.concepts.length>0){let Z=$.concepts.map(()=>"EXISTS (SELECT 1 FROM json_each(o.concepts) WHERE LOWER(value) = LOWER(?))");J+=` AND (${Z.join(" OR ")})`;for(let K of $.concepts)Q.push(K)}if($.files&&$.files.length>0){let Z=$.files.map(()=>`(EXISTS (SELECT 1 FROM json_each(o.files_read) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\')
32
- OR EXISTS (SELECT 1 FROM json_each(o.files_modified) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\'))`);J+=` AND (${Z.join(" OR ")})`;for(let K of $.files){let M=`%${Z$(K)}%`;Q.push(M,M)}}return J+=" ORDER BY rank LIMIT ? OFFSET ?",Q.push($.limit??10),Q.push($.offset??0),this.db.all(J,Q).map((Z)=>({observation:this.mapRow(Z),rank:Z.rank,snippet:Z.title}))}searchByConcept($,H=10,J){let Q=!!J,Z=`SELECT o.*
31
+ `,Z=[H.query];if(J&&H.projectPath)Q+=" AND s.project_path = ?",Z.push(H.projectPath);if(H.sessionId)Q+=" AND o.session_id = ?",Z.push(H.sessionId);if(H.type)Q+=" AND o.type = ?",Z.push(H.type);if(H.importanceMin!==void 0)Q+=" AND o.importance >= ?",Z.push(H.importanceMin);if(H.importanceMax!==void 0)Q+=" AND o.importance <= ?",Z.push(H.importanceMax);if(H.createdAfter)Q+=" AND o.created_at >= ?",Z.push(H.createdAfter);if(H.createdBefore)Q+=" AND o.created_at <= ?",Z.push(H.createdBefore);if(H.concepts&&H.concepts.length>0){let $=H.concepts.map(()=>"EXISTS (SELECT 1 FROM json_each(o.concepts) WHERE LOWER(value) = LOWER(?))");Q+=` AND (${$.join(" OR ")})`;for(let K of H.concepts)Z.push(K)}if(H.files&&H.files.length>0){let $=H.files.map(()=>`(EXISTS (SELECT 1 FROM json_each(o.files_read) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\')
32
+ OR EXISTS (SELECT 1 FROM json_each(o.files_modified) WHERE LOWER(value) LIKE LOWER(?) ESCAPE '\\'))`);Q+=` AND (${$.join(" OR ")})`;for(let K of H.files){let W=`%${KH(K)}%`;Z.push(W,W)}}return Q+=" ORDER BY rank LIMIT ? OFFSET ?",Z.push(H.limit??10),Z.push(H.offset??0),this.db.all(Q,Z).map(($)=>({observation:this.mapRow($),rank:$.rank,snippet:$.title}))}searchByConcept(H,J=10,Q){let Z=!!Q,$=`SELECT o.*
33
33
  FROM observations o
34
34
  JOIN observations_fts fts ON o._rowid = fts.rowid
35
- ${Q?"JOIN sessions s ON o.session_id = s.id":""}
35
+ ${Z?"JOIN sessions s ON o.session_id = s.id":""}
36
36
  WHERE observations_fts MATCH ?
37
37
  AND o.superseded_by IS NULL AND o.deleted_at IS NULL
38
- ${Q?"AND s.project_path = ?":""}
38
+ ${Z?"AND s.project_path = ?":""}
39
39
  ORDER BY rank
40
- LIMIT ?`,K=[`concepts:${$}`];if(Q&&J)K.push(J);return K.push(H),this.db.all(Z,K).map((M)=>this.mapRow(M))}searchByFile($,H=10,J){let Q=!!J,Z=`SELECT o.*
40
+ LIMIT ?`,K=[`concepts:${H}`];if(Z&&Q)K.push(Q);return K.push(J),this.db.all($,K).map((W)=>this.mapRow(W))}searchByFile(H,J=10,Q){let Z=!!Q,$=`SELECT o.*
41
41
  FROM observations o
42
42
  JOIN observations_fts fts ON o._rowid = fts.rowid
43
- ${Q?"JOIN sessions s ON o.session_id = s.id":""}
43
+ ${Z?"JOIN sessions s ON o.session_id = s.id":""}
44
44
  WHERE observations_fts MATCH ?
45
45
  AND o.superseded_by IS NULL AND o.deleted_at IS NULL
46
- ${Q?"AND s.project_path = ?":""}
46
+ ${Z?"AND s.project_path = ?":""}
47
47
  ORDER BY rank
48
- LIMIT ?`,K=[`files_read:"${$.replace(/"/g,'""')}" OR files_modified:"${$.replace(/"/g,'""')}"`];if(Q&&J)K.push(J);return K.push(H),this.db.all(Z,K).map((M)=>this.mapRow(M))}setEmbedding($,H){this.db.run("UPDATE observations SET embedding = ? WHERE id = ?",[JSON.stringify(H),$])}getWithEmbeddings($,H){return this.db.all(`SELECT o.id, o.embedding, o.title
48
+ LIMIT ?`,K=[`files_read:"${H.replace(/"/g,'""')}" OR files_modified:"${H.replace(/"/g,'""')}"`];if(Z&&Q)K.push(Q);return K.push(J),this.db.all($,K).map((W)=>this.mapRow(W))}setEmbedding(H,J){this.db.run("UPDATE observations SET embedding = ? WHERE id = ?",[JSON.stringify(J),H])}getWithEmbeddings(H,J){return this.db.all(`SELECT o.id, o.embedding, o.title
49
49
  FROM observations o
50
50
  JOIN sessions s ON o.session_id = s.id
51
51
  WHERE s.project_path = ? AND o.embedding IS NOT NULL AND o.superseded_by IS NULL AND o.deleted_at IS NULL
52
52
  ORDER BY o.created_at DESC
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
53
+ LIMIT ?`,[H,J]).map((Q)=>{try{return{id:Q.id,embedding:JSON.parse(Q.embedding),title:Q.title}}catch{return null}}).filter((Q)=>Q!==null)}findSimilar(H,J,Q,Z){let $=this.db.all(`SELECT id, embedding FROM observations
54
54
  WHERE embedding IS NOT NULL AND type = ? AND superseded_by IS NULL AND deleted_at IS NULL
55
55
  ORDER BY created_at DESC
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
56
+ LIMIT 200`,[J]),K=[];for(let W of $)try{let X=JSON.parse(W.embedding);if(!Array.isArray(X)||X.length!==H.length)continue;let Y=v(H,X);if(Y>=Q)K.push({id:W.id,similarity:Y})}catch{}return K.sort((W,X)=>X.similarity-W.similarity).slice(0,Z)}insertVecEmbedding(H,J){let Q=new Float32Array(J);this.db.run("BEGIN");try{this.db.run("DELETE FROM observation_embeddings WHERE observation_id = ?",[H]),this.db.run("INSERT INTO observation_embeddings (observation_id, embedding) VALUES (?, ?)",[H,Q]),this.db.run("COMMIT")}catch(Z){throw this.db.run("ROLLBACK"),Z}}migrateExistingEmbeddings(H){let J=this.db.all("SELECT id, embedding FROM observations WHERE embedding IS NOT NULL"),Q=0,Z=0;for(let $ of J)try{let K=JSON.parse($.embedding);if(!Array.isArray(K)||K.length!==H){Z++;continue}this.insertVecEmbedding($.id,K),Q++}catch{Z++}return{migrated:Q,skipped:Z}}getVecEmbeddingMatches(H,J){try{let Q=new Float32Array(H);return this.db.all(`SELECT observation_id, distance
57
57
  FROM observation_embeddings
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
58
+ WHERE embedding MATCH ? AND k = ?`,[Q,J]).map((Z)=>({observationId:Z.observation_id,distance:Z.distance}))}catch{return[]}}searchVecSubset(H,J,Q){if(J.length===0)return[];try{let Z=new Float32Array(H),$=Math.max(Q*5,J.length),K=this.db.all(`SELECT observation_id, distance
59
59
  FROM observation_embeddings
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
60
+ WHERE embedding MATCH ? AND k = ?`,[Z,$]),W=new Set(J);return K.filter((X)=>W.has(X.observation_id)).slice(0,Q).map((X)=>({observationId:X.observation_id,distance:X.distance}))}catch{return[]}}update(H,J){let Q=this.getById(H);if(!Q)return null;if(Object.keys(J).length===0)return Q;let Z=this.create({sessionId:Q.sessionId,scope:Q.scope??"project",type:J.type??Q.type,title:J.title??Q.title,subtitle:J.subtitle??Q.subtitle,facts:J.facts??Q.facts,narrative:J.narrative??Q.narrative,concepts:J.concepts??Q.concepts,filesRead:J.filesRead??Q.filesRead,filesModified:J.filesModified??Q.filesModified,rawToolOutput:Q.rawToolOutput,toolName:"memory.revise",tokenCount:Q.tokenCount,discoveryTokens:Q.discoveryTokens,importance:J.importance??Q.importance});return this.db.run("UPDATE observations SET revision_of = ? WHERE id = ?",[H,Z.id]),this.supersede(H,Z.id),this.getById(Z.id)}supersede(H,J){let Q=new Date().toISOString();this.db.run("UPDATE observations SET superseded_by = ?, superseded_at = ? WHERE id = ?",[J,Q,H])}delete(H){if(this.db.all("SELECT id FROM observations WHERE id = ?",[H]).length===0)return!1;let Q=new Date().toISOString();return this.db.run("UPDATE observations SET deleted_at = ? WHERE id = ?",[Q,H]),this.deleteEmbeddingsForObservations([H]),!0}getLineage(H){let J=this.getByIdIncludingArchived(H);if(!J)return[];let Q=new Set([J.id]),Z=[J];while(Z[0].revisionOf){let $=this.getByIdIncludingArchived(Z[0].revisionOf);if(!$||Q.has($.id))break;Z.unshift($),Q.add($.id)}while(Z[Z.length-1].supersededBy){let $=Z[Z.length-1].supersededBy;if(!$)break;let K=this.getByIdIncludingArchived($);if(!K||Q.has(K.id))break;Z.push(K),Q.add(K.id)}return Z}deleteOlderThan(H){return this.db.all(`DELETE FROM observations
61
61
  WHERE (created_at < datetime('now', '-' || ? || ' days') OR deleted_at IS NOT NULL)
62
62
  AND session_id NOT IN (SELECT id FROM sessions WHERE status != 'completed')
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:`
63
+ RETURNING id`,[H]).length}deleteEmbeddingsForObservations(H){if(H.length===0)return;let J=H.map(()=>"?").join(",");try{this.db.run(`DELETE FROM observation_embeddings WHERE observation_id IN (${J})`,H)}catch{}this.db.run(`UPDATE observations SET embedding = NULL WHERE id IN (${J})`,H)}mapRow(H){return{id:H.id,sessionId:H.session_id,scope:H.scope??"project",type:H.type,title:H.title,subtitle:H.subtitle,facts:JSON.parse(H.facts),narrative:H.narrative,concepts:JSON.parse(H.concepts),filesRead:JSON.parse(H.files_read),filesModified:JSON.parse(H.files_modified),rawToolOutput:H.raw_tool_output,toolName:H.tool_name,createdAt:H.created_at,tokenCount:H.token_count,discoveryTokens:H.discovery_tokens??0,importance:H.importance??3,revisionOf:H.revision_of??null,deletedAt:H.deleted_at??null,supersededBy:H.superseded_by??null,supersededAt:H.superseded_at??null}}}var WH=[{version:1,name:"create-schema",up:`
64
64
  -- Sessions table
65
65
  CREATE TABLE IF NOT EXISTS sessions (
66
66
  _rowid INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -354,27 +354,30 @@ import{rmSync as F}from"fs";import{parseArgs as f$}from"util";import{existsSync
354
354
  INSERT INTO entities_fts(rowid, name, entity_type)
355
355
  VALUES (new._rowid, new.name, new.entity_type);
356
356
  END;
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(
357
+ `}];function g(H,J){if(H.migrate(WH),J?.hasVectorExtension&&J?.embeddingDimension&&J.embeddingDimension>0)XH(H,J.embeddingDimension)}function XH(H,J){if(H.get("SELECT name FROM sqlite_master WHERE type='table' AND name='observation_embeddings'")){let Z=H.get("SELECT value FROM _embedding_meta WHERE key = 'dimension'");if(Z&&Number(Z.value)!==J){console.warn(`[open-mem] vec0 table exists with dimension ${Z.value}, but config specifies ${J}. Drop observation_embeddings to re-create with new dimension.`);return}}else H.exec(`CREATE VIRTUAL TABLE observation_embeddings USING vec0(
358
358
  observation_id TEXT PRIMARY KEY,
359
- embedding float[${H}] distance_metric=cosine
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)
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}
363
- ${H}
364
- ${V}
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}
366
- ${H}
367
- ${V}${K}`}return`${$}
359
+ embedding float[${J}] distance_metric=cosine
360
+ )`);H.run("INSERT OR REPLACE INTO _embedding_meta (key, value) VALUES (?, ?)",["dimension",String(J)])}class F{db;constructor(H){this.db=H}create(H,J){let Q=new Date().toISOString();return this.db.run(`INSERT INTO sessions (id, project_path, started_at, status)
361
+ VALUES (?, ?, ?, 'active')`,[H,J,Q]),this.getById(H)}getOrCreate(H,J){let Q=this.getById(H);if(Q)return Q;return this.create(H,J)}getById(H){let J=this.db.get("SELECT * FROM sessions WHERE id = ?",[H]);return J?this.mapRow(J):null}getRecent(H,J=10){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC LIMIT ?",[H,J]).map((Q)=>this.mapRow(Q))}getAll(H){return this.db.all("SELECT * FROM sessions WHERE project_path = ? ORDER BY started_at DESC",[H]).map((J)=>this.mapRow(J))}getActive(){return this.db.all("SELECT * FROM sessions WHERE status = 'active' ORDER BY started_at DESC").map((H)=>this.mapRow(H))}updateStatus(H,J){this.db.run("UPDATE sessions SET status = ? WHERE id = ?",[J,H])}markCompleted(H){this.db.run("UPDATE sessions SET status = 'completed', ended_at = datetime('now') WHERE id = ?",[H])}incrementObservationCount(H){this.db.run("UPDATE sessions SET observation_count = observation_count + 1 WHERE id = ?",[H])}setSummary(H,J){this.db.run("UPDATE sessions SET summary_id = ? WHERE id = ?",[J,H])}mapRow(H){return{id:H.id,projectPath:H.project_path,startedAt:H.started_at,endedAt:H.ended_at??null,status:H.status,observationCount:H.observation_count,summaryId:H.summary_id??null}}}import{existsSync as FH}from"fs";import{readdir as SH,readFile as RH,unlink as AH,writeFile as CH}from"fs/promises";import{join as TH,resolve as IH}from"path";import{existsSync as P}from"fs";import{mkdir as YH,readFile as VH,rename as LH,unlink as UH,writeFile as BH}from"fs/promises";import{dirname as A,isAbsolute as b,join as N,normalize as MH,relative as f,resolve as R,sep as S}from"path";var U="<!-- open-mem-context -->",L="<!-- /open-mem-context -->",zH={bugfix:"\uD83D\uDD34",feature:"\uD83D\uDFE3",refactor:"\uD83D\uDD04",change:"\u2705",discovery:"\uD83D\uDD35",decision:"\u2696\uFE0F"},u=new Map,NH=new Set(["node_modules",".git","dist","coverage",".open-mem","build","__pycache__",".next",".nuxt"]);async function h(H,J,Q=5){if(J.length===0)return;let Z=[];for(let W of J){for(let X of W.filesModified)Z.push(X);for(let X of W.filesRead)Z.push(X)}let $=_H(Z,H,Q);if($.size===0)return;let K=EH(J,$,H);for(let[W,X]of K)try{let Y=OH(W,X,H);await kH(W,Y)}catch(Y){console.error(`[open-mem] Failed to update AGENTS.md in ${W}:`,Y)}}function OH(H,J,Q){let Z=[...J].sort((Y,V)=>V.createdAt.localeCompare(Y.createdAt)).slice(0,10),$=f(Q,H)||".",K=[];K.push(`## Recent Activity in \`${$}/\` (auto-generated by open-mem)`),K.push(""),K.push("| Type | Title | Date |"),K.push("|------|-------|------|");for(let Y of Z){let V=zH[Y.type]||"\uD83D\uDCDD",M=Y.createdAt.split("T")[0],x=Y.title.replace(/\|/g,"\\|");K.push(`| ${V} ${Y.type} | ${x} | ${M} |`)}let W=new Set;for(let Y of Z)for(let V of Y.concepts)W.add(V);if(W.size>0){let Y=[...W].slice(0,10).join(", ");K.push(""),K.push(`**Key concepts:** ${Y}`)}let X=Z.filter((Y)=>Y.type==="decision").map((Y)=>Y.title);if(X.length>0)K.push(""),K.push(`**Recent decisions:** ${X.slice(0,5).join("; ")}`);return K.join(`
362
+ `)}async function kH(H,J){if(!P(H))return;let Z=(u.get(H)??Promise.resolve()).then(async()=>{let $=N(H,"AGENTS.md"),K=N(H,".AGENTS.md.tmp"),W="";try{W=await VH($,"utf-8")}catch{}let X=GH(W,J);try{await YH(A(K),{recursive:!0}),await BH(K,X,"utf-8"),await LH(K,$)}catch(Y){try{await UH(K)}catch{}throw Y}});return u.set(H,Z.catch(()=>{})),Z}function GH(H,J){if(!H)return`${U}
363
+ ${J}
364
+ ${L}
365
+ `;let Q=H.indexOf(U),Z=H.indexOf(L);if(Q!==-1&&Z!==-1&&Z>Q){let K=H.substring(0,Q),W=H.substring(Z+L.length);return`${K}${U}
366
+ ${J}
367
+ ${L}${W}`}let $=H;if(Q!==-1&&Z===-1)$=$.replace(U,"").trim();else if(Q===-1&&Z!==-1)$=$.replace(L,"").trim();else if(Q!==-1&&Z!==-1&&Z<=Q)$=$.replace(L,"").replace(U,"").trim();return`${$}
368
368
 
369
- ${O}
370
- ${H}
371
- ${V}
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}
373
- `;if(!Z)return`${Q}
374
- `;return`${Q}
369
+ ${U}
370
+ ${J}
371
+ ${L}
372
+ `}function _H(H,J,Q){let Z=new Set,$=R(J);for(let K of H){if(!K||!K.trim())continue;if(K.startsWith("~")||K.startsWith("http"))continue;let W=b(K)?K:N(J,K),X=A(W),Y=R(X);if(!Y.startsWith($+S)&&Y!==$)continue;if(Y===$)continue;let V=f($,Y);if(V.split(S).length>Q)continue;if(MH(V).split(S).some((o)=>NH.has(o)))continue;if(!P(Y))continue;Z.add(Y)}return Z}function EH(H,J,Q){let Z=new Map;for(let $ of H){let K=[...$.filesModified,...$.filesRead],W=new Set;for(let X of K){if(!X)continue;let Y=b(X)?X:N(Q,X),V=R(A(Y));if(J.has(V))W.add(V)}for(let X of W){let Y=Z.get(X)??[];Y.push($),Z.set(X,Y)}}return Z}var C="<!-- open-mem-context -->",O="<!-- /open-mem-context -->";async function m(H,J){let Q;try{Q=await SH(H,{withFileTypes:!0,encoding:"utf8"})}catch{return}for(let Z of Q){let $=String(Z.name);if($===".git"||$==="node_modules"||$===".open-mem"||$==="dist")continue;let K=TH(H,$);if(Z.isDirectory())await m(K,J);else if(Z.isFile()&&$==="AGENTS.md")J.push(K)}}async function p(H){let J=IH(H),Q=[];return await m(J,Q),Q}function DH(H){let J=H.indexOf(C),Q=H.indexOf(O);if(J===-1&&Q===-1)return H;if(J!==-1&&Q===-1){let K=H.replace(C,"").trim();return K?`${K}
373
+ `:""}if(J===-1&&Q!==-1){let K=H.replace(O,"").trim();return K?`${K}
374
+ `:""}if(Q<=J){let K=H.replace(C,"").replace(O,"").trim();return K?`${K}
375
+ `:""}let Z=H.slice(0,J).trimEnd(),$=H.slice(Q+O.length).trimStart();if(!Z&&!$)return"";if(!Z)return`${$}
376
+ `;if(!$)return`${Z}
377
+ `;return`${Z}
375
378
 
376
- ${Z}
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:
379
+ ${$}
380
+ `}async function c(H,J=!1){let Q=await p(H),Z=0;for(let $ of Q){let K=await RH($,"utf-8"),W=DH(K);if(W!==K){if(Z+=1,!J)if(W==="")await AH($);else await CH($,W,"utf-8")}}return{files:Q,changed:Z}}async function l(H,J,Q,Z,$=!1){let W=J.getAll(H).flatMap((Y)=>Q.getBySession(Y.id));if($){let Y=new Set;for(let V of W)for(let M of[...V.filesRead,...V.filesModified])Y.add(M);return{observations:W.length,filesTouched:Y.size}}if(!FH(H))return{observations:0,filesTouched:0};await h(H,W,Z);let X=await p(H);return{observations:W.length,filesTouched:X.length}}import{spawnSync as d}from"child_process";import{dirname as xH,resolve as i}from"path";function qH(H){try{let J=d("git",["rev-parse","--git-common-dir"],{cwd:H,encoding:"utf-8",timeout:5000});if(J.status!==0||!J.stdout)return null;let Q=J.stdout.trim();if(Q===".git")return null;let Z=d("git",["rev-parse","--git-dir"],{cwd:H,encoding:"utf-8",timeout:5000});if(Z.status!==0||!Z.stdout)return null;let $=Z.stdout.trim(),K=i(H,Q),W=i(H,$);if(K===W)return null;let X=xH(K);if(X===K||X==="/")return null;return X}catch{return null}}function n(H){return qH(H)??H}var{positionals:r,values:D}=jH({args:Bun.argv.slice(2),options:{project:{type:"string",short:"p"},"dry-run":{type:"boolean",default:!1}},allowPositionals:!0,strict:!1}),B=r[0]??"help",I=r[1]??"",yH=typeof D.project==="string"?D.project:process.cwd(),k=n(yH);function s(){console.log(`Usage:
378
381
  open-mem-maintenance reset-db --project <path>
379
382
  open-mem-maintenance folder-context clean --project <path> [--dry-run]
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$();
383
+ open-mem-maintenance folder-context rebuild --project <path> [--dry-run]`)}async function wH(){if(B==="help"||B==="--help"||B==="-h"){s();return}if(B==="reset-db"){let H=G(k);T(H.dbPath,{force:!0}),T(`${H.dbPath}-wal`,{force:!0}),T(`${H.dbPath}-shm`,{force:!0}),console.log(`Removed database files for ${H.dbPath}`);return}if(B==="folder-context"&&(I==="clean"||I==="rebuild")){let H=D["dry-run"]===!0;if(I==="clean"){let W=await c(k,H);console.log(`${H?"[dry-run] ":""}Scanned ${W.files.length} AGENTS.md files, changed ${W.changed}.`);return}z.enableExtensionSupport();let J=G(k),Q=w(J.dbPath);g(Q,{hasVectorExtension:Q.hasVectorExtension,embeddingDimension:J.embeddingDimension});let Z=new F(Q),$=new E(Q),K=await l(k,Z,$,J.folderContextMaxDepth,H);Q.close(),console.log(`${H?"[dry-run] ":""}Rebuilt context from ${K.observations} observations, touched ${K.filesTouched} files.`);return}s(),process.exitCode=1}wH();