@stackmemoryai/stackmemory 0.3.7 → 0.3.8

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 (190) hide show
  1. package/dist/agents/core/agent-task-manager.js +5 -5
  2. package/dist/agents/core/agent-task-manager.js.map +2 -2
  3. package/dist/agents/verifiers/base-verifier.js +2 -2
  4. package/dist/agents/verifiers/base-verifier.js.map +2 -2
  5. package/dist/cli/claude-sm.js +0 -11
  6. package/dist/cli/claude-sm.js.map +2 -2
  7. package/dist/cli/codex-sm.js +0 -11
  8. package/dist/cli/codex-sm.js.map +2 -2
  9. package/dist/cli/commands/chromadb.js +64 -34
  10. package/dist/cli/commands/chromadb.js.map +2 -2
  11. package/dist/cli/commands/clear.js +9 -13
  12. package/dist/cli/commands/clear.js.map +2 -2
  13. package/dist/cli/commands/config.js +43 -33
  14. package/dist/cli/commands/config.js.map +2 -2
  15. package/dist/cli/commands/context.js.map +2 -2
  16. package/dist/cli/commands/dashboard.js +41 -13
  17. package/dist/cli/commands/dashboard.js.map +2 -2
  18. package/dist/cli/commands/gc.js +69 -20
  19. package/dist/cli/commands/gc.js.map +2 -2
  20. package/dist/cli/commands/handoff.js.map +2 -2
  21. package/dist/cli/commands/infinite-storage.js +60 -19
  22. package/dist/cli/commands/infinite-storage.js.map +2 -2
  23. package/dist/cli/commands/linear-create.js +36 -8
  24. package/dist/cli/commands/linear-create.js.map +2 -2
  25. package/dist/cli/commands/linear-list.js +33 -10
  26. package/dist/cli/commands/linear-list.js.map +2 -2
  27. package/dist/cli/commands/linear-migrate.js +17 -4
  28. package/dist/cli/commands/linear-migrate.js.map +2 -2
  29. package/dist/cli/commands/linear-test.js +14 -6
  30. package/dist/cli/commands/linear-test.js.map +2 -2
  31. package/dist/cli/commands/linear-unified.js +123 -35
  32. package/dist/cli/commands/linear-unified.js.map +2 -2
  33. package/dist/cli/commands/linear.js.map +2 -2
  34. package/dist/cli/commands/monitor.js.map +2 -2
  35. package/dist/cli/commands/onboard.js +35 -8
  36. package/dist/cli/commands/onboard.js.map +2 -2
  37. package/dist/cli/commands/quality.js +2 -7
  38. package/dist/cli/commands/quality.js.map +2 -2
  39. package/dist/cli/commands/session.js +23 -6
  40. package/dist/cli/commands/session.js.map +2 -2
  41. package/dist/cli/commands/skills.js +72 -27
  42. package/dist/cli/commands/skills.js.map +2 -2
  43. package/dist/cli/commands/storage.js +108 -38
  44. package/dist/cli/commands/storage.js.map +2 -2
  45. package/dist/cli/commands/tui.js.map +2 -2
  46. package/dist/cli/commands/webhook.js +57 -18
  47. package/dist/cli/commands/webhook.js.map +2 -2
  48. package/dist/cli/commands/workflow.js +8 -15
  49. package/dist/cli/commands/workflow.js.map +2 -2
  50. package/dist/cli/commands/worktree.js +34 -13
  51. package/dist/cli/commands/worktree.js.map +2 -2
  52. package/dist/cli/index.js +0 -11
  53. package/dist/cli/index.js.map +2 -2
  54. package/dist/core/config/types.js.map +1 -1
  55. package/dist/core/context/auto-context.js +10 -6
  56. package/dist/core/context/auto-context.js.map +2 -2
  57. package/dist/core/context/context-bridge.js.map +2 -2
  58. package/dist/core/context/frame-database.js +13 -3
  59. package/dist/core/context/frame-database.js.map +2 -2
  60. package/dist/core/context/frame-digest.js +7 -5
  61. package/dist/core/context/frame-digest.js.map +2 -2
  62. package/dist/core/context/frame-manager.js.map +2 -2
  63. package/dist/core/context/frame-stack.js +16 -5
  64. package/dist/core/context/frame-stack.js.map +2 -2
  65. package/dist/core/context/incremental-gc.js +10 -3
  66. package/dist/core/context/incremental-gc.js.map +2 -2
  67. package/dist/core/context/index.js.map +1 -1
  68. package/dist/core/context/permission-manager.js.map +2 -2
  69. package/dist/core/context/refactored-frame-manager.js +12 -3
  70. package/dist/core/context/refactored-frame-manager.js.map +2 -2
  71. package/dist/core/context/shared-context-layer.js +4 -2
  72. package/dist/core/context/shared-context-layer.js.map +2 -2
  73. package/dist/core/database/batch-operations.js +112 -86
  74. package/dist/core/database/batch-operations.js.map +2 -2
  75. package/dist/core/database/query-cache.js +19 -9
  76. package/dist/core/database/query-cache.js.map +2 -2
  77. package/dist/core/database/sqlite-adapter.js +1 -1
  78. package/dist/core/database/sqlite-adapter.js.map +2 -2
  79. package/dist/core/digest/enhanced-hybrid-digest.js +8 -2
  80. package/dist/core/digest/enhanced-hybrid-digest.js.map +2 -2
  81. package/dist/core/errors/recovery.js +9 -2
  82. package/dist/core/errors/recovery.js.map +2 -2
  83. package/dist/core/frame/workflow-templates-stub.js.map +1 -1
  84. package/dist/core/frame/workflow-templates.js +40 -1
  85. package/dist/core/frame/workflow-templates.js.map +2 -2
  86. package/dist/core/monitoring/logger.js +6 -1
  87. package/dist/core/monitoring/logger.js.map +2 -2
  88. package/dist/core/monitoring/metrics.js.map +2 -2
  89. package/dist/core/monitoring/progress-tracker.js.map +2 -2
  90. package/dist/core/performance/context-cache.js.map +2 -2
  91. package/dist/core/performance/lazy-context-loader.js +24 -20
  92. package/dist/core/performance/lazy-context-loader.js.map +2 -2
  93. package/dist/core/performance/optimized-frame-context.js +27 -12
  94. package/dist/core/performance/optimized-frame-context.js.map +2 -2
  95. package/dist/core/performance/performance-benchmark.js +10 -6
  96. package/dist/core/performance/performance-benchmark.js.map +2 -2
  97. package/dist/core/performance/performance-profiler.js +51 -14
  98. package/dist/core/performance/performance-profiler.js.map +2 -2
  99. package/dist/core/performance/streaming-jsonl-parser.js +5 -1
  100. package/dist/core/performance/streaming-jsonl-parser.js.map +2 -2
  101. package/dist/core/projects/project-manager.js +14 -20
  102. package/dist/core/projects/project-manager.js.map +2 -2
  103. package/dist/core/retrieval/context-retriever.js.map +1 -1
  104. package/dist/core/retrieval/llm-context-retrieval.js.map +2 -2
  105. package/dist/core/session/clear-survival-stub.js +5 -1
  106. package/dist/core/session/clear-survival-stub.js.map +2 -2
  107. package/dist/core/session/clear-survival.js +35 -0
  108. package/dist/core/session/clear-survival.js.map +2 -2
  109. package/dist/core/session/index.js.map +1 -1
  110. package/dist/core/session/session-manager.js.map +2 -2
  111. package/dist/core/storage/chromadb-adapter.js +6 -2
  112. package/dist/core/storage/chromadb-adapter.js.map +2 -2
  113. package/dist/core/storage/chromadb-simple.js +17 -5
  114. package/dist/core/storage/chromadb-simple.js.map +2 -2
  115. package/dist/core/storage/infinite-storage.js +109 -46
  116. package/dist/core/storage/infinite-storage.js.map +2 -2
  117. package/dist/core/storage/railway-optimized-storage.js +48 -22
  118. package/dist/core/storage/railway-optimized-storage.js.map +2 -2
  119. package/dist/core/storage/remote-storage.js +41 -23
  120. package/dist/core/storage/remote-storage.js.map +2 -2
  121. package/dist/core/trace/cli-trace-wrapper.js +9 -2
  122. package/dist/core/trace/cli-trace-wrapper.js.map +2 -2
  123. package/dist/core/trace/db-trace-wrapper.js +96 -68
  124. package/dist/core/trace/db-trace-wrapper.js.map +2 -2
  125. package/dist/core/trace/debug-trace.js +25 -8
  126. package/dist/core/trace/debug-trace.js.map +2 -2
  127. package/dist/core/trace/index.js +6 -2
  128. package/dist/core/trace/index.js.map +2 -2
  129. package/dist/core/trace/linear-api-wrapper.js +10 -5
  130. package/dist/core/trace/linear-api-wrapper.js.map +2 -2
  131. package/dist/core/trace/trace-demo.js +14 -10
  132. package/dist/core/trace/trace-demo.js.map +2 -2
  133. package/dist/core/trace/trace-detector.js +9 -2
  134. package/dist/core/trace/trace-detector.js.map +2 -2
  135. package/dist/core/trace/types.js.map +1 -1
  136. package/dist/core/utils/compression.js.map +1 -1
  137. package/dist/core/utils/update-checker.js.map +1 -1
  138. package/dist/core/worktree/worktree-manager.js +18 -7
  139. package/dist/core/worktree/worktree-manager.js.map +2 -2
  140. package/dist/features/analytics/core/analytics-service.js.map +2 -2
  141. package/dist/features/analytics/queries/metrics-queries.js +1 -1
  142. package/dist/features/analytics/queries/metrics-queries.js.map +2 -2
  143. package/dist/features/tasks/pebbles-task-store.js.map +1 -1
  144. package/dist/features/tui/components/analytics-panel.js +36 -15
  145. package/dist/features/tui/components/analytics-panel.js.map +2 -2
  146. package/dist/features/tui/components/pr-tracker.js +19 -7
  147. package/dist/features/tui/components/pr-tracker.js.map +2 -2
  148. package/dist/features/tui/components/session-monitor.js +22 -9
  149. package/dist/features/tui/components/session-monitor.js.map +2 -2
  150. package/dist/features/tui/components/subagent-fleet.js +20 -13
  151. package/dist/features/tui/components/subagent-fleet.js.map +2 -2
  152. package/dist/features/tui/components/task-board.js +26 -10
  153. package/dist/features/tui/components/task-board.js.map +2 -2
  154. package/dist/features/tui/index.js.map +2 -2
  155. package/dist/features/tui/services/data-service.js +6 -2
  156. package/dist/features/tui/services/data-service.js.map +2 -2
  157. package/dist/features/tui/services/linear-task-reader.js +3 -1
  158. package/dist/features/tui/services/linear-task-reader.js.map +2 -2
  159. package/dist/features/tui/services/websocket-client.js +3 -1
  160. package/dist/features/tui/services/websocket-client.js.map +2 -2
  161. package/dist/features/tui/terminal-compat.js +6 -2
  162. package/dist/features/tui/terminal-compat.js.map +2 -2
  163. package/dist/features/web/client/stores/task-store.js.map +2 -2
  164. package/dist/features/web/server/index.js +18 -10
  165. package/dist/features/web/server/index.js.map +2 -2
  166. package/dist/integrations/linear/sync-service.js +12 -13
  167. package/dist/integrations/linear/sync-service.js.map +2 -2
  168. package/dist/integrations/linear/sync.js +174 -12
  169. package/dist/integrations/linear/sync.js.map +2 -2
  170. package/dist/integrations/linear/unified-sync.js +1 -1
  171. package/dist/integrations/linear/unified-sync.js.map +1 -1
  172. package/dist/integrations/linear/webhook-server.js +15 -16
  173. package/dist/integrations/linear/webhook-server.js.map +2 -2
  174. package/dist/mcp/stackmemory-mcp-server.js +0 -11
  175. package/dist/mcp/stackmemory-mcp-server.js.map +2 -2
  176. package/dist/servers/production/auth-middleware.js.map +2 -2
  177. package/dist/servers/railway/index.js.map +2 -2
  178. package/dist/services/config-service.js +6 -7
  179. package/dist/services/config-service.js.map +2 -2
  180. package/dist/services/context-service.js +11 -12
  181. package/dist/services/context-service.js.map +2 -2
  182. package/dist/skills/claude-skills.js +4 -2
  183. package/dist/skills/claude-skills.js.map +2 -2
  184. package/dist/skills/dashboard-launcher.js.map +2 -2
  185. package/dist/skills/repo-ingestion-skill.js.map +2 -2
  186. package/dist/utils/env.js +46 -0
  187. package/dist/utils/env.js.map +7 -0
  188. package/dist/utils/logger.js +0 -11
  189. package/dist/utils/logger.js.map +2 -2
  190. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/context/shared-context-layer.ts"],
