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.

346 lines (345 loc) 10.5 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { getQueryCache, createCacheKey } from "../database/query-cache.js"; import { logger } from "../monitoring/logger.js"; class OptimizedContextAssembler { db; cache = getQueryCache(); preparedStatements = /* @__PURE__ */ new Map(); constructor(db) { this.db = db; this.initializePreparedStatements(); } /** * Get hot stack context with optimizations */ async getHotStackContext(activeStack, options = {}) { const startTime = performance.now(); const stats = { cacheHits: 0, dbQueries: 0, totalRows: 0 }; const { maxEvents = 20, includeClosed = false, enableCaching = true, batchSize = 10 } = options; try { const contexts = []; for (let i = 0; i < activeStack.length; i += batchSize) { const batch = activeStack.slice(i, i + batchSize); const batchContexts = await this.processBatch( batch, maxEvents, includeClosed, enableCaching, stats ); contexts.push(...batchContexts); } const assemblyTimeMs = performance.now() - startTime; return contexts.map((context) => ({ ...context, performance: { assemblyTimeMs: assemblyTimeMs / contexts.length, ...stats } })); } catch (error) { logger.error("Failed to assemble hot stack context", error, { activeStack, options }); throw error; } } /** * Get single frame context with full optimization */ async getFrameContext(frameId, options = {}) { const startTime = performance.now(); const stats = { cacheHits: 0, dbQueries: 0, totalRows: 0 }; const { maxEvents = 50, enableCaching = true } = options; const cacheKey = createCacheKey("frame_context", [frameId, maxEvents]); if (enableCaching) { const cached = this.cache.getFrameContext(cacheKey); if (cached) { stats.cacheHits++; return { ...cached, performance: { assemblyTimeMs: performance.now() - startTime, ...stats } }; } } try { const context = await this.assembleFrameContext( frameId, maxEvents, stats ); if (!context) return null; if (enableCaching) { this.cache.cacheFrameContext(cacheKey, context); } const result = { ...context, performance: { assemblyTimeMs: performance.now() - startTime, ...stats } }; return result; } catch (error) { logger.error("Failed to get frame context", error, { frameId }); throw error; } } /** * Process a batch of frames efficiently */ async processBatch(frameIds, maxEvents, includeClosed, enableCaching, stats) { const contexts = []; const uncachedIds = []; for (const frameId of frameIds) { const cacheKey = createCacheKey("frame_context", [frameId, maxEvents]); if (enableCaching) { const cached = this.cache.getFrameContext(cacheKey); if (cached) { stats.cacheHits++; contexts.push(cached); continue; } } uncachedIds.push(frameId); } if (uncachedIds.length === 0) { return contexts; } const frames = await this.batchGetFrames(uncachedIds, stats); const allEvents = await this.batchGetEvents(uncachedIds, maxEvents, stats); const allAnchors = await this.batchGetAnchors(uncachedIds, stats); const allArtifacts = await this.batchGetArtifacts(uncachedIds, stats); for (const frameId of uncachedIds) { const frame = frames.get(frameId); if (!frame || !includeClosed && frame.state === "closed") { continue; } const context = { frameId, header: { goal: frame.name, constraints: this.extractConstraints(frame.inputs), definitions: frame.inputs.definitions }, anchors: allAnchors.get(frameId) || [], recentEvents: allEvents.get(frameId) || [], activeArtifacts: allArtifacts.get(frameId) || [] }; if (enableCaching) { const cacheKey = createCacheKey("frame_context", [frameId, maxEvents]); this.cache.cacheFrameContext(cacheKey, context); } contexts.push(context); } return contexts; } /** * Batch get frames with single query */ async batchGetFrames(frameIds, stats) { if (frameIds.length === 0) return /* @__PURE__ */ new Map(); const stmt = this.preparedStatements.get("batch_frames"); if (!stmt) throw new Error("Prepared statement not found: batch_frames"); const placeholders = frameIds.map(() => "?").join(","); const query = `SELECT * FROM frames WHERE frame_id IN (${placeholders})`; stats.dbQueries++; const rows = this.db.prepare(query).all(...frameIds); stats.totalRows += rows.length; const frameMap = /* @__PURE__ */ new Map(); for (const row of rows) { frameMap.set(row.frame_id, { ...row, inputs: JSON.parse(row.inputs || "{}"), outputs: JSON.parse(row.outputs || "{}"), digest_json: JSON.parse(row.digest_json || "{}") }); } return frameMap; } /** * Batch get events for multiple frames */ async batchGetEvents(frameIds, maxEvents, stats) { if (frameIds.length === 0) return /* @__PURE__ */ new Map(); const placeholders = frameIds.map(() => "?").join(","); const query = ` SELECT *, ROW_NUMBER() OVER (PARTITION BY frame_id ORDER BY seq DESC) as rn FROM events WHERE frame_id IN (${placeholders}) AND rn <= ${maxEvents} ORDER BY frame_id, seq DESC `; stats.dbQueries++; const rows = this.db.prepare(query).all(...frameIds); stats.totalRows += rows.length; const eventMap = /* @__PURE__ */ new Map(); for (const row of rows) { if (!eventMap.has(row.frame_id)) { eventMap.set(row.frame_id, []); } eventMap.get(row.frame_id).push({ ...row, payload: JSON.parse(row.payload) }); } return eventMap; } /** * Batch get anchors for multiple frames */ async batchGetAnchors(frameIds, stats) { if (frameIds.length === 0) return /* @__PURE__ */ new Map(); const placeholders = frameIds.map(() => "?").join(","); const query = ` SELECT * FROM anchors WHERE frame_id IN (${placeholders}) ORDER BY frame_id, priority DESC, created_at ASC `; stats.dbQueries++; const rows = this.db.prepare(query).all(...frameIds); stats.totalRows += rows.length; const anchorMap = /* @__PURE__ */ new Map(); for (const row of rows) { if (!anchorMap.has(row.frame_id)) { anchorMap.set(row.frame_id, []); } anchorMap.get(row.frame_id).push({ ...row, metadata: JSON.parse(row.metadata || "{}") }); } return anchorMap; } /** * Batch get active artifacts for multiple frames */ async batchGetArtifacts(frameIds, stats) { if (frameIds.length === 0) return /* @__PURE__ */ new Map(); const placeholders = frameIds.map(() => "?").join(","); const query = ` SELECT frame_id, payload FROM events WHERE frame_id IN (${placeholders}) AND event_type = 'artifact' ORDER BY frame_id, ts DESC `; stats.dbQueries++; const rows = this.db.prepare(query).all(...frameIds); stats.totalRows += rows.length; const artifactMap = /* @__PURE__ */ new Map(); for (const row of rows) { const payload = JSON.parse(row.payload); if (!artifactMap.has(row.frame_id)) { artifactMap.set(row.frame_id, []); } if (payload.path) { artifactMap.get(row.frame_id).push(payload.path); } } return artifactMap; } /** * Assemble single frame context */ async assembleFrameContext(frameId, maxEvents, stats) { const frame = await this.batchGetFrames([frameId], stats).then( (map) => map.get(frameId) ); if (!frame) return null; const [events, anchors, artifacts] = await Promise.all([ this.batchGetEvents([frameId], maxEvents, stats).then( (map) => map.get(frameId) || [] ), this.batchGetAnchors([frameId], stats).then( (map) => map.get(frameId) || [] ), this.batchGetArtifacts([frameId], stats).then( (map) => map.get(frameId) || [] ) ]); return { frameId, header: { goal: frame.name, constraints: this.extractConstraints(frame.inputs), definitions: frame.inputs.definitions }, anchors, recentEvents: events, activeArtifacts: artifacts }; } /** * Extract constraints from frame inputs */ extractConstraints(inputs) { const constraints = []; if (inputs.constraints && Array.isArray(inputs.constraints)) { constraints.push(...inputs.constraints); } if (inputs.requirements && Array.isArray(inputs.requirements)) { constraints.push(...inputs.requirements); } if (inputs.limitations && Array.isArray(inputs.limitations)) { constraints.push(...inputs.limitations); } return constraints; } /** * Initialize prepared statements for common queries */ initializePreparedStatements() { try { this.preparedStatements.set( "single_frame", this.db.prepare("SELECT * FROM frames WHERE frame_id = ?") ); this.preparedStatements.set( "frame_events", this.db.prepare( "SELECT * FROM events WHERE frame_id = ? ORDER BY seq DESC LIMIT ?" ) ); this.preparedStatements.set( "frame_anchors", this.db.prepare( "SELECT * FROM anchors WHERE frame_id = ? ORDER BY priority DESC, created_at ASC" ) ); logger.info( "Prepared statements initialized for optimized context assembly" ); } catch (error) { logger.error("Failed to initialize prepared statements", error); throw error; } } /** * Clear cache and reset prepared statements */ cleanup() { this.cache.clear(); this.preparedStatements.clear(); } } export { OptimizedContextAssembler }; //# sourceMappingURL=optimized-frame-context.js.map