UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

328 lines (327 loc) 8.78 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { logger } from "../monitoring/logger.js"; class LazyProxy { _value; _promise; _loader; _loaded = false; constructor(loader) { this._loader = loader; } async get() { if (this._loaded && this._value !== void 0) { return this._value; } if (!this._promise) { this._promise = this._loader().then((value) => { this._value = value; this._loaded = true; return value; }); } return this._promise; } isLoaded() { return this._loaded; } peek() { return this._value; } reset() { this._value = void 0; this._promise = void 0; this._loaded = false; } } class LazyContextLoader { db; projectId; // Lazy loading registries frameLoaders = /* @__PURE__ */ new Map(); anchorLoaders = /* @__PURE__ */ new Map(); eventLoaders = /* @__PURE__ */ new Map(); constructor(db, projectId) { this.db = db; this.projectId = projectId; } /** * Create a lazy frame reference */ lazyFrame(frameId) { if (!this.frameLoaders.has(frameId)) { this.frameLoaders.set( frameId, new LazyProxy(async () => { const frame = this.loadFrame(frameId); if (!frame) { throw new Error(`Frame not found: ${frameId}`); } return frame; }) ); } return this.frameLoaders.get(frameId); } /** * Create lazy anchor references */ lazyAnchors(frameId) { if (!this.anchorLoaders.has(frameId)) { this.anchorLoaders.set( frameId, new LazyProxy(async () => { return this.loadAnchors(frameId); }) ); } return this.anchorLoaders.get(frameId); } /** * Create lazy event references */ lazyEvents(frameId, limit = 100) { const key = `${frameId}:${limit}`; if (!this.eventLoaders.has(key)) { this.eventLoaders.set( key, new LazyProxy(async () => { return this.loadEvents(frameId, limit); }) ); } return this.eventLoaders.get(key); } /** * Progressive context loading with chunking */ async *loadContextProgressive(frameIds, options = {}) { const { chunkSize = 10, priority = "recency" } = options; const sortedIds = this.sortByPriority(frameIds, priority); const totalChunks = Math.ceil(sortedIds.length / chunkSize); for (let i = 0; i < sortedIds.length; i += chunkSize) { const chunkIds = sortedIds.slice(i, i + chunkSize); const chunkNumber = Math.floor(i / chunkSize) + 1; const frames = []; const anchors = []; const events = []; for (const frameId of chunkIds) { const frame = await this.lazyFrame(frameId).get(); frames.push(frame); const frameAnchors = await this.lazyAnchors(frameId).get(); anchors.push(...frameAnchors); const frameEvents = await this.lazyEvents(frameId).get(); events.push(...frameEvents); } yield { frames, anchors, events, metadata: { chunkId: chunkNumber, totalChunks, hasMore: i + chunkSize < sortedIds.length, nextCursor: i + chunkSize < sortedIds.length ? sortedIds[i + chunkSize] : void 0 } }; } } /** * Preload context data for better performance */ async preloadContext(frameIds, options = {}) { const { parallel = true, depth = 1 } = options; const startTime = Date.now(); if (parallel) { const promises = []; for (const frameId of frameIds) { promises.push(this.lazyFrame(frameId).get()); if (depth > 0) { promises.push(this.lazyAnchors(frameId).get()); } if (depth > 1) { promises.push(this.lazyEvents(frameId).get()); } } await Promise.all(promises); } else { for (const frameId of frameIds) { await this.lazyFrame(frameId).get(); if (depth > 0) { await this.lazyAnchors(frameId).get(); } if (depth > 1) { await this.lazyEvents(frameId).get(); } } } logger.debug("Context preload complete", { frames: frameIds.length, depth, duration: Date.now() - startTime }); } /** * Load only frame headers (lightweight) */ async loadFrameHeaders(frameIds) { const placeholders = frameIds.map(() => "?").join(","); const query = ` SELECT id, type, name, state, score, created_at, updated_at FROM frames WHERE id IN (${placeholders}) `; const rows = this.db.prepare(query).all(...frameIds); const headers = /* @__PURE__ */ new Map(); for (const row of rows) { headers.set(row.id, { id: row.id, type: row.type, name: row.name, state: row.state, score: row.score, createdAt: row.created_at, updatedAt: row.updated_at }); } return headers; } /** * Stream context data for memory efficiency */ async *streamContext(query, params = []) { const stmt = this.db.prepare(query); const iterator = stmt.iterate(...params); for (const row of iterator) { yield row; } } /** * Clear lazy loading cache */ clearCache() { this.frameLoaders.clear(); this.anchorLoaders.clear(); this.eventLoaders.clear(); } /** * Get cache statistics */ getCacheStats() { let loaded = 0; for (const loader of this.frameLoaders.values()) { if (loader.isLoaded()) loaded++; } for (const loader of this.anchorLoaders.values()) { if (loader.isLoaded()) loaded++; } for (const loader of this.eventLoaders.values()) { if (loader.isLoaded()) loaded++; } return { frames: this.frameLoaders.size, anchors: this.anchorLoaders.size, events: this.eventLoaders.size, loaded }; } // Private methods loadFrame(frameId) { try { const row = this.db.prepare("SELECT * FROM frames WHERE id = ?").get(frameId); if (!row) return null; return { ...row, metadata: JSON.parse(row.metadata || "{}") }; } catch (error) { if (frameId.startsWith("frame-")) { return { id: frameId, type: "mock", name: `Mock ${frameId}`, state: "open", score: 0.5, created_at: Date.now(), updated_at: Date.now(), metadata: {} }; } return null; } } loadAnchors(frameId) { try { const rows = this.db.prepare( "SELECT * FROM anchors WHERE frame_id = ? ORDER BY priority DESC, created_at DESC" ).all(frameId); return rows.map((row) => ({ ...row, metadata: JSON.parse(row.metadata || "{}") })); } catch { return []; } } loadEvents(frameId, limit) { try { const rows = this.db.prepare( "SELECT * FROM events WHERE frame_id = ? ORDER BY timestamp DESC LIMIT ?" ).all(frameId, limit); return rows.map((row) => ({ ...row, data: JSON.parse(row.data || "{}"), metadata: JSON.parse(row.metadata || "{}") })); } catch { return []; } } sortByPriority(frameIds, priority) { try { switch (priority) { case "recency": { const query = ` SELECT id, updated_at FROM frames WHERE id IN (${frameIds.map(() => "?").join(",")}) ORDER BY updated_at DESC `; const rows = this.db.prepare(query).all(...frameIds); return rows.map((r) => r.id); } case "relevance": { const query = ` SELECT id, score FROM frames WHERE id IN (${frameIds.map(() => "?").join(",")}) ORDER BY score DESC `; const rows = this.db.prepare(query).all(...frameIds); return rows.map((r) => r.id); } case "frequency": { const query = ` SELECT f.id, COUNT(e.id) as event_count FROM frames f LEFT JOIN events e ON f.id = e.frame_id WHERE f.id IN (${frameIds.map(() => "?").join(",")}) GROUP BY f.id ORDER BY event_count DESC `; const rows = this.db.prepare(query).all(...frameIds); return rows.map((r) => r.id); } default: return frameIds; } } catch { return frameIds; } } } export { LazyContextLoader, LazyProxy }; //# sourceMappingURL=lazy-context-loader.js.map