4
- "sourcesContent": ["/**\n * Shared Context Layer for Cross-Session Reference\n *\n * This layer maintains a lightweight shared context across sessions while\n * preserving run_id isolation for write operations. It enables:\n * - Read access to frames from other sessions\n * - Automatic context inheritance\n * - Efficient caching and indexing\n * - Safe concurrent access\n */\n\nimport { v4 as uuidv4 } from 'uuid';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { logger } from '../monitoring/logger.js';\nimport { sessionManager } from '../session/session-manager.js';\nimport type { Frame } from '../frame-manager/frame-manager.js';\n// Type-safe environment variable access\nfunction getEnv(key: string, defaultValue?: string): string {\n const value = process.env[key];\n if (value === undefined) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Environment variable ${key} is required`);\n }\n return value;\n}\n\nfunction getOptionalEnv(key: string): string | undefined {\n return process.env[key];\n}\n\n\nexport interface SharedContext {\n projectId: string;\n branch?: string;\n lastUpdated: number;\n sessions: SharedSessionContext[];\n globalPatterns: ContextPattern[];\n decisionLog: Decision[];\n referenceIndex: ReferenceIndex;\n}\n\nexport interface SharedSessionContext {\n sessionId: string;\n runId: string;\n summary: string;\n keyFrames: FrameSummary[];\n createdAt: number;\n lastActiveAt: number;\n metadata: Record<string, any>;\n}\n\nexport interface FrameSummary {\n frameId: string;\n title: string;\n type: string;\n score: number;\n tags: string[];\n summary?: string;\n createdAt: number;\n}\n\nexport interface ContextPattern {\n pattern: string;\n type: 'error' | 'success' | 'decision' | 'learning';\n frequency: number;\n lastSeen: number;\n resolution?: string;\n}\n\nexport interface Decision {\n id: string;\n decision: string;\n reasoning: string;\n timestamp: number;\n sessionId: string;\n outcome?: 'success' | 'failure' | 'pending';\n}\n\nexport interface ReferenceIndex {\n byTag: Map<string, string[]>;\n byType: Map<string, string[]>;\n byScore: string[];\n recentlyAccessed: string[];\n}\n\nexport class SharedContextLayer {\n private static instance: SharedContextLayer;\n private contextDir: string;\n private cache: Map<string, SharedContext> = new Map();\n private readonly MAX_CACHE_SIZE = 100;\n private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private lastCacheClean = Date.now();\n\n private constructor() {\n const homeDir = process.env['HOME'] || process.env['USERPROFILE'] || '';\n this.contextDir = path.join(homeDir, '.stackmemory', 'shared-context');\n }\n\n static getInstance(): SharedContextLayer {\n if (!SharedContextLayer.instance) {\n SharedContextLayer.instance = new SharedContextLayer();\n }\n return SharedContextLayer.instance;\n }\n\n async initialize(): Promise<void> {\n await fs.mkdir(this.contextDir, { recursive: true });\n await fs.mkdir(path.join(this.contextDir, 'projects'), { recursive: true });\n await fs.mkdir(path.join(this.contextDir, 'patterns'), { recursive: true });\n await fs.mkdir(path.join(this.contextDir, 'decisions'), {\n recursive: true,\n });\n }\n\n /**\n * Get or create shared context for current project/branch\n */\n async getSharedContext(options?: {\n projectId?: string;\n branch?: string;\n includeOtherBranches?: boolean;\n }): Promise<SharedContext> {\n const session = sessionManager.getCurrentSession();\n const projectId = options?.projectId || session?.projectId || 'global';\n const branch = options?.branch || session?.branch;\n\n const cacheKey = `${projectId}:${branch || 'main'}`;\n\n // Check cache first\n if (this.cache.has(cacheKey)) {\n const cached = this.cache.get(cacheKey)!;\n if (Date.now() - cached.lastUpdated < this.CACHE_TTL) {\n return cached;\n }\n }\n\n // Load from disk\n const context = await this.loadProjectContext(projectId, branch);\n\n // Include other branches if requested\n if (options?.includeOtherBranches) {\n const otherBranches = await this.loadOtherBranchContexts(\n projectId,\n branch\n );\n context.sessions.push(...otherBranches);\n }\n\n // Update cache\n this.cache.set(cacheKey, context);\n this.cleanCache();\n\n return context;\n }\n\n /**\n * Add current session's important frames to shared context\n */\n async addToSharedContext(\n frames: Frame[],\n options?: {\n minScore?: number;\n tags?: string[];\n }\n ): Promise<void> {\n const session = sessionManager.getCurrentSession();\n if (!session) return;\n\n const context = await this.getSharedContext();\n const minScore = options?.minScore || 0.7;\n\n // Filter important frames\n const importantFrames = frames.filter((f) => {\n const score = this.calculateFrameScore(f);\n return score >= minScore;\n });\n\n // Create session context\n const sessionContext: SharedSessionContext = {\n sessionId: session.sessionId,\n runId: session.runId,\n summary: this.generateSessionSummary(importantFrames),\n keyFrames: importantFrames.map((f) => this.summarizeFrame(f)),\n createdAt: session.startedAt,\n lastActiveAt: Date.now(),\n metadata: session.metadata,\n };\n\n // Update or add session context\n const existingIndex = context.sessions.findIndex(\n (s) => s.sessionId === session.sessionId\n );\n if (existingIndex >= 0) {\n context.sessions[existingIndex] = sessionContext;\n } else {\n context.sessions.push(sessionContext);\n }\n\n // Update patterns\n this.updatePatterns(context, importantFrames);\n\n // Update reference index\n this.updateReferenceIndex(context, importantFrames);\n\n // Save context\n await this.saveProjectContext(context);\n }\n\n /**\n * Query shared context for relevant frames\n */\n async querySharedContext(query: {\n tags?: string[];\n type?: string;\n minScore?: number;\n sessionId?: string;\n limit?: number;\n }): Promise<FrameSummary[]> {\n const context = await this.getSharedContext({ includeOtherBranches: true });\n let results: FrameSummary[] = [];\n\n // Collect all frames from all sessions\n for (const session of context.sessions) {\n if (query.sessionId && session.sessionId !== query.sessionId) continue;\n \n // Skip sessions without keyFrames\n if (!session.keyFrames || !Array.isArray(session.keyFrames)) continue;\n\n const filtered = session.keyFrames.filter((f) => {\n if (query.tags && !query.tags.some((tag) => f.tags.includes(tag)))\n return false;\n if (query.type && f.type !== query.type) return false;\n if (query.minScore && f.score < query.minScore) return false;\n return true;\n });\n\n results.push(...filtered);\n }\n\n // Sort by score and recency\n results.sort((a, b) => {\n const scoreWeight = 0.7;\n const recencyWeight = 0.3;\n\n const aScore =\n a.score * scoreWeight +\n (1 - (Date.now() - a.createdAt) / (30 * 24 * 60 * 60 * 1000)) *\n recencyWeight;\n const bScore =\n b.score * scoreWeight +\n (1 - (Date.now() - b.createdAt) / (30 * 24 * 60 * 60 * 1000)) *\n recencyWeight;\n\n return bScore - aScore;\n });\n\n // Apply limit\n if (query.limit) {\n results = results.slice(0, query.limit);\n }\n\n // Update recently accessed\n const index = context.referenceIndex;\n if (!index.recentlyAccessed) {\n index.recentlyAccessed = [];\n }\n \n // Add frameIds to recently accessed, removing duplicates\n if (results.length > 0) {\n const frameIds = results.map((r) => r.frameId);\n index.recentlyAccessed = [\n ...frameIds,\n ...index.recentlyAccessed.filter((id: any) => !frameIds.includes(id))\n ].slice(0, 100);\n \n // Save the updated context with recently accessed frames\n await this.saveProjectContext(context);\n }\n\n return results;\n }\n\n /**\n * Get relevant patterns from shared context\n */\n async getPatterns(type?: ContextPattern['type']): Promise<ContextPattern[]> {\n const context = await this.getSharedContext();\n\n if (type) {\n return context.globalPatterns.filter((p) => p.type === type);\n }\n\n return context.globalPatterns;\n }\n\n /**\n * Add a decision to the shared context\n */\n async addDecision(\n decision: Omit<Decision, 'id' | 'timestamp' | 'sessionId'>\n ): Promise<void> {\n const session = sessionManager.getCurrentSession();\n if (!session) return;\n\n const context = await this.getSharedContext();\n\n const newDecision: Decision = {\n id: uuidv4(),\n timestamp: Date.now(),\n sessionId: session.sessionId,\n outcome: 'pending',\n ...decision,\n };\n\n context.decisionLog.push(newDecision);\n\n // Keep only last 100 decisions\n if (context.decisionLog.length > 100) {\n context.decisionLog = context.decisionLog.slice(-100);\n }\n\n await this.saveProjectContext(context);\n }\n\n /**\n * Get recent decisions from shared context\n */\n async getDecisions(limit: number = 10): Promise<Decision[]> {\n const context = await this.getSharedContext();\n return context.decisionLog.slice(-limit);\n }\n\n /**\n * Automatic context discovery on CLI startup\n */\n async autoDiscoverContext(): Promise<{\n hasSharedContext: boolean;\n sessionCount: number;\n recentPatterns: ContextPattern[];\n lastDecisions: Decision[];\n suggestedFrames: FrameSummary[];\n }> {\n const context = await this.getSharedContext({\n includeOtherBranches: false,\n });\n\n // Get recent patterns (last 7 days)\n const recentPatterns = context.globalPatterns\n .filter((p) => Date.now() - p.lastSeen < 7 * 24 * 60 * 60 * 1000)\n .sort((a, b) => b.frequency - a.frequency)\n .slice(0, 5);\n\n // Get last 5 decisions\n const lastDecisions = context.decisionLog.slice(-5);\n\n // Get suggested frames based on recent access and score\n const suggestedFrames = await this.querySharedContext({\n minScore: 0.8,\n limit: 5,\n });\n\n return {\n hasSharedContext: context.sessions.length > 0,\n sessionCount: context.sessions.length,\n recentPatterns,\n lastDecisions,\n suggestedFrames,\n };\n }\n\n private async loadProjectContext(\n projectId: string,\n branch?: string\n ): Promise<SharedContext> {\n const contextFile = path.join(\n this.contextDir,\n 'projects',\n `${projectId}_${branch || 'main'}.json`\n );\n\n try {\n const data = await fs.readFile(contextFile, 'utf-8');\n const context = JSON.parse(data);\n\n // Reconstruct Maps\n context.referenceIndex.byTag = new Map(\n Object.entries(context.referenceIndex.byTag || {})\n );\n context.referenceIndex.byType = new Map(\n Object.entries(context.referenceIndex.byType || {})\n );\n\n return context;\n } catch {\n // Return empty context if file doesn't exist\n return {\n projectId,\n branch,\n lastUpdated: Date.now(),\n sessions: [],\n globalPatterns: [],\n decisionLog: [],\n referenceIndex: {\n byTag: new Map(),\n byType: new Map(),\n byScore: [],\n recentlyAccessed: [],\n },\n };\n }\n }\n\n private async saveProjectContext(context: SharedContext): Promise<void> {\n const contextFile = path.join(\n this.contextDir,\n 'projects',\n `${context.projectId}_${context.branch || 'main'}.json`\n );\n\n // Convert Maps to objects for JSON serialization\n const serializable = {\n ...context,\n lastUpdated: Date.now(),\n referenceIndex: {\n ...context.referenceIndex,\n byTag: Object.fromEntries(context.referenceIndex.byTag),\n byType: Object.fromEntries(context.referenceIndex.byType),\n },\n };\n\n await fs.writeFile(contextFile, JSON.stringify(serializable, null, 2));\n }\n\n private async loadOtherBranchContexts(\n projectId: string,\n currentBranch?: string\n ): Promise<SharedSessionContext[]> {\n const projectsDir = path.join(this.contextDir, 'projects');\n const files = await fs.readdir(projectsDir);\n const sessions: SharedSessionContext[] = [];\n\n for (const file of files) {\n if (\n file.startsWith(`${projectId}_`) &&\n !file.includes(currentBranch || 'main')\n ) {\n try {\n const data = await fs.readFile(path.join(projectsDir, file), 'utf-8');\n const context = JSON.parse(data);\n sessions.push(...context.sessions);\n } catch {\n // Skip invalid files\n }\n }\n }\n\n return sessions;\n }\n\n private calculateFrameScore(frame: Frame): number {\n // Simple scoring algorithm\n let score = 0.5;\n\n // Boost for certain types\n if (frame.type === 'task' || frame.type === 'review') score += 0.2;\n if (frame.type === 'debug' || frame.type === 'write') score += 0.15;\n if (frame.type === 'error') score += 0.15; // Error frames are important for pattern extraction\n \n // Check for data property (used in tests)\n const frameWithData = frame as any;\n if (frameWithData.data) score += 0.2;\n\n // Boost for having outputs (indicates completion/results)\n if (frame.outputs && Object.keys(frame.outputs).length > 0) score += 0.2;\n if (frame.digest_text || (frame.digest_json && Object.keys(frame.digest_json).length > 0)) score += 0.1;\n\n // Time decay (reduce score for older frames) - but handle missing created_at\n if (frame.created_at) {\n const age = Date.now() - frame.created_at;\n const daysSinceCreation = age / (24 * 60 * 60 * 1000);\n score *= Math.max(0.3, 1 - daysSinceCreation / 30);\n }\n\n return Math.min(1, score);\n }\n\n private summarizeFrame(frame: Frame): FrameSummary {\n return {\n frameId: frame.frame_id,\n title: frame.name,\n type: frame.type,\n score: this.calculateFrameScore(frame),\n tags: [],\n summary: this.generateFrameSummary(frame),\n createdAt: frame.created_at,\n };\n }\n\n private generateFrameSummary(frame: Frame): string {\n // Generate a brief summary of the frame\n const parts = [];\n const frameWithData = frame as any;\n\n if (frame.type) parts.push(`[${frame.type}]`);\n if (frame.name) parts.push(frame.name);\n if (frameWithData.title) parts.push(frameWithData.title);\n if (frameWithData.data?.error) parts.push(`Error: ${frameWithData.data.error}`);\n if (frameWithData.data?.resolution)\n parts.push(`Resolution: ${frameWithData.data.resolution}`);\n\n return parts.join(' - ').slice(0, 200);\n }\n\n private generateSessionSummary(frames: Frame[]): string {\n const types = [...new Set(frames.map((f) => f.type))];\n return `Session with ${frames.length} key frames: ${types.join(', ')}`;\n }\n\n private updatePatterns(context: SharedContext, frames: Frame[]): void {\n for (const frame of frames) {\n // Extract patterns from frame data\n // Handle frames with a data property (used in tests)\n const frameWithData = frame as any;\n if (frameWithData.data?.error) {\n this.addPattern(\n context,\n frameWithData.data.error,\n 'error',\n frameWithData.data?.resolution\n );\n } else if (frame.type === 'error' && frame.name) {\n // Only extract from name/outputs if no data.error property\n const errorText = frame.outputs?.error || frame.name;\n const resolution = frame.outputs?.resolution;\n if (errorText) {\n this.addPattern(context, errorText, 'error', resolution);\n }\n }\n\n if (frame.type === 'decision' && frameWithData.data?.decision) {\n this.addPattern(context, frameWithData.data.decision, 'decision');\n } else if (frame.digest_json?.decision) {\n // Only extract from digest_json if no data.decision\n this.addPattern(context, frame.digest_json.decision, 'decision');\n }\n }\n }\n\n private addPattern(\n context: SharedContext,\n pattern: string,\n type: ContextPattern['type'],\n resolution?: string\n ): void {\n const existing = context.globalPatterns.find(\n (p) => p.pattern === pattern && p.type === type\n );\n\n if (existing) {\n existing.frequency++;\n existing.lastSeen = Date.now();\n if (resolution) existing.resolution = resolution;\n } else {\n context.globalPatterns.push({\n pattern,\n type,\n frequency: 1,\n lastSeen: Date.now(),\n resolution,\n });\n }\n\n // Keep only top 100 patterns\n if (context.globalPatterns.length > 100) {\n context.globalPatterns.sort((a, b) => b.frequency - a.frequency);\n context.globalPatterns = context.globalPatterns.slice(0, 100);\n }\n }\n\n private updateReferenceIndex(context: SharedContext, frames: Frame[]): void {\n for (const frame of frames) {\n const summary = this.summarizeFrame(frame);\n\n // Index by tags\n for (const tag of summary.tags) {\n if (!context.referenceIndex.byTag.has(tag)) {\n context.referenceIndex.byTag.set(tag, []);\n }\n context.referenceIndex.byTag.get(tag)!.push(frame.frameId);\n }\n\n // Index by type\n if (!context.referenceIndex.byType.has(frame.type)) {\n context.referenceIndex.byType.set(frame.type, []);\n }\n context.referenceIndex.byType.get(frame.type)!.push(frame.frameId);\n\n // Update score index\n const scoreIndex = context.referenceIndex.byScore;\n const insertIndex = scoreIndex.findIndex((id) => {\n const otherFrame = context.sessions\n .flatMap((s) => s.keyFrames)\n .find((f) => f.frameId === id);\n return otherFrame && otherFrame.score < summary.score;\n });\n\n if (insertIndex >= 0) {\n scoreIndex.splice(insertIndex, 0, frame.frameId);\n } else {\n scoreIndex.push(frame.frameId);\n }\n\n // Keep only top 1000 by score\n context.referenceIndex.byScore = scoreIndex.slice(0, 1000);\n }\n }\n\n private cleanCache(): void {\n if (Date.now() - this.lastCacheClean < 60000) return; // Clean every minute\n\n if (this.cache.size > this.MAX_CACHE_SIZE) {\n const entries = Array.from(this.cache.entries()).sort(\n (a, b) => b[1].lastUpdated - a[1].lastUpdated\n );\n\n this.cache = new Map(entries.slice(0, this.MAX_CACHE_SIZE / 2));\n }\n\n this.lastCacheClean = Date.now();\n }\n}\n\nexport const sharedContextLayer = SharedContextLayer.getInstance();\n\n// Export for testing\nexport {\n SharedContext,\n SharedSessionContext,\n FrameSummary,\n ContextPattern,\n Decision,\n ReferenceIndex,\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Shared Context Layer for Cross-Session Reference\n *\n * This layer maintains a lightweight shared context across sessions while\n * preserving run_id isolation for write operations. It enables:\n * - Read access to frames from other sessions\n * - Automatic context inheritance\n * - Efficient caching and indexing\n * - Safe concurrent access\n */\n\nimport { v4 as uuidv4 } from 'uuid';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport { logger } from '../monitoring/logger.js';\nimport { sessionManager } from '../session/session-manager.js';\nimport type { Frame } from '../frame-manager/frame-manager.js';\n// Type-safe environment variable access\nfunction getEnv(key: string, defaultValue?: string): string {\n const value = process.env[key];\n if (value === undefined) {\n if (defaultValue !== undefined) return defaultValue;\n throw new Error(`Environment variable ${key} is required`);\n }\n return value;\n}\n\nfunction getOptionalEnv(key: string): string | undefined {\n return process.env[key];\n}\n\nexport interface SharedContext {\n projectId: string;\n branch?: string;\n lastUpdated: number;\n sessions: SharedSessionContext[];\n globalPatterns: ContextPattern[];\n decisionLog: Decision[];\n referenceIndex: ReferenceIndex;\n}\n\nexport interface SharedSessionContext {\n sessionId: string;\n runId: string;\n summary: string;\n keyFrames: FrameSummary[];\n createdAt: number;\n lastActiveAt: number;\n metadata: Record<string, any>;\n}\n\nexport interface FrameSummary {\n frameId: string;\n title: string;\n type: string;\n score: number;\n tags: string[];\n summary?: string;\n createdAt: number;\n}\n\nexport interface ContextPattern {\n pattern: string;\n type: 'error' | 'success' | 'decision' | 'learning';\n frequency: number;\n lastSeen: number;\n resolution?: string;\n}\n\nexport interface Decision {\n id: string;\n decision: string;\n reasoning: string;\n timestamp: number;\n sessionId: string;\n outcome?: 'success' | 'failure' | 'pending';\n}\n\nexport interface ReferenceIndex {\n byTag: Map<string, string[]>;\n byType: Map<string, string[]>;\n byScore: string[];\n recentlyAccessed: string[];\n}\n\nexport class SharedContextLayer {\n private static instance: SharedContextLayer;\n private contextDir: string;\n private cache: Map<string, SharedContext> = new Map();\n private readonly MAX_CACHE_SIZE = 100;\n private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n private lastCacheClean = Date.now();\n\n private constructor() {\n const homeDir = process.env['HOME'] || process.env['USERPROFILE'] || '';\n this.contextDir = path.join(homeDir, '.stackmemory', 'shared-context');\n }\n\n static getInstance(): SharedContextLayer {\n if (!SharedContextLayer.instance) {\n SharedContextLayer.instance = new SharedContextLayer();\n }\n return SharedContextLayer.instance;\n }\n\n async initialize(): Promise<void> {\n await fs.mkdir(this.contextDir, { recursive: true });\n await fs.mkdir(path.join(this.contextDir, 'projects'), { recursive: true });\n await fs.mkdir(path.join(this.contextDir, 'patterns'), { recursive: true });\n await fs.mkdir(path.join(this.contextDir, 'decisions'), {\n recursive: true,\n });\n }\n\n /**\n * Get or create shared context for current project/branch\n */\n async getSharedContext(options?: {\n projectId?: string;\n branch?: string;\n includeOtherBranches?: boolean;\n }): Promise<SharedContext> {\n const session = sessionManager.getCurrentSession();\n const projectId = options?.projectId || session?.projectId || 'global';\n const branch = options?.branch || session?.branch;\n\n const cacheKey = `${projectId}:${branch || 'main'}`;\n\n // Check cache first\n if (this.cache.has(cacheKey)) {\n const cached = this.cache.get(cacheKey)!;\n if (Date.now() - cached.lastUpdated < this.CACHE_TTL) {\n return cached;\n }\n }\n\n // Load from disk\n const context = await this.loadProjectContext(projectId, branch);\n\n // Include other branches if requested\n if (options?.includeOtherBranches) {\n const otherBranches = await this.loadOtherBranchContexts(\n projectId,\n branch\n );\n context.sessions.push(...otherBranches);\n }\n\n // Update cache\n this.cache.set(cacheKey, context);\n this.cleanCache();\n\n return context;\n }\n\n /**\n * Add current session's important frames to shared context\n */\n async addToSharedContext(\n frames: Frame[],\n options?: {\n minScore?: number;\n tags?: string[];\n }\n ): Promise<void> {\n const session = sessionManager.getCurrentSession();\n if (!session) return;\n\n const context = await this.getSharedContext();\n const minScore = options?.minScore || 0.7;\n\n // Filter important frames\n const importantFrames = frames.filter((f) => {\n const score = this.calculateFrameScore(f);\n return score >= minScore;\n });\n\n // Create session context\n const sessionContext: SharedSessionContext = {\n sessionId: session.sessionId,\n runId: session.runId,\n summary: this.generateSessionSummary(importantFrames),\n keyFrames: importantFrames.map((f) => this.summarizeFrame(f)),\n createdAt: session.startedAt,\n lastActiveAt: Date.now(),\n metadata: session.metadata,\n };\n\n // Update or add session context\n const existingIndex = context.sessions.findIndex(\n (s) => s.sessionId === session.sessionId\n );\n if (existingIndex >= 0) {\n context.sessions[existingIndex] = sessionContext;\n } else {\n context.sessions.push(sessionContext);\n }\n\n // Update patterns\n this.updatePatterns(context, importantFrames);\n\n // Update reference index\n this.updateReferenceIndex(context, importantFrames);\n\n // Save context\n await this.saveProjectContext(context);\n }\n\n /**\n * Query shared context for relevant frames\n */\n async querySharedContext(query: {\n tags?: string[];\n type?: string;\n minScore?: number;\n sessionId?: string;\n limit?: number;\n }): Promise<FrameSummary[]> {\n const context = await this.getSharedContext({ includeOtherBranches: true });\n let results: FrameSummary[] = [];\n\n // Collect all frames from all sessions\n for (const session of context.sessions) {\n if (query.sessionId && session.sessionId !== query.sessionId) continue;\n\n // Skip sessions without keyFrames\n if (!session.keyFrames || !Array.isArray(session.keyFrames)) continue;\n\n const filtered = session.keyFrames.filter((f) => {\n if (query.tags && !query.tags.some((tag) => f.tags.includes(tag)))\n return false;\n if (query.type && f.type !== query.type) return false;\n if (query.minScore && f.score < query.minScore) return false;\n return true;\n });\n\n results.push(...filtered);\n }\n\n // Sort by score and recency\n results.sort((a, b) => {\n const scoreWeight = 0.7;\n const recencyWeight = 0.3;\n\n const aScore =\n a.score * scoreWeight +\n (1 - (Date.now() - a.createdAt) / (30 * 24 * 60 * 60 * 1000)) *\n recencyWeight;\n const bScore =\n b.score * scoreWeight +\n (1 - (Date.now() - b.createdAt) / (30 * 24 * 60 * 60 * 1000)) *\n recencyWeight;\n\n return bScore - aScore;\n });\n\n // Apply limit\n if (query.limit) {\n results = results.slice(0, query.limit);\n }\n\n // Update recently accessed\n const index = context.referenceIndex;\n if (!index.recentlyAccessed) {\n index.recentlyAccessed = [];\n }\n\n // Add frameIds to recently accessed, removing duplicates\n if (results.length > 0) {\n const frameIds = results.map((r) => r.frameId);\n index.recentlyAccessed = [\n ...frameIds,\n ...index.recentlyAccessed.filter((id: any) => !frameIds.includes(id)),\n ].slice(0, 100);\n\n // Save the updated context with recently accessed frames\n await this.saveProjectContext(context);\n }\n\n return results;\n }\n\n /**\n * Get relevant patterns from shared context\n */\n async getPatterns(type?: ContextPattern['type']): Promise<ContextPattern[]> {\n const context = await this.getSharedContext();\n\n if (type) {\n return context.globalPatterns.filter((p) => p.type === type);\n }\n\n return context.globalPatterns;\n }\n\n /**\n * Add a decision to the shared context\n */\n async addDecision(\n decision: Omit<Decision, 'id' | 'timestamp' | 'sessionId'>\n ): Promise<void> {\n const session = sessionManager.getCurrentSession();\n if (!session) return;\n\n const context = await this.getSharedContext();\n\n const newDecision: Decision = {\n id: uuidv4(),\n timestamp: Date.now(),\n sessionId: session.sessionId,\n outcome: 'pending',\n ...decision,\n };\n\n context.decisionLog.push(newDecision);\n\n // Keep only last 100 decisions\n if (context.decisionLog.length > 100) {\n context.decisionLog = context.decisionLog.slice(-100);\n }\n\n await this.saveProjectContext(context);\n }\n\n /**\n * Get recent decisions from shared context\n */\n async getDecisions(limit: number = 10): Promise<Decision[]> {\n const context = await this.getSharedContext();\n return context.decisionLog.slice(-limit);\n }\n\n /**\n * Automatic context discovery on CLI startup\n */\n async autoDiscoverContext(): Promise<{\n hasSharedContext: boolean;\n sessionCount: number;\n recentPatterns: ContextPattern[];\n lastDecisions: Decision[];\n suggestedFrames: FrameSummary[];\n }> {\n const context = await this.getSharedContext({\n includeOtherBranches: false,\n });\n\n // Get recent patterns (last 7 days)\n const recentPatterns = context.globalPatterns\n .filter((p) => Date.now() - p.lastSeen < 7 * 24 * 60 * 60 * 1000)\n .sort((a, b) => b.frequency - a.frequency)\n .slice(0, 5);\n\n // Get last 5 decisions\n const lastDecisions = context.decisionLog.slice(-5);\n\n // Get suggested frames based on recent access and score\n const suggestedFrames = await this.querySharedContext({\n minScore: 0.8,\n limit: 5,\n });\n\n return {\n hasSharedContext: context.sessions.length > 0,\n sessionCount: context.sessions.length,\n recentPatterns,\n lastDecisions,\n suggestedFrames,\n };\n }\n\n private async loadProjectContext(\n projectId: string,\n branch?: string\n ): Promise<SharedContext> {\n const contextFile = path.join(\n this.contextDir,\n 'projects',\n `${projectId}_${branch || 'main'}.json`\n );\n\n try {\n const data = await fs.readFile(contextFile, 'utf-8');\n const context = JSON.parse(data);\n\n // Reconstruct Maps\n context.referenceIndex.byTag = new Map(\n Object.entries(context.referenceIndex.byTag || {})\n );\n context.referenceIndex.byType = new Map(\n Object.entries(context.referenceIndex.byType || {})\n );\n\n return context;\n } catch {\n // Return empty context if file doesn't exist\n return {\n projectId,\n branch,\n lastUpdated: Date.now(),\n sessions: [],\n globalPatterns: [],\n decisionLog: [],\n referenceIndex: {\n byTag: new Map(),\n byType: new Map(),\n byScore: [],\n recentlyAccessed: [],\n },\n };\n }\n }\n\n private async saveProjectContext(context: SharedContext): Promise<void> {\n const contextFile = path.join(\n this.contextDir,\n 'projects',\n `${context.projectId}_${context.branch || 'main'}.json`\n );\n\n // Convert Maps to objects for JSON serialization\n const serializable = {\n ...context,\n lastUpdated: Date.now(),\n referenceIndex: {\n ...context.referenceIndex,\n byTag: Object.fromEntries(context.referenceIndex.byTag),\n byType: Object.fromEntries(context.referenceIndex.byType),\n },\n };\n\n await fs.writeFile(contextFile, JSON.stringify(serializable, null, 2));\n }\n\n private async loadOtherBranchContexts(\n projectId: string,\n currentBranch?: string\n ): Promise<SharedSessionContext[]> {\n const projectsDir = path.join(this.contextDir, 'projects');\n const files = await fs.readdir(projectsDir);\n const sessions: SharedSessionContext[] = [];\n\n for (const file of files) {\n if (\n file.startsWith(`${projectId}_`) &&\n !file.includes(currentBranch || 'main')\n ) {\n try {\n const data = await fs.readFile(path.join(projectsDir, file), 'utf-8');\n const context = JSON.parse(data);\n sessions.push(...context.sessions);\n } catch {\n // Skip invalid files\n }\n }\n }\n\n return sessions;\n }\n\n private calculateFrameScore(frame: Frame): number {\n // Simple scoring algorithm\n let score = 0.5;\n\n // Boost for certain types\n if (frame.type === 'task' || frame.type === 'review') score += 0.2;\n if (frame.type === 'debug' || frame.type === 'write') score += 0.15;\n if (frame.type === 'error') score += 0.15; // Error frames are important for pattern extraction\n\n // Check for data property (used in tests)\n const frameWithData = frame as any;\n if (frameWithData.data) score += 0.2;\n\n // Boost for having outputs (indicates completion/results)\n if (frame.outputs && Object.keys(frame.outputs).length > 0) score += 0.2;\n if (\n frame.digest_text ||\n (frame.digest_json && Object.keys(frame.digest_json).length > 0)\n )\n score += 0.1;\n\n // Time decay (reduce score for older frames) - but handle missing created_at\n if (frame.created_at) {\n const age = Date.now() - frame.created_at;\n const daysSinceCreation = age / (24 * 60 * 60 * 1000);\n score *= Math.max(0.3, 1 - daysSinceCreation / 30);\n }\n\n return Math.min(1, score);\n }\n\n private summarizeFrame(frame: Frame): FrameSummary {\n return {\n frameId: frame.frame_id,\n title: frame.name,\n type: frame.type,\n score: this.calculateFrameScore(frame),\n tags: [],\n summary: this.generateFrameSummary(frame),\n createdAt: frame.created_at,\n };\n }\n\n private generateFrameSummary(frame: Frame): string {\n // Generate a brief summary of the frame\n const parts = [];\n const frameWithData = frame as any;\n\n if (frame.type) parts.push(`[${frame.type}]`);\n if (frame.name) parts.push(frame.name);\n if (frameWithData.title) parts.push(frameWithData.title);\n if (frameWithData.data?.error)\n parts.push(`Error: ${frameWithData.data.error}`);\n if (frameWithData.data?.resolution)\n parts.push(`Resolution: ${frameWithData.data.resolution}`);\n\n return parts.join(' - ').slice(0, 200);\n }\n\n private generateSessionSummary(frames: Frame[]): string {\n const types = [...new Set(frames.map((f) => f.type))];\n return `Session with ${frames.length} key frames: ${types.join(', ')}`;\n }\n\n private updatePatterns(context: SharedContext, frames: Frame[]): void {\n for (const frame of frames) {\n // Extract patterns from frame data\n // Handle frames with a data property (used in tests)\n const frameWithData = frame as any;\n if (frameWithData.data?.error) {\n this.addPattern(\n context,\n frameWithData.data.error,\n 'error',\n frameWithData.data?.resolution\n );\n } else if (frame.type === 'error' && frame.name) {\n // Only extract from name/outputs if no data.error property\n const errorText = frame.outputs?.error || frame.name;\n const resolution = frame.outputs?.resolution;\n if (errorText) {\n this.addPattern(context, errorText, 'error', resolution);\n }\n }\n\n if (frame.type === 'decision' && frameWithData.data?.decision) {\n this.addPattern(context, frameWithData.data.decision, 'decision');\n } else if (frame.digest_json?.decision) {\n // Only extract from digest_json if no data.decision\n this.addPattern(context, frame.digest_json.decision, 'decision');\n }\n }\n }\n\n private addPattern(\n context: SharedContext,\n pattern: string,\n type: ContextPattern['type'],\n resolution?: string\n ): void {\n const existing = context.globalPatterns.find(\n (p) => p.pattern === pattern && p.type === type\n );\n\n if (existing) {\n existing.frequency++;\n existing.lastSeen = Date.now();\n if (resolution) existing.resolution = resolution;\n } else {\n context.globalPatterns.push({\n pattern,\n type,\n frequency: 1,\n lastSeen: Date.now(),\n resolution,\n });\n }\n\n // Keep only top 100 patterns\n if (context.globalPatterns.length > 100) {\n context.globalPatterns.sort((a, b) => b.frequency - a.frequency);\n context.globalPatterns = context.globalPatterns.slice(0, 100);\n }\n }\n\n private updateReferenceIndex(context: SharedContext, frames: Frame[]): void {\n for (const frame of frames) {\n const summary = this.summarizeFrame(frame);\n\n // Index by tags\n for (const tag of summary.tags) {\n if (!context.referenceIndex.byTag.has(tag)) {\n context.referenceIndex.byTag.set(tag, []);\n }\n context.referenceIndex.byTag.get(tag)!.push(frame.frameId);\n }\n\n // Index by type\n if (!context.referenceIndex.byType.has(frame.type)) {\n context.referenceIndex.byType.set(frame.type, []);\n }\n context.referenceIndex.byType.get(frame.type)!.push(frame.frameId);\n\n // Update score index\n const scoreIndex = context.referenceIndex.byScore;\n const insertIndex = scoreIndex.findIndex((id) => {\n const otherFrame = context.sessions\n .flatMap((s) => s.keyFrames)\n .find((f) => f.frameId === id);\n return otherFrame && otherFrame.score < summary.score;\n });\n\n if (insertIndex >= 0) {\n scoreIndex.splice(insertIndex, 0, frame.frameId);\n } else {\n scoreIndex.push(frame.frameId);\n }\n\n // Keep only top 1000 by score\n context.referenceIndex.byScore = scoreIndex.slice(0, 1000);\n }\n }\n\n private cleanCache(): void {\n if (Date.now() - this.lastCacheClean < 60000) return; // Clean every minute\n\n if (this.cache.size > this.MAX_CACHE_SIZE) {\n const entries = Array.from(this.cache.entries()).sort(\n (a, b) => b[1].lastUpdated - a[1].lastUpdated\n );\n\n this.cache = new Map(entries.slice(0, this.MAX_CACHE_SIZE / 2));\n }\n\n this.lastCacheClean = Date.now();\n }\n}\n\nexport const sharedContextLayer = SharedContextLayer.getInstance();\n\n// Export for testing\nexport {\n SharedContext,\n SharedSessionContext,\n FrameSummary,\n ContextPattern,\n Decision,\n ReferenceIndex,\n};\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -51,22 +51,23 @@ class BatchOperationsManager {
51
51
  * Bulk update frame digests
52
52
  */
53
53
  async bulkUpdateFrameDigests(updates, options = {}) {
54
- const {
55
- batchSize = 50,
56
- enableTransactions = true
57
- } = options;
58
- return trace.traceAsync("function", "bulkUpdateFrameDigests", { count: updates.length }, async () => {
59
- const startTime = performance.now();
60
- const stats = {
61
- totalRecords: updates.length,
62
- batchesProcessed: 0,
63
- successfulInserts: 0,
64
- failedInserts: 0,
65
- totalTimeMs: 0,
66
- avgBatchTimeMs: 0
67
- };
68
- if (updates.length === 0) return stats;
69
- const stmt = this.db.prepare(`
54
+ const { batchSize = 50, enableTransactions = true } = options;
55
+ return trace.traceAsync(
56
+ "function",
57
+ "bulkUpdateFrameDigests",
58
+ { count: updates.length },
59
+ async () => {
60
+ const startTime = performance.now();
61
+ const stats = {
62
+ totalRecords: updates.length,
63
+ batchesProcessed: 0,
64
+ successfulInserts: 0,
65
+ failedInserts: 0,
66
+ totalTimeMs: 0,
67
+ avgBatchTimeMs: 0
68
+ };
69
+ if (updates.length === 0) return stats;
70
+ const stmt = this.db.prepare(`
70
71
  UPDATE frames
71
72
  SET digest_text = ?,
72
73
  digest_json = ?,
@@ -74,37 +75,41 @@ class BatchOperationsManager {
74
75
  state = CASE WHEN ? IS NOT NULL THEN 'closed' ELSE state END
75
76
  WHERE frame_id = ?
76
77
  `);
77
- const updateFn = (batch) => {
78
- for (const update of batch) {
79
- try {
80
- const result = stmt.run(
81
- update.digest_text,
82
- JSON.stringify(update.digest_json),
83
- update.closed_at,
84
- update.closed_at,
85
- update.frame_id
86
- );
87
- stats.successfulInserts += result.changes;
88
- } catch (error) {
89
- stats.failedInserts++;
90
- logger.warn("Failed to update frame digest", {
91
- frameId: update.frame_id,
92
- error: error.message
93
- });
78
+ const updateFn = (batch) => {
79
+ for (const update of batch) {
80
+ try {
81
+ const result = stmt.run(
82
+ update.digest_text,
83
+ JSON.stringify(update.digest_json),
84
+ update.closed_at,
85
+ update.closed_at,
86
+ update.frame_id
87
+ );
88
+ stats.successfulInserts += result.changes;
89
+ } catch (error) {
90
+ stats.failedInserts++;
91
+ logger.warn("Failed to update frame digest", {
92
+ frameId: update.frame_id,
93
+ error: error.message
94
+ });
95
+ }
94
96
  }
97
+ };
98
+ if (enableTransactions) {
99
+ const transaction = this.db.transaction(updateFn);
100
+ await this.processBatches(updates, batchSize, transaction, stats);
101
+ } else {
102
+ await this.processBatches(updates, batchSize, updateFn, stats);
95
103
  }
96
- };
97
- if (enableTransactions) {
98
- const transaction = this.db.transaction(updateFn);
99
- await this.processBatches(updates, batchSize, transaction, stats);
100
- } else {
101
- await this.processBatches(updates, batchSize, updateFn, stats);
104
+ stats.totalTimeMs = performance.now() - startTime;
105
+ stats.avgBatchTimeMs = stats.batchesProcessed > 0 ? stats.totalTimeMs / stats.batchesProcessed : 0;
106
+ logger.info(
107
+ "Bulk frame digest update completed",
108
+ stats
109
+ );
110
+ return stats;
102
111
  }
103
- stats.totalTimeMs = performance.now() - startTime;
104
- stats.avgBatchTimeMs = stats.batchesProcessed > 0 ? stats.totalTimeMs / stats.batchesProcessed : 0;
105
- logger.info("Bulk frame digest update completed", stats);
106
- return stats;
107
- });
112
+ );
108
113
  }
109
114
  /**
110
115
  * Generic bulk insert with preprocessing
@@ -116,50 +121,68 @@ class BatchOperationsManager {
116
121
  enableTransactions = true,
117
122
  preprocessor
118
123
  } = options;
119
- return trace.traceAsync("function", `bulkInsert${table}`, { count: records.length }, async () => {
120
- const startTime = performance.now();
121
- const stats = {
122
- totalRecords: records.length,
123
- batchesProcessed: 0,
124
- successfulInserts: 0,
125
- failedInserts: 0,
126
- totalTimeMs: 0,
127
- avgBatchTimeMs: 0
128
- };
129
- if (records.length === 0) return stats;
130
- const processedRecords = preprocessor ? records.map(preprocessor) : records;
131
- const firstRecord = processedRecords[0];
132
- const columns = Object.keys(firstRecord);
133
- const placeholders = columns.map(() => "?").join(", ");
134
- const conflictClause = this.getConflictClause(onConflict);
135
- const insertSql = `INSERT ${conflictClause} INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
136
- const stmt = this.db.prepare(insertSql);
137
- const insertFn = (batch) => {
138
- for (const record of batch) {
139
- try {
140
- const values = columns.map((col) => record[col]);
141
- const result = stmt.run(...values);
142
- stats.successfulInserts += result.changes;
143
- } catch (error) {
144
- stats.failedInserts++;
145
- logger.warn(`Failed to insert ${table} record`, {
146
- record,
147
- error: error.message
148
- });
124
+ return trace.traceAsync(
125
+ "function",
126
+ `bulkInsert${table}`,
127
+ { count: records.length },
128
+ async () => {
129
+ const startTime = performance.now();
130
+ const stats = {
131
+ totalRecords: records.length,
132
+ batchesProcessed: 0,
133
+ successfulInserts: 0,
134
+ failedInserts: 0,
135
+ totalTimeMs: 0,
136
+ avgBatchTimeMs: 0
137
+ };
138
+ if (records.length === 0) return stats;
139
+ const processedRecords = preprocessor ? records.map(preprocessor) : records;
140
+ const firstRecord = processedRecords[0];
141
+ const columns = Object.keys(firstRecord);
142
+ const placeholders = columns.map(() => "?").join(", ");
143
+ const conflictClause = this.getConflictClause(onConflict);
144
+ const insertSql = `INSERT ${conflictClause} INTO ${table} (${columns.join(", ")}) VALUES (${placeholders})`;
145
+ const stmt = this.db.prepare(insertSql);
146
+ const insertFn = (batch) => {
147
+ for (const record of batch) {
148
+ try {
149
+ const values = columns.map((col) => record[col]);
150
+ const result = stmt.run(...values);
151
+ stats.successfulInserts += result.changes;
152
+ } catch (error) {
153
+ stats.failedInserts++;
154
+ logger.warn(`Failed to insert ${table} record`, {
155
+ record,
156
+ error: error.message
157
+ });
158
+ }
149
159
  }
160
+ };
161
+ if (enableTransactions) {
162
+ const transaction = this.db.transaction(insertFn);
163
+ await this.processBatches(
164
+ processedRecords,
165
+ batchSize,
166
+ transaction,
167
+ stats
168
+ );
169
+ } else {
170
+ await this.processBatches(
171
+ processedRecords,
172
+ batchSize,
173
+ insertFn,
174
+ stats
175
+ );
150
176
  }
151
- };
152
- if (enableTransactions) {
153
- const transaction = this.db.transaction(insertFn);
154
- await this.processBatches(processedRecords, batchSize, transaction, stats);
155
- } else {
156
- await this.processBatches(processedRecords, batchSize, insertFn, stats);
177
+ stats.totalTimeMs = performance.now() - startTime;
178
+ stats.avgBatchTimeMs = stats.batchesProcessed > 0 ? stats.totalTimeMs / stats.batchesProcessed : 0;
179
+ logger.info(
180
+ `Bulk ${table} insert completed`,
181
+ stats
182
+ );
183
+ return stats;
157
184
  }
158
- stats.totalTimeMs = performance.now() - startTime;
159
- stats.avgBatchTimeMs = stats.batchesProcessed > 0 ? stats.totalTimeMs / stats.batchesProcessed : 0;
160
- logger.info(`Bulk ${table} insert completed`, stats);
161
- return stats;
162
- });
185
+ );
163
186
  }
164
187
  /**
165
188
  * Process records in batches
@@ -272,7 +295,10 @@ class BatchOperationsManager {
272
295
  break;
273
296
  // Add update and delete operations as needed
274
297
  default:
275
- logger.warn("Unsupported batch operation", { table, operation: op.operation });
298
+ logger.warn("Unsupported batch operation", {
299
+ table,
300
+ operation: op.operation
301
+ });
276
302
  }
277
303
  }
278
304
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/database/batch-operations.ts"],
4
- "sourcesContent": ["/**\n * Batch Database Operations\n * High-performance bulk operations with transaction management\n */\n\nimport Database from 'better-sqlite3';\nimport { getConnectionPool } from './connection-pool.js';\nimport { logger } from '../monitoring/logger.js';\nimport { trace } from '../trace/index.js';\n\nexport interface BatchOperation {\n table: string;\n operation: 'insert' | 'update' | 'delete';\n data: Record<string, any>[];\n onConflict?: 'ignore' | 'replace' | 'update';\n}\n\nexport interface BulkInsertOptions {\n batchSize?: number;\n onConflict?: 'ignore' | 'replace' | 'update';\n enableTransactions?: boolean;\n parallelTables?: boolean;\n}\n\nexport interface BatchStats {\n totalRecords: number;\n batchesProcessed: number;\n successfulInserts: number;\n failedInserts: number;\n totalTimeMs: number;\n avgBatchTimeMs: number;\n}\n\n/**\n * High-performance batch operations manager\n */\nexport class BatchOperationsManager {\n private db: Database.Database;\n private preparedStatements = new Map<string, Database.Statement>();\n private batchQueue: BatchOperation[] = [];\n private isProcessing = false;\n\n constructor(db?: Database.Database) {\n if (db) {\n this.db = db;\n this.initializePreparedStatements();\n } else {\n // Will be initialized when used with getConnectionPool().withConnection()\n this.db = undefined as any;\n }\n }\n\n /**\n * Add events in bulk with optimized batching\n */\n async bulkInsertEvents(\n events: Array<{\n frame_id: string;\n run_id: string;\n seq: number;\n event_type: string;\n payload: any;\n ts: number;\n }>,\n options: BulkInsertOptions = {}\n ): Promise<BatchStats> {\n const {\n batchSize = 100,\n onConflict = 'ignore',\n enableTransactions = true,\n } = options;\n\n return this.performBulkInsert('events', events, {\n batchSize,\n onConflict,\n enableTransactions,\n preprocessor: (event) => ({\n ...event,\n event_id: `evt_${event.frame_id}_${event.seq}_${Date.now()}`,\n payload: JSON.stringify(event.payload),\n }),\n });\n }\n\n /**\n * Add anchors in bulk\n */\n async bulkInsertAnchors(\n anchors: Array<{\n frame_id: string;\n type: string;\n text: string;\n priority: number;\n metadata: any;\n }>,\n options: BulkInsertOptions = {}\n ): Promise<BatchStats> {\n return this.performBulkInsert('anchors', anchors, {\n ...options,\n preprocessor: (anchor) => ({\n ...anchor,\n anchor_id: `anc_${anchor.frame_id}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n metadata: JSON.stringify(anchor.metadata),\n created_at: Date.now(),\n }),\n });\n }\n\n /**\n * Bulk update frame digests\n */\n async bulkUpdateFrameDigests(\n updates: Array<{\n frame_id: string;\n digest_text: string;\n digest_json: any;\n closed_at?: number;\n }>,\n options: BulkInsertOptions = {}\n ): Promise<BatchStats> {\n const {\n batchSize = 50,\n enableTransactions = true,\n } = options;\n\n return trace.traceAsync('function', 'bulkUpdateFrameDigests', { count: updates.length }, async () => {\n const startTime = performance.now();\n const stats: BatchStats = {\n totalRecords: updates.length,\n batchesProcessed: 0,\n successfulInserts: 0,\n failedInserts: 0,\n totalTimeMs: 0,\n avgBatchTimeMs: 0,\n };\n\n if (updates.length === 0) return stats;\n\n const stmt = this.db.prepare(`\n UPDATE frames \n SET digest_text = ?, \n digest_json = ?, \n closed_at = COALESCE(?, closed_at),\n state = CASE WHEN ? IS NOT NULL THEN 'closed' ELSE state END\n WHERE frame_id = ?\n `);\n\n const updateFn = (batch: typeof updates) => {\n for (const update of batch) {\n try {\n const result = stmt.run(\n update.digest_text,\n JSON.stringify(update.digest_json),\n update.closed_at,\n update.closed_at,\n update.frame_id\n );\n stats.successfulInserts += result.changes;\n } catch (error: unknown) {\n stats.failedInserts++;\n logger.warn('Failed to update frame digest', {\n frameId: update.frame_id,\n error: (error as Error).message,\n });\n }\n }\n };\n\n if (enableTransactions) {\n const transaction = this.db.transaction(updateFn);\n await this.processBatches(updates, batchSize, transaction, stats);\n } else {\n await this.processBatches(updates, batchSize, updateFn, stats);\n }\n\n stats.totalTimeMs = performance.now() - startTime;\n stats.avgBatchTimeMs = stats.batchesProcessed > 0 \n ? stats.totalTimeMs / stats.batchesProcessed \n : 0;\n\n logger.info('Bulk frame digest update completed', stats as unknown as Record<string, unknown>);\n return stats;\n });\n }\n\n /**\n * Generic bulk insert with preprocessing\n */\n private async performBulkInsert<T extends Record<string, any>>(\n table: string,\n records: T[],\n options: BulkInsertOptions & {\n preprocessor?: (record: T) => Record<string, any>;\n } = {}\n ): Promise<BatchStats> {\n const {\n batchSize = 100,\n onConflict = 'ignore',\n enableTransactions = true,\n preprocessor,\n } = options;\n\n return trace.traceAsync('function', `bulkInsert${table}`, { count: records.length }, async () => {\n const startTime = performance.now();\n const stats: BatchStats = {\n totalRecords: records.length,\n batchesProcessed: 0,\n successfulInserts: 0,\n failedInserts: 0,\n totalTimeMs: 0,\n avgBatchTimeMs: 0,\n };\n\n if (records.length === 0) return stats;\n\n // Preprocess records if needed\n const processedRecords = preprocessor \n ? records.map(preprocessor)\n : records;\n\n // Build dynamic insert statement\n const firstRecord = processedRecords[0];\n const columns = Object.keys(firstRecord);\n const placeholders = columns.map(() => '?').join(', ');\n const conflictClause = this.getConflictClause(onConflict);\n \n const insertSql = `INSERT ${conflictClause} INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;\n const stmt = this.db.prepare(insertSql);\n\n const insertFn = (batch: typeof processedRecords) => {\n for (const record of batch) {\n try {\n const values = columns.map((col: any) => record[col]);\n const result = stmt.run(...values);\n stats.successfulInserts += result.changes;\n } catch (error: unknown) {\n stats.failedInserts++;\n logger.warn(`Failed to insert ${table} record`, {\n record,\n error: (error as Error).message,\n });\n }\n }\n };\n\n if (enableTransactions) {\n const transaction = this.db.transaction(insertFn);\n await this.processBatches(processedRecords, batchSize, transaction, stats);\n } else {\n await this.processBatches(processedRecords, batchSize, insertFn, stats);\n }\n\n stats.totalTimeMs = performance.now() - startTime;\n stats.avgBatchTimeMs = stats.batchesProcessed > 0 \n ? stats.totalTimeMs / stats.batchesProcessed \n : 0;\n\n logger.info(`Bulk ${table} insert completed`, stats as unknown as Record<string, unknown>);\n return stats;\n });\n }\n\n /**\n * Process records in batches\n */\n private async processBatches<T>(\n records: T[],\n batchSize: number,\n processFn: (batch: T[]) => void,\n stats: BatchStats\n ): Promise<void> {\n for (let i = 0; i < records.length; i += batchSize) {\n const batch = records.slice(i, i + batchSize);\n const batchStart = performance.now();\n \n try {\n processFn(batch);\n stats.batchesProcessed++;\n \n const batchTime = performance.now() - batchStart;\n logger.debug('Batch processed', {\n batchNumber: stats.batchesProcessed,\n records: batch.length,\n timeMs: batchTime.toFixed(2),\n });\n\n // Yield control to prevent blocking\n if (stats.batchesProcessed % 10 === 0) {\n await new Promise(resolve => setImmediate(resolve));\n }\n\n } catch (error: unknown) {\n stats.failedInserts += batch.length;\n logger.error('Batch processing failed', error as Error, {\n batchNumber: stats.batchesProcessed + 1,\n batchSize: batch.length,\n });\n }\n }\n }\n\n /**\n * Queue batch operation for later processing\n */\n queueBatchOperation(operation: BatchOperation): void {\n this.batchQueue.push(operation);\n \n if (this.batchQueue.length >= 10 && !this.isProcessing) {\n setImmediate(() => this.processBatchQueue());\n }\n }\n\n /**\n * Process queued batch operations\n */\n async processBatchQueue(): Promise<void> {\n if (this.isProcessing || this.batchQueue.length === 0) {\n return;\n }\n\n this.isProcessing = true;\n const operations = [...this.batchQueue];\n this.batchQueue = [];\n\n try {\n const groupedOps = this.groupOperationsByTable(operations);\n \n for (const [table, tableOps] of groupedOps) {\n await this.processTableOperations(table, tableOps);\n }\n\n logger.info('Batch queue processed', {\n operations: operations.length,\n tables: groupedOps.size,\n });\n\n } catch (error: unknown) {\n logger.error('Batch queue processing failed', error as Error);\n } finally {\n this.isProcessing = false;\n }\n }\n\n /**\n * Flush any remaining queued operations\n */\n async flush(): Promise<void> {\n if (this.batchQueue.length > 0) {\n await this.processBatchQueue();\n }\n }\n\n /**\n * Get SQL conflict clause\n */\n private getConflictClause(onConflict: string): string {\n switch (onConflict) {\n case 'ignore':\n return 'OR IGNORE';\n case 'replace':\n return 'OR REPLACE';\n case 'update':\n return 'ON CONFLICT DO UPDATE SET';\n default:\n return '';\n }\n }\n\n /**\n * Group operations by table for efficient processing\n */\n private groupOperationsByTable(operations: BatchOperation[]): Map<string, BatchOperation[]> {\n const grouped = new Map<string, BatchOperation[]>();\n \n for (const op of operations) {\n if (!grouped.has(op.table)) {\n grouped.set(op.table, []);\n }\n grouped.get(op.table)!.push(op);\n }\n \n return grouped;\n }\n\n /**\n * Process all operations for a specific table\n */\n private async processTableOperations(table: string, operations: BatchOperation[]): Promise<void> {\n for (const op of operations) {\n switch (op.operation) {\n case 'insert':\n await this.performBulkInsert(table, op.data, {\n onConflict: op.onConflict,\n });\n break;\n // Add update and delete operations as needed\n default:\n logger.warn('Unsupported batch operation', { table, operation: op.operation });\n }\n }\n }\n\n /**\n * Initialize commonly used prepared statements\n */\n private initializePreparedStatements(): void {\n // Event insertion\n this.preparedStatements.set('insert_event', \n this.db.prepare(`\n INSERT OR IGNORE INTO events \n (event_id, frame_id, run_id, seq, event_type, payload, ts) \n VALUES (?, ?, ?, ?, ?, ?, ?)\n `)\n );\n\n // Anchor insertion\n this.preparedStatements.set('insert_anchor',\n this.db.prepare(`\n INSERT OR IGNORE INTO anchors \n (anchor_id, frame_id, type, text, priority, metadata, created_at) \n VALUES (?, ?, ?, ?, ?, ?, ?)\n `)\n );\n\n logger.info('Batch operations prepared statements initialized');\n }\n\n /**\n * Cleanup resources\n */\n cleanup(): void {\n // Modern better-sqlite3 automatically handles cleanup\n this.preparedStatements.clear();\n }\n}\n\n// Global batch operations manager\nlet globalBatchManager: BatchOperationsManager | null = null;\n\n/**\n * Get or create global batch operations manager\n */\nexport function getBatchManager(db?: Database.Database): BatchOperationsManager {\n if (!globalBatchManager) {\n globalBatchManager = new BatchOperationsManager(db);\n }\n return globalBatchManager;\n}\n\n/**\n * Convenience function for bulk event insertion\n */\nexport async function bulkInsertEvents(\n events: any[],\n options?: BulkInsertOptions\n): Promise<BatchStats> {\n const manager = getBatchManager();\n return manager.bulkInsertEvents(events, options);\n}\n\n/**\n * Convenience function for bulk anchor insertion\n */\nexport async function bulkInsertAnchors(\n anchors: any[],\n options?: BulkInsertOptions\n): Promise<BatchStats> {\n const manager = getBatchManager();\n return manager.bulkInsertAnchors(anchors, options);\n}"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Batch Database Operations\n * High-performance bulk operations with transaction management\n */\n\nimport Database from 'better-sqlite3';\nimport { getConnectionPool } from './connection-pool.js';\nimport { logger } from '../monitoring/logger.js';\nimport { trace } from '../trace/index.js';\n\nexport interface BatchOperation {\n table: string;\n operation: 'insert' | 'update' | 'delete';\n data: Record<string, any>[];\n onConflict?: 'ignore' | 'replace' | 'update';\n}\n\nexport interface BulkInsertOptions {\n batchSize?: number;\n onConflict?: 'ignore' | 'replace' | 'update';\n enableTransactions?: boolean;\n parallelTables?: boolean;\n}\n\nexport interface BatchStats {\n totalRecords: number;\n batchesProcessed: number;\n successfulInserts: number;\n failedInserts: number;\n totalTimeMs: number;\n avgBatchTimeMs: number;\n}\n\n/**\n * High-performance batch operations manager\n */\nexport class BatchOperationsManager {\n private db: Database.Database;\n private preparedStatements = new Map<string, Database.Statement>();\n private batchQueue: BatchOperation[] = [];\n private isProcessing = false;\n\n constructor(db?: Database.Database) {\n if (db) {\n this.db = db;\n this.initializePreparedStatements();\n } else {\n // Will be initialized when used with getConnectionPool().withConnection()\n this.db = undefined as any;\n }\n }\n\n /**\n * Add events in bulk with optimized batching\n */\n async bulkInsertEvents(\n events: Array<{\n frame_id: string;\n run_id: string;\n seq: number;\n event_type: string;\n payload: any;\n ts: number;\n }>,\n options: BulkInsertOptions = {}\n ): Promise<BatchStats> {\n const {\n batchSize = 100,\n onConflict = 'ignore',\n enableTransactions = true,\n } = options;\n\n return this.performBulkInsert('events', events, {\n batchSize,\n onConflict,\n enableTransactions,\n preprocessor: (event) => ({\n ...event,\n event_id: `evt_${event.frame_id}_${event.seq}_${Date.now()}`,\n payload: JSON.stringify(event.payload),\n }),\n });\n }\n\n /**\n * Add anchors in bulk\n */\n async bulkInsertAnchors(\n anchors: Array<{\n frame_id: string;\n type: string;\n text: string;\n priority: number;\n metadata: any;\n }>,\n options: BulkInsertOptions = {}\n ): Promise<BatchStats> {\n return this.performBulkInsert('anchors', anchors, {\n ...options,\n preprocessor: (anchor) => ({\n ...anchor,\n anchor_id: `anc_${anchor.frame_id}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,\n metadata: JSON.stringify(anchor.metadata),\n created_at: Date.now(),\n }),\n });\n }\n\n /**\n * Bulk update frame digests\n */\n async bulkUpdateFrameDigests(\n updates: Array<{\n frame_id: string;\n digest_text: string;\n digest_json: any;\n closed_at?: number;\n }>,\n options: BulkInsertOptions = {}\n ): Promise<BatchStats> {\n const { batchSize = 50, enableTransactions = true } = options;\n\n return trace.traceAsync(\n 'function',\n 'bulkUpdateFrameDigests',\n { count: updates.length },\n async () => {\n const startTime = performance.now();\n const stats: BatchStats = {\n totalRecords: updates.length,\n batchesProcessed: 0,\n successfulInserts: 0,\n failedInserts: 0,\n totalTimeMs: 0,\n avgBatchTimeMs: 0,\n };\n\n if (updates.length === 0) return stats;\n\n const stmt = this.db.prepare(`\n UPDATE frames \n SET digest_text = ?, \n digest_json = ?, \n closed_at = COALESCE(?, closed_at),\n state = CASE WHEN ? IS NOT NULL THEN 'closed' ELSE state END\n WHERE frame_id = ?\n `);\n\n const updateFn = (batch: typeof updates) => {\n for (const update of batch) {\n try {\n const result = stmt.run(\n update.digest_text,\n JSON.stringify(update.digest_json),\n update.closed_at,\n update.closed_at,\n update.frame_id\n );\n stats.successfulInserts += result.changes;\n } catch (error: unknown) {\n stats.failedInserts++;\n logger.warn('Failed to update frame digest', {\n frameId: update.frame_id,\n error: (error as Error).message,\n });\n }\n }\n };\n\n if (enableTransactions) {\n const transaction = this.db.transaction(updateFn);\n await this.processBatches(updates, batchSize, transaction, stats);\n } else {\n await this.processBatches(updates, batchSize, updateFn, stats);\n }\n\n stats.totalTimeMs = performance.now() - startTime;\n stats.avgBatchTimeMs =\n stats.batchesProcessed > 0\n ? stats.totalTimeMs / stats.batchesProcessed\n : 0;\n\n logger.info(\n 'Bulk frame digest update completed',\n stats as unknown as Record<string, unknown>\n );\n return stats;\n }\n );\n }\n\n /**\n * Generic bulk insert with preprocessing\n */\n private async performBulkInsert<T extends Record<string, any>>(\n table: string,\n records: T[],\n options: BulkInsertOptions & {\n preprocessor?: (record: T) => Record<string, any>;\n } = {}\n ): Promise<BatchStats> {\n const {\n batchSize = 100,\n onConflict = 'ignore',\n enableTransactions = true,\n preprocessor,\n } = options;\n\n return trace.traceAsync(\n 'function',\n `bulkInsert${table}`,\n { count: records.length },\n async () => {\n const startTime = performance.now();\n const stats: BatchStats = {\n totalRecords: records.length,\n batchesProcessed: 0,\n successfulInserts: 0,\n failedInserts: 0,\n totalTimeMs: 0,\n avgBatchTimeMs: 0,\n };\n\n if (records.length === 0) return stats;\n\n // Preprocess records if needed\n const processedRecords = preprocessor\n ? records.map(preprocessor)\n : records;\n\n // Build dynamic insert statement\n const firstRecord = processedRecords[0];\n const columns = Object.keys(firstRecord);\n const placeholders = columns.map(() => '?').join(', ');\n const conflictClause = this.getConflictClause(onConflict);\n\n const insertSql = `INSERT ${conflictClause} INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;\n const stmt = this.db.prepare(insertSql);\n\n const insertFn = (batch: typeof processedRecords) => {\n for (const record of batch) {\n try {\n const values = columns.map((col: any) => record[col]);\n const result = stmt.run(...values);\n stats.successfulInserts += result.changes;\n } catch (error: unknown) {\n stats.failedInserts++;\n logger.warn(`Failed to insert ${table} record`, {\n record,\n error: (error as Error).message,\n });\n }\n }\n };\n\n if (enableTransactions) {\n const transaction = this.db.transaction(insertFn);\n await this.processBatches(\n processedRecords,\n batchSize,\n transaction,\n stats\n );\n } else {\n await this.processBatches(\n processedRecords,\n batchSize,\n insertFn,\n stats\n );\n }\n\n stats.totalTimeMs = performance.now() - startTime;\n stats.avgBatchTimeMs =\n stats.batchesProcessed > 0\n ? stats.totalTimeMs / stats.batchesProcessed\n : 0;\n\n logger.info(\n `Bulk ${table} insert completed`,\n stats as unknown as Record<string, unknown>\n );\n return stats;\n }\n );\n }\n\n /**\n * Process records in batches\n */\n private async processBatches<T>(\n records: T[],\n batchSize: number,\n processFn: (batch: T[]) => void,\n stats: BatchStats\n ): Promise<void> {\n for (let i = 0; i < records.length; i += batchSize) {\n const batch = records.slice(i, i + batchSize);\n const batchStart = performance.now();\n\n try {\n processFn(batch);\n stats.batchesProcessed++;\n\n const batchTime = performance.now() - batchStart;\n logger.debug('Batch processed', {\n batchNumber: stats.batchesProcessed,\n records: batch.length,\n timeMs: batchTime.toFixed(2),\n });\n\n // Yield control to prevent blocking\n if (stats.batchesProcessed % 10 === 0) {\n await new Promise((resolve) => setImmediate(resolve));\n }\n } catch (error: unknown) {\n stats.failedInserts += batch.length;\n logger.error('Batch processing failed', error as Error, {\n batchNumber: stats.batchesProcessed + 1,\n batchSize: batch.length,\n });\n }\n }\n }\n\n /**\n * Queue batch operation for later processing\n */\n queueBatchOperation(operation: BatchOperation): void {\n this.batchQueue.push(operation);\n\n if (this.batchQueue.length >= 10 && !this.isProcessing) {\n setImmediate(() => this.processBatchQueue());\n }\n }\n\n /**\n * Process queued batch operations\n */\n async processBatchQueue(): Promise<void> {\n if (this.isProcessing || this.batchQueue.length === 0) {\n return;\n }\n\n this.isProcessing = true;\n const operations = [...this.batchQueue];\n this.batchQueue = [];\n\n try {\n const groupedOps = this.groupOperationsByTable(operations);\n\n for (const [table, tableOps] of groupedOps) {\n await this.processTableOperations(table, tableOps);\n }\n\n logger.info('Batch queue processed', {\n operations: operations.length,\n tables: groupedOps.size,\n });\n } catch (error: unknown) {\n logger.error('Batch queue processing failed', error as Error);\n } finally {\n this.isProcessing = false;\n }\n }\n\n /**\n * Flush any remaining queued operations\n */\n async flush(): Promise<void> {\n if (this.batchQueue.length > 0) {\n await this.processBatchQueue();\n }\n }\n\n /**\n * Get SQL conflict clause\n */\n private getConflictClause(onConflict: string): string {\n switch (onConflict) {\n case 'ignore':\n return 'OR IGNORE';\n case 'replace':\n return 'OR REPLACE';\n case 'update':\n return 'ON CONFLICT DO UPDATE SET';\n default:\n return '';\n }\n }\n\n /**\n * Group operations by table for efficient processing\n */\n private groupOperationsByTable(\n operations: BatchOperation[]\n ): Map<string, BatchOperation[]> {\n const grouped = new Map<string, BatchOperation[]>();\n\n for (const op of operations) {\n if (!grouped.has(op.table)) {\n grouped.set(op.table, []);\n }\n grouped.get(op.table)!.push(op);\n }\n\n return grouped;\n }\n\n /**\n * Process all operations for a specific table\n */\n private async processTableOperations(\n table: string,\n operations: BatchOperation[]\n ): Promise<void> {\n for (const op of operations) {\n switch (op.operation) {\n case 'insert':\n await this.performBulkInsert(table, op.data, {\n onConflict: op.onConflict,\n });\n break;\n // Add update and delete operations as needed\n default:\n logger.warn('Unsupported batch operation', {\n table,\n operation: op.operation,\n });\n }\n }\n }\n\n /**\n * Initialize commonly used prepared statements\n */\n private initializePreparedStatements(): void {\n // Event insertion\n this.preparedStatements.set(\n 'insert_event',\n this.db.prepare(`\n INSERT OR IGNORE INTO events \n (event_id, frame_id, run_id, seq, event_type, payload, ts) \n VALUES (?, ?, ?, ?, ?, ?, ?)\n `)\n );\n\n // Anchor insertion\n this.preparedStatements.set(\n 'insert_anchor',\n this.db.prepare(`\n INSERT OR IGNORE INTO anchors \n (anchor_id, frame_id, type, text, priority, metadata, created_at) \n VALUES (?, ?, ?, ?, ?, ?, ?)\n `)\n );\n\n logger.info('Batch operations prepared statements initialized');\n }\n\n /**\n * Cleanup resources\n */\n cleanup(): void {\n // Modern better-sqlite3 automatically handles cleanup\n this.preparedStatements.clear();\n }\n}\n\n// Global batch operations manager\nlet globalBatchManager: BatchOperationsManager | null = null;\n\n/**\n * Get or create global batch operations manager\n */\nexport function getBatchManager(\n db?: Database.Database\n): BatchOperationsManager {\n if (!globalBatchManager) {\n globalBatchManager = new BatchOperationsManager(db);\n }\n return globalBatchManager;\n}\n\n/**\n * Convenience function for bulk event insertion\n */\nexport async function bulkInsertEvents(\n events: any[],\n options?: BulkInsertOptions\n): Promise<BatchStats> {\n const manager = getBatchManager();\n return manager.bulkInsertEvents(events, options);\n}\n\n/**\n * Convenience function for bulk anchor insertion\n */\nexport async function bulkInsertAnchors(\n anchors: any[],\n options?: BulkInsertOptions\n): Promise<BatchStats> {\n const manager = getBatchManager();\n return manager.bulkInsertAnchors(anchors, options);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -113,7 +113,10 @@ class LRUQueryCache {
113
113
  count++;
114
114
  }
115
115
  }
116
- logger.info("Pattern invalidation", { pattern: pattern.source, invalidated: count });
116
+ logger.info("Pattern invalidation", {
117
+ pattern: pattern.source,
118
+ invalidated: count
119
+ });
117
120
  return count;
118
121
  }
119
122
  /**
@@ -131,7 +134,10 @@ class LRUQueryCache {
131
134
  * Get cache contents for debugging
132
135
  */
133
136
  debug() {
134
- return Array.from(this.cache.entries()).map(([key, entry]) => ({ key, entry }));
137
+ return Array.from(this.cache.entries()).map(([key, entry]) => ({
138
+ key,
139
+ entry
140
+ }));
135
141
  }
136
142
  /**
137
143
  * Cleanup expired entries
@@ -149,7 +155,10 @@ class LRUQueryCache {
149
155
  }
150
156
  }
151
157
  if (removed > 0) {
152
- logger.info("Cache cleanup completed", { removed, remaining: this.cache.size });
158
+ logger.info("Cache cleanup completed", {
159
+ removed,
160
+ remaining: this.cache.size
161
+ });
153
162
  }
154
163
  return removed;
155
164
  }
@@ -158,7 +167,7 @@ class StackMemoryQueryCache {
158
167
  frameCache = new LRUQueryCache({ maxSize: 500, ttlMs: 3e5 });
159
168
  // 5 min
160
169
  eventCache = new LRUQueryCache({ maxSize: 1e3, ttlMs: 18e4 });
161
- // 3 min
170
+ // 3 min
162
171
  anchorCache = new LRUQueryCache({ maxSize: 200, ttlMs: 6e5 });
163
172
  // 10 min
164
173
  digestCache = new LRUQueryCache({ maxSize: 100, ttlMs: 9e5 });
@@ -220,7 +229,7 @@ class StackMemoryQueryCache {
220
229
  logger.info("Invalidated frame caches", { frameId });
221
230
  }
222
231
  /**
223
- * Invalidate all caches for a project
232
+ * Invalidate all caches for a project
224
233
  */
225
234
  invalidateProject(projectId) {
226
235
  const pattern = new RegExp(`^(frame|context|events|anchors|digest):.+`);
@@ -229,7 +238,10 @@ class StackMemoryQueryCache {
229
238
  total += this.eventCache.invalidatePattern(pattern);
230
239
  total += this.anchorCache.invalidatePattern(pattern);
231
240
  total += this.digestCache.invalidatePattern(pattern);
232
- logger.info("Invalidated project caches", { projectId, totalInvalidated: total });
241
+ logger.info("Invalidated project caches", {
242
+ projectId,
243
+ totalInvalidated: total
244
+ });
233
245
  }
234
246
  /**
235
247
  * Get comprehensive cache metrics
@@ -270,9 +282,7 @@ function getQueryCache() {
270
282
  return globalCache;
271
283
  }
272
284
  function createCacheKey(queryName, params) {
273
- const paramsStr = params.map(
274
- (p) => typeof p === "object" ? JSON.stringify(p) : String(p)
275
- ).join(":");
285
+ const paramsStr = params.map((p) => typeof p === "object" ? JSON.stringify(p) : String(p)).join(":");
276
286
  return `${queryName}:${paramsStr}`;
277
287
  }
278
288
  export {