@stackmemoryai/stackmemory 0.5.33 → 0.5.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/agents/core/agent-task-manager.js.map +1 -1
  2. package/dist/cli/commands/clear.js +1 -1
  3. package/dist/cli/commands/clear.js.map +1 -1
  4. package/dist/cli/commands/context.js +1 -1
  5. package/dist/cli/commands/context.js.map +1 -1
  6. package/dist/cli/commands/dashboard.js.map +1 -1
  7. package/dist/cli/commands/discovery.js +1 -1
  8. package/dist/cli/commands/discovery.js.map +1 -1
  9. package/dist/cli/commands/handoff.js +1 -1
  10. package/dist/cli/commands/handoff.js.map +1 -1
  11. package/dist/cli/commands/monitor.js +1 -1
  12. package/dist/cli/commands/monitor.js.map +1 -1
  13. package/dist/cli/commands/quality.js +1 -1
  14. package/dist/cli/commands/quality.js.map +1 -1
  15. package/dist/cli/commands/skills.js +1 -1
  16. package/dist/cli/commands/skills.js.map +1 -1
  17. package/dist/cli/commands/workflow.js +1 -1
  18. package/dist/cli/commands/workflow.js.map +1 -1
  19. package/dist/cli/commands/worktree.js +1 -1
  20. package/dist/cli/commands/worktree.js.map +1 -1
  21. package/dist/cli/index.js +1 -1
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/core/context/auto-context.js.map +1 -1
  24. package/dist/core/context/compaction-handler.js.map +2 -2
  25. package/dist/core/context/context-bridge.js.map +2 -2
  26. package/dist/core/context/dual-stack-manager.js +1 -1
  27. package/dist/core/context/dual-stack-manager.js.map +1 -1
  28. package/dist/core/context/enhanced-rehydration.js.map +1 -1
  29. package/dist/core/context/frame-database.js +43 -10
  30. package/dist/core/context/frame-database.js.map +2 -2
  31. package/dist/core/context/frame-handoff-manager.js.map +1 -1
  32. package/dist/core/context/frame-lifecycle-hooks.js +119 -0
  33. package/dist/core/context/frame-lifecycle-hooks.js.map +7 -0
  34. package/dist/core/context/frame-stack.js +36 -7
  35. package/dist/core/context/frame-stack.js.map +2 -2
  36. package/dist/core/context/incremental-gc.js.map +2 -2
  37. package/dist/core/context/index.js +4 -22
  38. package/dist/core/context/index.js.map +2 -2
  39. package/dist/core/context/refactored-frame-manager.js +170 -37
  40. package/dist/core/context/refactored-frame-manager.js.map +3 -3
  41. package/dist/core/context/shared-context-layer.js.map +1 -1
  42. package/dist/core/context/stack-merge-resolver.js.map +1 -1
  43. package/dist/core/database/database-adapter.js.map +1 -1
  44. package/dist/core/database/paradedb-adapter.js.map +1 -1
  45. package/dist/core/database/query-router.js.map +1 -1
  46. package/dist/core/database/sqlite-adapter.js.map +1 -1
  47. package/dist/core/digest/frame-digest-integration.js.map +1 -1
  48. package/dist/core/digest/hybrid-digest-generator.js.map +1 -1
  49. package/dist/core/digest/types.js.map +1 -1
  50. package/dist/core/errors/index.js +249 -0
  51. package/dist/core/errors/index.js.map +2 -2
  52. package/dist/core/frame/workflow-templates.js.map +2 -2
  53. package/dist/core/merge/conflict-detector.js.map +1 -1
  54. package/dist/core/merge/resolution-engine.js.map +1 -1
  55. package/dist/core/merge/stack-diff.js.map +1 -1
  56. package/dist/core/models/model-router.js +10 -1
  57. package/dist/core/models/model-router.js.map +2 -2
  58. package/dist/core/monitoring/error-handler.js +37 -270
  59. package/dist/core/monitoring/error-handler.js.map +3 -3
  60. package/dist/core/monitoring/session-monitor.js.map +1 -1
  61. package/dist/core/performance/lazy-context-loader.js.map +1 -1
  62. package/dist/core/performance/optimized-frame-context.js.map +1 -1
  63. package/dist/core/retrieval/context-retriever.js.map +1 -1
  64. package/dist/core/retrieval/graph-retrieval.js.map +1 -1
  65. package/dist/core/retrieval/hierarchical-retrieval.js.map +1 -1
  66. package/dist/core/retrieval/llm-context-retrieval.js.map +1 -1
  67. package/dist/core/retrieval/retrieval-benchmarks.js.map +1 -1
  68. package/dist/core/retrieval/summary-generator.js.map +1 -1
  69. package/dist/core/retrieval/types.js.map +1 -1
  70. package/dist/core/storage/chromadb-adapter.js.map +1 -1
  71. package/dist/core/storage/infinite-storage.js.map +1 -1
  72. package/dist/core/storage/two-tier-storage.js.map +1 -1
  73. package/dist/features/tasks/task-aware-context.js.map +1 -1
  74. package/dist/features/web/server/index.js +1 -1
  75. package/dist/features/web/server/index.js.map +1 -1
  76. package/dist/hooks/schemas.js +50 -0
  77. package/dist/hooks/schemas.js.map +2 -2
  78. package/dist/hooks/sms-action-runner.js +47 -1
  79. package/dist/hooks/sms-action-runner.js.map +2 -2
  80. package/dist/hooks/sms-notify.js +63 -1
  81. package/dist/hooks/sms-notify.js.map +2 -2
  82. package/dist/hooks/sms-webhook.js +10 -3
  83. package/dist/hooks/sms-webhook.js.map +2 -2
  84. package/dist/hooks/whatsapp-commands.js +172 -69
  85. package/dist/hooks/whatsapp-commands.js.map +2 -2
  86. package/dist/hooks/whatsapp-sync.js +34 -0
  87. package/dist/hooks/whatsapp-sync.js.map +2 -2
  88. package/dist/index.js +1 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/integrations/mcp/handlers/context-handlers.js.map +1 -1
  91. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +1 -1
  92. package/dist/integrations/mcp/server.js +1 -1
  93. package/dist/integrations/mcp/server.js.map +1 -1
  94. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +1 -1
  95. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +1 -1
  96. package/dist/integrations/ralph/context/stackmemory-context-loader.js +1 -1
  97. package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +1 -1
  98. package/dist/integrations/ralph/learning/pattern-learner.js +1 -1
  99. package/dist/integrations/ralph/learning/pattern-learner.js.map +1 -1
  100. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +1 -1
  101. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +1 -1
  102. package/dist/integrations/ralph/swarm/swarm-coordinator.js +1 -1
  103. package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +1 -1
  104. package/dist/integrations/ralph/visualization/ralph-debugger.js +1 -1
  105. package/dist/integrations/ralph/visualization/ralph-debugger.js.map +1 -1
  106. package/dist/mcp/stackmemory-mcp-server.js +1 -1
  107. package/dist/mcp/stackmemory-mcp-server.js.map +1 -1
  108. package/dist/skills/claude-skills.js.map +1 -1
  109. package/dist/skills/recursive-agent-orchestrator.js.map +1 -1
  110. package/dist/skills/unified-rlm-orchestrator.js.map +1 -1
  111. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/context/frame-stack.ts"],
4
- "sourcesContent": ["/**\n * Frame Stack Management\n * Handles the call stack of active frames\n */\n\nimport { Frame, FrameContext, FrameType } from './frame-types.js';\nimport { FrameDatabase } from './frame-database.js';\nimport { logger } from '../monitoring/logger.js';\nimport { FrameError, ErrorCode } from '../errors/index.js';\n\nexport class FrameStack {\n private activeStack: string[] = [];\n\n constructor(\n private frameDb: FrameDatabase,\n private projectId: string,\n private runId: string\n ) {}\n\n /**\n * Initialize stack by loading active frames\n */\n async initialize(): Promise<void> {\n try {\n const activeFrames = this.frameDb.getFramesByProject(\n this.projectId,\n 'active'\n );\n\n // Rebuild stack from database\n this.activeStack = this.buildStackFromFrames(activeFrames);\n\n logger.info('Frame stack initialized', {\n stackDepth: this.activeStack.length,\n projectId: this.projectId,\n });\n } catch (error: unknown) {\n logger.error('Failed to initialize frame stack', {\n error: error instanceof Error ? error.message : String(error),\n projectId: this.projectId,\n runId: this.runId,\n });\n throw new FrameError(\n 'Failed to initialize frame stack',\n ErrorCode.FRAME_INIT_FAILED,\n {\n projectId: this.projectId,\n runId: this.runId,\n originalError: error instanceof Error ? error.message : String(error),\n }\n );\n }\n }\n\n /**\n * Push new frame onto stack\n */\n pushFrame(frameId: string): void {\n if (this.activeStack.includes(frameId)) {\n logger.warn('Frame already on stack', { frameId });\n return;\n }\n\n this.activeStack.push(frameId);\n\n logger.debug('Pushed frame to stack', {\n frameId,\n stackDepth: this.activeStack.length,\n });\n }\n\n /**\n * Pop frame from stack\n */\n popFrame(frameId?: string): string | undefined {\n if (this.activeStack.length === 0) {\n return undefined;\n }\n\n let poppedFrameId: string | undefined;\n\n if (frameId) {\n // Pop specific frame (and all frames above it)\n const index = this.activeStack.indexOf(frameId);\n if (index === -1) {\n logger.warn('Frame not found on stack', { frameId });\n return undefined;\n }\n\n // Remove the target frame and all frames above it\n const removed = this.activeStack.splice(index);\n poppedFrameId = removed[0];\n\n if (removed.length > 1) {\n logger.info('Popped multiple frames due to stack unwinding', {\n targetFrame: frameId,\n removedFrames: removed,\n });\n }\n } else {\n // Pop top frame\n poppedFrameId = this.activeStack.pop();\n }\n\n if (poppedFrameId) {\n logger.debug('Popped frame from stack', {\n frameId: poppedFrameId,\n stackDepth: this.activeStack.length,\n });\n }\n\n return poppedFrameId;\n }\n\n /**\n * Get current (top) frame ID\n */\n getCurrentFrameId(): string | undefined {\n return this.activeStack[this.activeStack.length - 1];\n }\n\n /**\n * Get stack depth\n */\n getDepth(): number {\n return this.activeStack.length;\n }\n\n /**\n * Get complete stack\n */\n getStack(): string[] {\n return [...this.activeStack];\n }\n\n /**\n * Get stack as frame objects\n */\n getStackFrames(): Frame[] {\n return this.activeStack\n .map((frameId: any) => this.frameDb.getFrame(frameId))\n .filter(Boolean) as Frame[];\n }\n\n /**\n * Get frame context for the hot stack\n */\n getHotStackContext(maxEvents: number = 20): FrameContext[] {\n return this.activeStack\n .map((frameId: any) => this.buildFrameContext(frameId, maxEvents))\n .filter(Boolean) as FrameContext[];\n }\n\n /**\n * Check if frame is on stack\n */\n isFrameActive(frameId: string): boolean {\n return this.activeStack.includes(frameId);\n }\n\n /**\n * Get parent frame ID for current frame\n */\n getParentFrameId(): string | undefined {\n if (this.activeStack.length < 2) {\n return undefined;\n }\n return this.activeStack[this.activeStack.length - 2];\n }\n\n /**\n * Get frame depth on stack (0-based)\n */\n getFrameStackDepth(frameId: string): number {\n return this.activeStack.indexOf(frameId);\n }\n\n /**\n * Clear entire stack\n */\n clear(): void {\n const previousDepth = this.activeStack.length;\n this.activeStack = [];\n\n logger.info('Cleared frame stack', { previousDepth });\n }\n\n /**\n * Validate stack consistency\n */\n validateStack(): { isValid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check if all frames in stack exist and are active\n for (const frameId of this.activeStack) {\n const frame = this.frameDb.getFrame(frameId);\n\n if (!frame) {\n errors.push(`Frame not found in database: ${frameId}`);\n continue;\n }\n\n if (frame.state !== 'active') {\n errors.push(\n `Frame on stack is not active: ${frameId} (state: ${frame.state})`\n );\n }\n\n if (frame.project_id !== this.projectId) {\n errors.push(`Frame belongs to different project: ${frameId}`);\n }\n }\n\n // Check for parent-child consistency\n for (let i = 1; i < this.activeStack.length; i++) {\n const currentFrameId = this.activeStack[i];\n const expectedParentId = this.activeStack[i - 1];\n const currentFrame = this.frameDb.getFrame(currentFrameId);\n\n if (currentFrame?.parent_frame_id !== expectedParentId) {\n errors.push(\n `Frame parent mismatch: ${currentFrameId} parent should be ${expectedParentId} but is ${currentFrame?.parent_frame_id}`\n );\n }\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n }\n\n /**\n * Build frame context for a specific frame\n */\n private buildFrameContext(\n frameId: string,\n maxEvents: number\n ): FrameContext | null {\n try {\n const frame = this.frameDb.getFrame(frameId);\n if (!frame) {\n logger.warn('Frame not found for context building', { frameId });\n return null;\n }\n\n const anchors = this.frameDb.getFrameAnchors(frameId);\n const recentEvents = this.frameDb.getFrameEvents(frameId, maxEvents);\n const activeArtifacts = this.extractActiveArtifacts(recentEvents);\n\n return {\n frameId,\n header: {\n goal: frame.name,\n constraints: this.extractConstraints(frame.inputs),\n definitions: frame.inputs.definitions,\n },\n anchors,\n recentEvents,\n activeArtifacts,\n };\n } catch (error: unknown) {\n logger.warn('Failed to build frame context', { frameId, error });\n return null;\n }\n }\n\n /**\n * Extract constraints from frame inputs\n */\n private extractConstraints(inputs: Record<string, any>): string[] {\n const constraints: string[] = [];\n\n if (inputs.constraints && Array.isArray(inputs.constraints)) {\n constraints.push(...inputs.constraints);\n }\n\n return constraints;\n }\n\n /**\n * Extract active artifacts from events\n */\n private extractActiveArtifacts(events: any[]): string[] {\n const artifacts: string[] = [];\n\n for (const event of events) {\n if (event.event_type === 'artifact' && event.payload?.path) {\n artifacts.push(event.payload.path);\n }\n }\n\n // Return unique artifacts\n return [...new Set(artifacts)];\n }\n\n /**\n * Build stack order from database frames\n */\n private buildStackFromFrames(frames: Frame[]): string[] {\n if (frames.length === 0) {\n return [];\n }\n\n // Create parent-child map\n const parentMap = new Map<string, string>();\n const frameMap = new Map<string, Frame>();\n\n for (const frame of frames) {\n frameMap.set(frame.frame_id, frame);\n if (frame.parent_frame_id) {\n parentMap.set(frame.frame_id, frame.parent_frame_id);\n }\n }\n\n // Find root frames (no parent or parent not in active set)\n const rootFrames = frames.filter(\n (f: any) => !f.parent_frame_id || !frameMap.has(f.parent_frame_id)\n );\n\n if (rootFrames.length === 0) {\n logger.warn('No root frames found in active set');\n return [];\n }\n\n if (rootFrames.length > 1) {\n logger.warn('Multiple root frames found, using most recent', {\n rootFrames: rootFrames.map((f: any) => f.frame_id),\n });\n }\n\n // Build stack from root to leaves\n const stack: string[] = [];\n let currentFrame = rootFrames.sort(\n (a, b) => a.created_at - b.created_at\n )[0];\n\n while (currentFrame) {\n stack.push(currentFrame.frame_id);\n\n // Find child frame\n const childFrame = frames.find(\n (f: any) => f.parent_frame_id === currentFrame.frame_id\n );\n if (childFrame) {\n currentFrame = childFrame;\n } else {\n break;\n }\n }\n\n return stack;\n }\n}\n"],
5
- "mappings": ";;;;AAOA,SAAS,cAAc;AACvB,SAAS,YAAY,iBAAiB;AAE/B,MAAM,WAAW;AAAA,EAGtB,YACU,SACA,WACA,OACR;AAHQ;AACA;AACA;AAAA,EACP;AAAA,EANK,cAAwB,CAAC;AAAA;AAAA;AAAA;AAAA,EAWjC,MAAM,aAA4B;AAChC,QAAI;AACF,YAAM,eAAe,KAAK,QAAQ;AAAA,QAChC,KAAK;AAAA,QACL;AAAA,MACF;AAGA,WAAK,cAAc,KAAK,qBAAqB,YAAY;AAEzD,aAAO,KAAK,2BAA2B;AAAA,QACrC,YAAY,KAAK,YAAY;AAAA,QAC7B,WAAW,KAAK;AAAA,MAClB,CAAC;AAAA,IACH,SAAS,OAAgB;AACvB,aAAO,MAAM,oCAAoC;AAAA,QAC/C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAC5D,WAAW,KAAK;AAAA,QAChB,OAAO,KAAK;AAAA,MACd,CAAC;AACD,YAAM,IAAI;AAAA,QACR;AAAA,QACA,UAAU;AAAA,QACV;AAAA,UACE,WAAW,KAAK;AAAA,UAChB,OAAO,KAAK;AAAA,UACZ,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAuB;AAC/B,QAAI,KAAK,YAAY,SAAS,OAAO,GAAG;AACtC,aAAO,KAAK,0BAA0B,EAAE,QAAQ,CAAC;AACjD;AAAA,IACF;AAEA,SAAK,YAAY,KAAK,OAAO;AAE7B,WAAO,MAAM,yBAAyB;AAAA,MACpC;AAAA,MACA,YAAY,KAAK,YAAY;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,SAAsC;AAC7C,QAAI,KAAK,YAAY,WAAW,GAAG;AACjC,aAAO;AAAA,IACT;AAEA,QAAI;AAEJ,QAAI,SAAS;AAEX,YAAM,QAAQ,KAAK,YAAY,QAAQ,OAAO;AAC9C,UAAI,UAAU,IAAI;AAChB,eAAO,KAAK,4BAA4B,EAAE,QAAQ,CAAC;AACnD,eAAO;AAAA,MACT;AAGA,YAAM,UAAU,KAAK,YAAY,OAAO,KAAK;AAC7C,sBAAgB,QAAQ,CAAC;AAEzB,UAAI,QAAQ,SAAS,GAAG;AACtB,eAAO,KAAK,iDAAiD;AAAA,UAC3D,aAAa;AAAA,UACb,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AAEL,sBAAgB,KAAK,YAAY,IAAI;AAAA,IACvC;AAEA,QAAI,eAAe;AACjB,aAAO,MAAM,2BAA2B;AAAA,QACtC,SAAS;AAAA,QACT,YAAY,KAAK,YAAY;AAAA,MAC/B,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAwC;AACtC,WAAO,KAAK,YAAY,KAAK,YAAY,SAAS,CAAC;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAmB;AACjB,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,WAAqB;AACnB,WAAO,CAAC,GAAG,KAAK,WAAW;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,iBAA0B;AACxB,WAAO,KAAK,YACT,IAAI,CAAC,YAAiB,KAAK,QAAQ,SAAS,OAAO,CAAC,EACpD,OAAO,OAAO;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,YAAoB,IAAoB;AACzD,WAAO,KAAK,YACT,IAAI,CAAC,YAAiB,KAAK,kBAAkB,SAAS,SAAS,CAAC,EAChE,OAAO,OAAO;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,SAA0B;AACtC,WAAO,KAAK,YAAY,SAAS,OAAO;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAuC;AACrC,QAAI,KAAK,YAAY,SAAS,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,WAAO,KAAK,YAAY,KAAK,YAAY,SAAS,CAAC;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,SAAyB;AAC1C,WAAO,KAAK,YAAY,QAAQ,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,UAAM,gBAAgB,KAAK,YAAY;AACvC,SAAK,cAAc,CAAC;AAEpB,WAAO,KAAK,uBAAuB,EAAE,cAAc,CAAC;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAwD;AACtD,UAAM,SAAmB,CAAC;AAG1B,eAAW,WAAW,KAAK,aAAa;AACtC,YAAM,QAAQ,KAAK,QAAQ,SAAS,OAAO;AAE3C,UAAI,CAAC,OAAO;AACV,eAAO,KAAK,gCAAgC,OAAO,EAAE;AACrD;AAAA,MACF;AAEA,UAAI,MAAM,UAAU,UAAU;AAC5B,eAAO;AAAA,UACL,iCAAiC,OAAO,YAAY,MAAM,KAAK;AAAA,QACjE;AAAA,MACF;AAEA,UAAI,MAAM,eAAe,KAAK,WAAW;AACvC,eAAO,KAAK,uCAAuC,OAAO,EAAE;AAAA,MAC9D;AAAA,IACF;AAGA,aAAS,IAAI,GAAG,IAAI,KAAK,YAAY,QAAQ,KAAK;AAChD,YAAM,iBAAiB,KAAK,YAAY,CAAC;AACzC,YAAM,mBAAmB,KAAK,YAAY,IAAI,CAAC;AAC/C,YAAM,eAAe,KAAK,QAAQ,SAAS,cAAc;AAEzD,UAAI,cAAc,oBAAoB,kBAAkB;AACtD,eAAO;AAAA,UACL,0BAA0B,cAAc,qBAAqB,gBAAgB,WAAW,cAAc,eAAe;AAAA,QACvH;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS,OAAO,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBACN,SACA,WACqB;AACrB,QAAI;AACF,YAAM,QAAQ,KAAK,QAAQ,SAAS,OAAO;AAC3C,UAAI,CAAC,OAAO;AACV,eAAO,KAAK,wCAAwC,EAAE,QAAQ,CAAC;AAC/D,eAAO;AAAA,MACT;AAEA,YAAM,UAAU,KAAK,QAAQ,gBAAgB,OAAO;AACpD,YAAM,eAAe,KAAK,QAAQ,eAAe,SAAS,SAAS;AACnE,YAAM,kBAAkB,KAAK,uBAAuB,YAAY;AAEhE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,MAAM,MAAM;AAAA,UACZ,aAAa,KAAK,mBAAmB,MAAM,MAAM;AAAA,UACjD,aAAa,MAAM,OAAO;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AACvB,aAAO,KAAK,iCAAiC,EAAE,SAAS,MAAM,CAAC;AAC/D,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,QAAuC;AAChE,UAAM,cAAwB,CAAC;AAE/B,QAAI,OAAO,eAAe,MAAM,QAAQ,OAAO,WAAW,GAAG;AAC3D,kBAAY,KAAK,GAAG,OAAO,WAAW;AAAA,IACxC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,uBAAuB,QAAyB;AACtD,UAAM,YAAsB,CAAC;AAE7B,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,eAAe,cAAc,MAAM,SAAS,MAAM;AAC1D,kBAAU,KAAK,MAAM,QAAQ,IAAI;AAAA,MACnC;AAAA,IACF;AAGA,WAAO,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAqB,QAA2B;AACtD,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,YAAY,oBAAI,IAAoB;AAC1C,UAAM,WAAW,oBAAI,IAAmB;AAExC,eAAW,SAAS,QAAQ;AAC1B,eAAS,IAAI,MAAM,UAAU,KAAK;AAClC,UAAI,MAAM,iBAAiB;AACzB,kBAAU,IAAI,MAAM,UAAU,MAAM,eAAe;AAAA,MACrD;AAAA,IACF;AAGA,UAAM,aAAa,OAAO;AAAA,MACxB,CAAC,MAAW,CAAC,EAAE,mBAAmB,CAAC,SAAS,IAAI,EAAE,eAAe;AAAA,IACnE;AAEA,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO,KAAK,oCAAoC;AAChD,aAAO,CAAC;AAAA,IACV;AAEA,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,KAAK,iDAAiD;AAAA,QAC3D,YAAY,WAAW,IAAI,CAAC,MAAW,EAAE,QAAQ;AAAA,MACnD,CAAC;AAAA,IACH;AAGA,UAAM,QAAkB,CAAC;AACzB,QAAI,eAAe,WAAW;AAAA,MAC5B,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE;AAAA,IAC7B,EAAE,CAAC;AAEH,WAAO,cAAc;AACnB,YAAM,KAAK,aAAa,QAAQ;AAGhC,YAAM,aAAa,OAAO;AAAA,QACxB,CAAC,MAAW,EAAE,oBAAoB,aAAa;AAAA,MACjD;AACA,UAAI,YAAY;AACd,uBAAe;AAAA,MACjB,OAAO;AACL;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["/**\n * Frame Stack Management\n * Handles the call stack of active frames\n */\n\nimport { Frame, FrameContext, FrameType } from './frame-types.js';\nimport { FrameDatabase } from './frame-database.js';\nimport { logger } from '../monitoring/logger.js';\nimport { FrameError, ErrorCode } from '../errors/index.js';\nimport { FrameQueryMode } from '../session/index.js';\n\nexport class FrameStack {\n private activeStack: string[] = [];\n private queryMode: FrameQueryMode = FrameQueryMode.PROJECT_ACTIVE;\n\n constructor(\n private frameDb: FrameDatabase,\n private projectId: string,\n private runId: string\n ) {}\n\n /**\n * Initialize stack by loading active frames\n */\n async initialize(): Promise<void> {\n try {\n const activeFrames = this.frameDb.getFramesByProject(\n this.projectId,\n 'active'\n );\n\n // Rebuild stack from database\n this.activeStack = this.buildStackFromFrames(activeFrames);\n\n logger.info('Frame stack initialized', {\n stackDepth: this.activeStack.length,\n projectId: this.projectId,\n });\n } catch (error: unknown) {\n logger.error('Failed to initialize frame stack', {\n error: error instanceof Error ? error.message : String(error),\n projectId: this.projectId,\n runId: this.runId,\n });\n throw new FrameError(\n 'Failed to initialize frame stack',\n ErrorCode.FRAME_INIT_FAILED,\n {\n projectId: this.projectId,\n runId: this.runId,\n originalError: error instanceof Error ? error.message : String(error),\n }\n );\n }\n }\n\n /**\n * Push new frame onto stack\n */\n pushFrame(frameId: string): void {\n if (this.activeStack.includes(frameId)) {\n logger.warn('Frame already on stack', { frameId });\n return;\n }\n\n this.activeStack.push(frameId);\n\n logger.debug('Pushed frame to stack', {\n frameId,\n stackDepth: this.activeStack.length,\n });\n }\n\n /**\n * Pop frame from stack\n */\n popFrame(frameId?: string): string | undefined {\n if (this.activeStack.length === 0) {\n return undefined;\n }\n\n let poppedFrameId: string | undefined;\n\n if (frameId) {\n // Pop specific frame (and all frames above it)\n const index = this.activeStack.indexOf(frameId);\n if (index === -1) {\n logger.warn('Frame not found on stack', { frameId });\n return undefined;\n }\n\n // Remove the target frame and all frames above it\n const removed = this.activeStack.splice(index);\n poppedFrameId = removed[0];\n\n if (removed.length > 1) {\n logger.info('Popped multiple frames due to stack unwinding', {\n targetFrame: frameId,\n removedFrames: removed,\n });\n }\n } else {\n // Pop top frame\n poppedFrameId = this.activeStack.pop();\n }\n\n if (poppedFrameId) {\n logger.debug('Popped frame from stack', {\n frameId: poppedFrameId,\n stackDepth: this.activeStack.length,\n });\n }\n\n return poppedFrameId;\n }\n\n /**\n * Get current (top) frame ID\n */\n getCurrentFrameId(): string | undefined {\n return this.activeStack[this.activeStack.length - 1];\n }\n\n /**\n * Get stack depth\n */\n getDepth(): number {\n return this.activeStack.length;\n }\n\n /**\n * Get complete stack\n */\n getStack(): string[] {\n return [...this.activeStack];\n }\n\n /**\n * Get stack as frame objects\n */\n getStackFrames(): Frame[] {\n return this.activeStack\n .map((frameId) => this.frameDb.getFrame(frameId))\n .filter((f): f is Frame => f !== undefined);\n }\n\n /**\n * Get frame context for the hot stack\n */\n getHotStackContext(maxEvents: number = 20): FrameContext[] {\n return this.activeStack\n .map((frameId) => this.buildFrameContext(frameId, maxEvents))\n .filter((ctx): ctx is FrameContext => ctx !== null);\n }\n\n /**\n * Check if frame is on stack\n */\n isFrameActive(frameId: string): boolean {\n return this.activeStack.includes(frameId);\n }\n\n /**\n * Get parent frame ID for current frame\n */\n getParentFrameId(): string | undefined {\n if (this.activeStack.length < 2) {\n return undefined;\n }\n return this.activeStack[this.activeStack.length - 2];\n }\n\n /**\n * Get frame depth on stack (0-based)\n */\n getFrameStackDepth(frameId: string): number {\n return this.activeStack.indexOf(frameId);\n }\n\n /**\n * Clear entire stack\n */\n clear(): void {\n const previousDepth = this.activeStack.length;\n this.activeStack = [];\n\n logger.info('Cleared frame stack', { previousDepth });\n }\n\n /**\n * Set query mode and reinitialize stack\n */\n setQueryMode(mode: FrameQueryMode): void {\n this.queryMode = mode;\n // Reinitialize with new query mode\n this.initialize().catch((error) => {\n logger.warn('Failed to reinitialize stack with new query mode', {\n mode,\n error,\n });\n });\n }\n\n /**\n * Remove a specific frame from the stack without popping frames above it\n */\n removeFrame(frameId: string): boolean {\n const index = this.activeStack.indexOf(frameId);\n if (index === -1) {\n return false;\n }\n\n this.activeStack.splice(index, 1);\n\n logger.debug('Removed frame from stack', {\n frameId,\n stackDepth: this.activeStack.length,\n });\n\n return true;\n }\n\n /**\n * Validate stack consistency\n */\n validateStack(): { isValid: boolean; errors: string[] } {\n const errors: string[] = [];\n\n // Check if all frames in stack exist and are active\n for (const frameId of this.activeStack) {\n const frame = this.frameDb.getFrame(frameId);\n\n if (!frame) {\n errors.push(`Frame not found in database: ${frameId}`);\n continue;\n }\n\n if (frame.state !== 'active') {\n errors.push(\n `Frame on stack is not active: ${frameId} (state: ${frame.state})`\n );\n }\n\n if (frame.project_id !== this.projectId) {\n errors.push(`Frame belongs to different project: ${frameId}`);\n }\n }\n\n // Check for parent-child consistency\n for (let i = 1; i < this.activeStack.length; i++) {\n const currentFrameId = this.activeStack[i];\n const expectedParentId = this.activeStack[i - 1];\n const currentFrame = this.frameDb.getFrame(currentFrameId);\n\n if (currentFrame?.parent_frame_id !== expectedParentId) {\n errors.push(\n `Frame parent mismatch: ${currentFrameId} parent should be ${expectedParentId} but is ${currentFrame?.parent_frame_id}`\n );\n }\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n }\n\n /**\n * Build frame context for a specific frame\n */\n private buildFrameContext(\n frameId: string,\n maxEvents: number\n ): FrameContext | null {\n try {\n const frame = this.frameDb.getFrame(frameId);\n if (!frame) {\n logger.warn('Frame not found for context building', { frameId });\n return null;\n }\n\n const anchors = this.frameDb.getFrameAnchors(frameId);\n const recentEvents = this.frameDb.getFrameEvents(frameId, maxEvents);\n const activeArtifacts = this.extractActiveArtifacts(recentEvents);\n\n return {\n frameId,\n header: {\n goal: frame.name,\n constraints: this.extractConstraints(frame.inputs),\n definitions: frame.inputs.definitions,\n },\n anchors,\n recentEvents,\n activeArtifacts,\n };\n } catch (error: unknown) {\n logger.warn('Failed to build frame context', { frameId, error });\n return null;\n }\n }\n\n /**\n * Extract constraints from frame inputs\n */\n private extractConstraints(inputs: Record<string, unknown>): string[] {\n const constraints: string[] = [];\n\n if (inputs.constraints && Array.isArray(inputs.constraints)) {\n constraints.push(...(inputs.constraints as string[]));\n }\n\n return constraints;\n }\n\n /**\n * Extract active artifacts from events\n */\n private extractActiveArtifacts(events: Event[]): string[] {\n const artifacts: string[] = [];\n\n for (const event of events) {\n const payload = event.payload as Record<string, unknown>;\n if (event.event_type === 'artifact' && payload?.path) {\n artifacts.push(payload.path as string);\n }\n }\n\n // Return unique artifacts\n return [...new Set(artifacts)];\n }\n\n /**\n * Build stack order from database frames\n */\n private buildStackFromFrames(frames: Frame[]): string[] {\n if (frames.length === 0) {\n return [];\n }\n\n // Create parent-child map\n const parentMap = new Map<string, string>();\n const frameMap = new Map<string, Frame>();\n\n for (const frame of frames) {\n frameMap.set(frame.frame_id, frame);\n if (frame.parent_frame_id) {\n parentMap.set(frame.frame_id, frame.parent_frame_id);\n }\n }\n\n // Find root frames (no parent or parent not in active set)\n const rootFrames = frames.filter(\n (f) => !f.parent_frame_id || !frameMap.has(f.parent_frame_id)\n );\n\n if (rootFrames.length === 0) {\n logger.warn('No root frames found in active set');\n return [];\n }\n\n if (rootFrames.length > 1) {\n logger.warn('Multiple root frames found, using most recent', {\n rootFrames: rootFrames.map((f) => f.frame_id),\n });\n }\n\n // Build stack from root to leaves\n const stack: string[] = [];\n let currentFrame: Frame | undefined = rootFrames.sort(\n (a, b) => a.created_at - b.created_at\n )[0];\n\n while (currentFrame) {\n stack.push(currentFrame.frame_id);\n\n // Find child frame\n const parentId = currentFrame.frame_id;\n const childFrame = frames.find((f) => f.parent_frame_id === parentId);\n if (childFrame) {\n currentFrame = childFrame;\n } else {\n break;\n }\n }\n\n return stack;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/context/incremental-gc.ts"],
4
- "sourcesContent": ["/**\n * Incremental Garbage Collection System (STA-288)\n *\n * Implements incremental GC strategy to avoid stop-the-world pauses\n * with generational aging and priority-based collection.\n */\n\nimport { Frame, FrameManager } from './frame-manager.js';\nimport { Logger } from '../monitoring/logger.js';\n\ninterface GCConfig {\n framesPerCycle: number; // Process in chunks (default: 100)\n cycleInterval: number; // Every minute (default: 60s)\n maxAge: number; // Max age before eligible for collection (30 days)\n generations: {\n young: number; // < 1 day\n mature: number; // 1-7 days\n old: number; // 7-30 days\n };\n}\n\ninterface GCStats {\n totalFrames: number;\n collectedFrames: number;\n lastRunTime: number;\n cycleCount: number;\n avgCycleTime: number;\n protectedFrames: number;\n}\n\nexport class IncrementalGarbageCollector {\n private logger: Logger;\n private config: GCConfig;\n private stats: GCStats;\n private isRunning: boolean = false;\n private cycleTimer: NodeJS.Timer | null = null;\n private frameManager: FrameManager;\n\n constructor(frameManager: FrameManager, config: Partial<GCConfig> = {}) {\n this.frameManager = frameManager;\n this.logger = new Logger('IncrementalGC');\n\n this.config = {\n framesPerCycle: config.framesPerCycle || 100,\n cycleInterval: config.cycleInterval || 60000, // 60 seconds\n maxAge: config.maxAge || 30 * 24 * 60 * 60 * 1000, // 30 days\n generations: {\n young: config.generations?.young || 24 * 60 * 60 * 1000, // 1 day\n mature: config.generations?.mature || 7 * 24 * 60 * 60 * 1000, // 7 days\n old: config.generations?.old || 30 * 24 * 60 * 60 * 1000, // 30 days\n },\n };\n\n this.stats = {\n totalFrames: 0,\n collectedFrames: 0,\n lastRunTime: 0,\n cycleCount: 0,\n avgCycleTime: 0,\n protectedFrames: 0,\n };\n\n this.logger.info('Incremental GC initialized', this.config);\n }\n\n /**\n * Start the garbage collection cycle\n */\n start(): void {\n if (this.isRunning) {\n this.logger.warn('GC already running');\n return;\n }\n\n this.isRunning = true;\n this.logger.info(\n `Starting incremental GC with ${this.config.cycleInterval}ms intervals`\n );\n\n this.cycleTimer = setInterval(() => {\n this.runCycle().catch((error) => {\n this.logger.error('GC cycle failed', error);\n });\n }, this.config.cycleInterval);\n }\n\n /**\n * Stop the garbage collection cycle\n */\n stop(): void {\n if (this.cycleTimer) {\n clearInterval(this.cycleTimer);\n this.cycleTimer = null;\n }\n this.isRunning = false;\n this.logger.info('Incremental GC stopped');\n }\n\n /**\n * Run a single GC cycle\n */\n async runCycle(): Promise<void> {\n const startTime = Date.now();\n this.logger.debug('Starting GC cycle', {\n cycle: this.stats.cycleCount + 1,\n });\n\n try {\n // Get all frames for analysis\n const allFrames = await this.frameManager.getAllFrames();\n this.stats.totalFrames = allFrames.length;\n\n if (allFrames.length === 0) {\n this.logger.debug('No frames to collect');\n return;\n }\n\n // Categorize frames by generation and protection status\n const categorized = this.categorizeFrames(allFrames);\n\n // Select candidates for collection (prioritized)\n const candidates = this.selectCollectionCandidates(categorized);\n\n // Process in chunks to avoid blocking\n const collected = await this.collectFramesIncremental(candidates);\n\n // Update statistics\n this.updateStats(startTime, collected.length);\n\n this.logger.info('GC cycle completed', {\n cycle: this.stats.cycleCount,\n collected: collected.length,\n protected: this.stats.protectedFrames,\n duration: Date.now() - startTime,\n });\n } catch (error: unknown) {\n this.logger.error('GC cycle error', error);\n }\n }\n\n /**\n * Categorize frames by generation and protection status\n */\n private categorizeFrames(frames: Frame[]): {\n young: Frame[];\n mature: Frame[];\n old: Frame[];\n protected: Frame[];\n } {\n const now = Date.now();\n const categories = { young: [], mature: [], old: [], protected: [] };\n\n for (const frame of frames) {\n const age = now - frame.created_at;\n\n // Check if frame is protected\n if (this.isProtected(frame)) {\n categories.protected.push(frame);\n continue;\n }\n\n // Categorize by age\n if (age < this.config.generations.young) {\n categories.young.push(frame);\n } else if (age < this.config.generations.mature) {\n categories.mature.push(frame);\n } else {\n categories.old.push(frame);\n }\n }\n\n this.stats.protectedFrames = categories.protected.length;\n\n this.logger.debug('Frame categorization', {\n young: categories.young.length,\n mature: categories.mature.length,\n old: categories.old.length,\n protected: categories.protected.length,\n });\n\n return categories;\n }\n\n /**\n * Check if a frame should be protected from collection\n */\n private isProtected(frame: Frame): boolean {\n // Protect current session/run frames\n const currentRunId = this.frameManager.getCurrentRunId?.();\n if (frame.run_id === currentRunId) {\n return true;\n }\n\n // Protect active frames\n if (frame.state === 'active') {\n return true;\n }\n\n // Protect recent frames (< 1 hour old)\n const recentThreshold = Date.now() - 60 * 60 * 1000; // 1 hour\n if (frame.created_at > recentThreshold) {\n return true;\n }\n\n // Protect frames with important outputs\n if (frame.outputs && Object.keys(frame.outputs).length > 0) {\n return true;\n }\n\n // Protect parent frames (have children)\n if (frame.depth === 0) {\n // Root frames\n return true;\n }\n\n return false;\n }\n\n /**\n * Select collection candidates with priority ordering\n */\n private selectCollectionCandidates(categorized: any): Frame[] {\n const candidates: Frame[] = [];\n\n // Priority 1: Closed frames without outputs\n const emptyClosedFrames = categorized.old.filter(\n (f: Frame) =>\n f.state === 'closed' &&\n (!f.outputs || Object.keys(f.outputs).length === 0)\n );\n\n // Priority 2: Orphaned frames (no dependencies)\n const orphaned = [...categorized.mature, ...categorized.old].filter(\n (f: Frame) => this.isOrphaned(f)\n );\n\n // Priority 3: Duplicate traces\n const duplicates = this.findDuplicateTraces([\n ...categorized.mature,\n ...categorized.old,\n ]);\n\n // Priority 4: Old mature frames\n const oldMature = categorized.mature.filter((f: Frame) => {\n const age = Date.now() - f.created_at;\n return age > this.config.generations.mature * 0.8; // 80% of mature threshold\n });\n\n // Combine with priority ordering\n candidates.push(...emptyClosedFrames);\n candidates.push(...orphaned);\n candidates.push(...duplicates);\n candidates.push(...oldMature);\n\n // Remove duplicates and limit to cycle size\n const uniqueCandidates = Array.from(new Set(candidates));\n return uniqueCandidates.slice(0, this.config.framesPerCycle);\n }\n\n /**\n * Check if frame is orphaned (no dependencies)\n */\n private isOrphaned(frame: Frame): boolean {\n // Check if frame has any references from other frames\n // This is a simplified check - in practice, would analyze actual dependencies\n return (\n !frame.parent_frame_id && frame.depth > 0 && frame.state === 'closed'\n );\n }\n\n /**\n * Find duplicate trace signatures\n */\n private findDuplicateTraces(frames: Frame[]): Frame[] {\n const signatureMap = new Map<string, Frame[]>();\n\n for (const frame of frames) {\n // Create a signature from trace content\n const signature = this.createTraceSignature(frame);\n if (!signatureMap.has(signature)) {\n signatureMap.set(signature, []);\n }\n signatureMap.get(signature)!.push(frame);\n }\n\n // Return duplicates (keep newest, mark older for collection)\n const duplicates: Frame[] = [];\n for (const [signature, frameList] of signatureMap) {\n if (frameList.length > 1) {\n // Sort by timestamp, keep newest\n frameList.sort((a, b) => b.timestamp - a.timestamp);\n duplicates.push(...frameList.slice(1)); // Mark older ones for collection\n }\n }\n\n return duplicates;\n }\n\n /**\n * Create a signature for duplicate detection\n */\n private createTraceSignature(frame: Frame): string {\n // Create signature from key frame properties\n const type = frame.type;\n const name = frame.name;\n const outputs = JSON.stringify(frame.outputs || {});\n const digestText = frame.digest_text || '';\n return `${type}:${name}:${outputs}:${digestText}`.toLowerCase();\n }\n\n /**\n * Collect frames incrementally to avoid blocking\n */\n private async collectFramesIncremental(\n candidates: Frame[]\n ): Promise<Frame[]> {\n const collected: Frame[] = [];\n const chunkSize = Math.min(10, Math.ceil(candidates.length / 10)); // Process in small chunks\n\n for (let i = 0; i < candidates.length; i += chunkSize) {\n const chunk = candidates.slice(i, i + chunkSize);\n\n for (const frame of chunk) {\n try {\n await this.frameManager.deleteFrame(frame.frame_id);\n collected.push(frame);\n\n this.logger.debug(`Collected frame ${frame.frame_id}`, {\n age: Date.now() - frame.created_at,\n type: frame.type,\n reason: this.getCollectionReason(frame),\n });\n } catch (error: unknown) {\n this.logger.warn(`Failed to collect frame ${frame.frame_id}`, error);\n }\n }\n\n // Yield control to avoid blocking\n await new Promise((resolve) => setImmediate(resolve));\n }\n\n return collected;\n }\n\n /**\n * Get human-readable collection reason\n */\n private getCollectionReason(frame: Frame): string {\n const age = Date.now() - frame.created_at;\n const ageHours = Math.floor(age / (60 * 60 * 1000));\n\n if (\n frame.state === 'closed' &&\n (!frame.outputs || Object.keys(frame.outputs).length === 0)\n ) {\n return 'empty-closed';\n }\n if (this.isOrphaned(frame)) return 'orphaned';\n if (ageHours > 24 * 30) return `old (${ageHours}h)`;\n return 'duplicate';\n }\n\n /**\n * Update GC statistics\n */\n private updateStats(startTime: number, collectedCount: number): void {\n const cycleTime = Date.now() - startTime;\n\n this.stats.cycleCount++;\n this.stats.collectedFrames += collectedCount;\n this.stats.lastRunTime = startTime;\n this.stats.avgCycleTime =\n (this.stats.avgCycleTime * (this.stats.cycleCount - 1) + cycleTime) /\n this.stats.cycleCount;\n }\n\n /**\n * Get GC statistics\n */\n getStats(): GCStats {\n return { ...this.stats };\n }\n\n /**\n * Force a manual GC cycle\n */\n async forceCollection(): Promise<void> {\n this.logger.info('Forcing manual GC cycle');\n await this.runCycle();\n }\n\n /**\n * Update GC configuration\n */\n updateConfig(newConfig: Partial<GCConfig>): void {\n this.config = { ...this.config, ...newConfig };\n this.logger.info('GC configuration updated', this.config);\n\n // Restart with new interval if running\n if (this.isRunning && newConfig.cycleInterval) {\n this.stop();\n this.start();\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Incremental Garbage Collection System (STA-288)\n *\n * Implements incremental GC strategy to avoid stop-the-world pauses\n * with generational aging and priority-based collection.\n */\n\nimport { FrameManager } from './index.js';\nimport type { Frame } from './index.js';\nimport { Logger } from '../monitoring/logger.js';\n\ninterface GCConfig {\n framesPerCycle: number; // Process in chunks (default: 100)\n cycleInterval: number; // Every minute (default: 60s)\n maxAge: number; // Max age before eligible for collection (30 days)\n generations: {\n young: number; // < 1 day\n mature: number; // 1-7 days\n old: number; // 7-30 days\n };\n}\n\ninterface GCStats {\n totalFrames: number;\n collectedFrames: number;\n lastRunTime: number;\n cycleCount: number;\n avgCycleTime: number;\n protectedFrames: number;\n}\n\nexport class IncrementalGarbageCollector {\n private logger: Logger;\n private config: GCConfig;\n private stats: GCStats;\n private isRunning: boolean = false;\n private cycleTimer: NodeJS.Timer | null = null;\n private frameManager: FrameManager;\n\n constructor(frameManager: FrameManager, config: Partial<GCConfig> = {}) {\n this.frameManager = frameManager;\n this.logger = new Logger('IncrementalGC');\n\n this.config = {\n framesPerCycle: config.framesPerCycle || 100,\n cycleInterval: config.cycleInterval || 60000, // 60 seconds\n maxAge: config.maxAge || 30 * 24 * 60 * 60 * 1000, // 30 days\n generations: {\n young: config.generations?.young || 24 * 60 * 60 * 1000, // 1 day\n mature: config.generations?.mature || 7 * 24 * 60 * 60 * 1000, // 7 days\n old: config.generations?.old || 30 * 24 * 60 * 60 * 1000, // 30 days\n },\n };\n\n this.stats = {\n totalFrames: 0,\n collectedFrames: 0,\n lastRunTime: 0,\n cycleCount: 0,\n avgCycleTime: 0,\n protectedFrames: 0,\n };\n\n this.logger.info('Incremental GC initialized', this.config);\n }\n\n /**\n * Start the garbage collection cycle\n */\n start(): void {\n if (this.isRunning) {\n this.logger.warn('GC already running');\n return;\n }\n\n this.isRunning = true;\n this.logger.info(\n `Starting incremental GC with ${this.config.cycleInterval}ms intervals`\n );\n\n this.cycleTimer = setInterval(() => {\n this.runCycle().catch((error) => {\n this.logger.error('GC cycle failed', error);\n });\n }, this.config.cycleInterval);\n }\n\n /**\n * Stop the garbage collection cycle\n */\n stop(): void {\n if (this.cycleTimer) {\n clearInterval(this.cycleTimer);\n this.cycleTimer = null;\n }\n this.isRunning = false;\n this.logger.info('Incremental GC stopped');\n }\n\n /**\n * Run a single GC cycle\n */\n async runCycle(): Promise<void> {\n const startTime = Date.now();\n this.logger.debug('Starting GC cycle', {\n cycle: this.stats.cycleCount + 1,\n });\n\n try {\n // Get all frames for analysis\n const allFrames = await this.frameManager.getAllFrames();\n this.stats.totalFrames = allFrames.length;\n\n if (allFrames.length === 0) {\n this.logger.debug('No frames to collect');\n return;\n }\n\n // Categorize frames by generation and protection status\n const categorized = this.categorizeFrames(allFrames);\n\n // Select candidates for collection (prioritized)\n const candidates = this.selectCollectionCandidates(categorized);\n\n // Process in chunks to avoid blocking\n const collected = await this.collectFramesIncremental(candidates);\n\n // Update statistics\n this.updateStats(startTime, collected.length);\n\n this.logger.info('GC cycle completed', {\n cycle: this.stats.cycleCount,\n collected: collected.length,\n protected: this.stats.protectedFrames,\n duration: Date.now() - startTime,\n });\n } catch (error: unknown) {\n this.logger.error('GC cycle error', error);\n }\n }\n\n /**\n * Categorize frames by generation and protection status\n */\n private categorizeFrames(frames: Frame[]): {\n young: Frame[];\n mature: Frame[];\n old: Frame[];\n protected: Frame[];\n } {\n const now = Date.now();\n const categories = { young: [], mature: [], old: [], protected: [] };\n\n for (const frame of frames) {\n const age = now - frame.created_at;\n\n // Check if frame is protected\n if (this.isProtected(frame)) {\n categories.protected.push(frame);\n continue;\n }\n\n // Categorize by age\n if (age < this.config.generations.young) {\n categories.young.push(frame);\n } else if (age < this.config.generations.mature) {\n categories.mature.push(frame);\n } else {\n categories.old.push(frame);\n }\n }\n\n this.stats.protectedFrames = categories.protected.length;\n\n this.logger.debug('Frame categorization', {\n young: categories.young.length,\n mature: categories.mature.length,\n old: categories.old.length,\n protected: categories.protected.length,\n });\n\n return categories;\n }\n\n /**\n * Check if a frame should be protected from collection\n */\n private isProtected(frame: Frame): boolean {\n // Protect current session/run frames\n const currentRunId = this.frameManager.getCurrentRunId?.();\n if (frame.run_id === currentRunId) {\n return true;\n }\n\n // Protect active frames\n if (frame.state === 'active') {\n return true;\n }\n\n // Protect recent frames (< 1 hour old)\n const recentThreshold = Date.now() - 60 * 60 * 1000; // 1 hour\n if (frame.created_at > recentThreshold) {\n return true;\n }\n\n // Protect frames with important outputs\n if (frame.outputs && Object.keys(frame.outputs).length > 0) {\n return true;\n }\n\n // Protect parent frames (have children)\n if (frame.depth === 0) {\n // Root frames\n return true;\n }\n\n return false;\n }\n\n /**\n * Select collection candidates with priority ordering\n */\n private selectCollectionCandidates(categorized: any): Frame[] {\n const candidates: Frame[] = [];\n\n // Priority 1: Closed frames without outputs\n const emptyClosedFrames = categorized.old.filter(\n (f: Frame) =>\n f.state === 'closed' &&\n (!f.outputs || Object.keys(f.outputs).length === 0)\n );\n\n // Priority 2: Orphaned frames (no dependencies)\n const orphaned = [...categorized.mature, ...categorized.old].filter(\n (f: Frame) => this.isOrphaned(f)\n );\n\n // Priority 3: Duplicate traces\n const duplicates = this.findDuplicateTraces([\n ...categorized.mature,\n ...categorized.old,\n ]);\n\n // Priority 4: Old mature frames\n const oldMature = categorized.mature.filter((f: Frame) => {\n const age = Date.now() - f.created_at;\n return age > this.config.generations.mature * 0.8; // 80% of mature threshold\n });\n\n // Combine with priority ordering\n candidates.push(...emptyClosedFrames);\n candidates.push(...orphaned);\n candidates.push(...duplicates);\n candidates.push(...oldMature);\n\n // Remove duplicates and limit to cycle size\n const uniqueCandidates = Array.from(new Set(candidates));\n return uniqueCandidates.slice(0, this.config.framesPerCycle);\n }\n\n /**\n * Check if frame is orphaned (no dependencies)\n */\n private isOrphaned(frame: Frame): boolean {\n // Check if frame has any references from other frames\n // This is a simplified check - in practice, would analyze actual dependencies\n return (\n !frame.parent_frame_id && frame.depth > 0 && frame.state === 'closed'\n );\n }\n\n /**\n * Find duplicate trace signatures\n */\n private findDuplicateTraces(frames: Frame[]): Frame[] {\n const signatureMap = new Map<string, Frame[]>();\n\n for (const frame of frames) {\n // Create a signature from trace content\n const signature = this.createTraceSignature(frame);\n if (!signatureMap.has(signature)) {\n signatureMap.set(signature, []);\n }\n signatureMap.get(signature)!.push(frame);\n }\n\n // Return duplicates (keep newest, mark older for collection)\n const duplicates: Frame[] = [];\n for (const [signature, frameList] of signatureMap) {\n if (frameList.length > 1) {\n // Sort by timestamp, keep newest\n frameList.sort((a, b) => b.timestamp - a.timestamp);\n duplicates.push(...frameList.slice(1)); // Mark older ones for collection\n }\n }\n\n return duplicates;\n }\n\n /**\n * Create a signature for duplicate detection\n */\n private createTraceSignature(frame: Frame): string {\n // Create signature from key frame properties\n const type = frame.type;\n const name = frame.name;\n const outputs = JSON.stringify(frame.outputs || {});\n const digestText = frame.digest_text || '';\n return `${type}:${name}:${outputs}:${digestText}`.toLowerCase();\n }\n\n /**\n * Collect frames incrementally to avoid blocking\n */\n private async collectFramesIncremental(\n candidates: Frame[]\n ): Promise<Frame[]> {\n const collected: Frame[] = [];\n const chunkSize = Math.min(10, Math.ceil(candidates.length / 10)); // Process in small chunks\n\n for (let i = 0; i < candidates.length; i += chunkSize) {\n const chunk = candidates.slice(i, i + chunkSize);\n\n for (const frame of chunk) {\n try {\n await this.frameManager.deleteFrame(frame.frame_id);\n collected.push(frame);\n\n this.logger.debug(`Collected frame ${frame.frame_id}`, {\n age: Date.now() - frame.created_at,\n type: frame.type,\n reason: this.getCollectionReason(frame),\n });\n } catch (error: unknown) {\n this.logger.warn(`Failed to collect frame ${frame.frame_id}`, error);\n }\n }\n\n // Yield control to avoid blocking\n await new Promise((resolve) => setImmediate(resolve));\n }\n\n return collected;\n }\n\n /**\n * Get human-readable collection reason\n */\n private getCollectionReason(frame: Frame): string {\n const age = Date.now() - frame.created_at;\n const ageHours = Math.floor(age / (60 * 60 * 1000));\n\n if (\n frame.state === 'closed' &&\n (!frame.outputs || Object.keys(frame.outputs).length === 0)\n ) {\n return 'empty-closed';\n }\n if (this.isOrphaned(frame)) return 'orphaned';\n if (ageHours > 24 * 30) return `old (${ageHours}h)`;\n return 'duplicate';\n }\n\n /**\n * Update GC statistics\n */\n private updateStats(startTime: number, collectedCount: number): void {\n const cycleTime = Date.now() - startTime;\n\n this.stats.cycleCount++;\n this.stats.collectedFrames += collectedCount;\n this.stats.lastRunTime = startTime;\n this.stats.avgCycleTime =\n (this.stats.avgCycleTime * (this.stats.cycleCount - 1) + cycleTime) /\n this.stats.cycleCount;\n }\n\n /**\n * Get GC statistics\n */\n getStats(): GCStats {\n return { ...this.stats };\n }\n\n /**\n * Force a manual GC cycle\n */\n async forceCollection(): Promise<void> {\n this.logger.info('Forcing manual GC cycle');\n await this.runCycle();\n }\n\n /**\n * Update GC configuration\n */\n updateConfig(newConfig: Partial<GCConfig>): void {\n this.config = { ...this.config, ...newConfig };\n this.logger.info('GC configuration updated', this.config);\n\n // Restart with new interval if running\n if (this.isRunning && newConfig.cycleInterval) {\n this.stop();\n this.start();\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,35 +3,17 @@ import { dirname as __pathDirname } from 'path';
3
3
  const __filename = __fileURLToPath(import.meta.url);
4
4
  const __dirname = __pathDirname(__filename);
5
5
  import { RefactoredFrameManager } from "./refactored-frame-manager.js";
6
- import {
7
- Frame,
8
- FrameContext,
9
- Anchor,
10
- Event,
11
- FrameType,
12
- FrameState,
13
- FrameCreationOptions,
14
- FrameManagerConfig,
15
- DigestResult
16
- } from "./frame-types.js";
17
6
  import { FrameDatabase } from "./frame-database.js";
18
7
  import { FrameStack } from "./frame-stack.js";
19
8
  import { FrameDigestGenerator } from "./frame-digest.js";
20
- import { FrameManager } from "./frame-manager.js";
9
+ import {
10
+ frameLifecycleHooks
11
+ } from "./frame-lifecycle-hooks.js";
21
12
  export {
22
- Anchor,
23
- DigestResult,
24
- Event,
25
- Frame,
26
- FrameContext,
27
- FrameCreationOptions,
28
13
  FrameDatabase,
29
14
  FrameDigestGenerator,
30
15
  RefactoredFrameManager as FrameManager,
31
- FrameManagerConfig,
32
16
  FrameStack,
33
- FrameState,
34
- FrameType,
35
- FrameManager as LegacyFrameManager
17
+ frameLifecycleHooks
36
18
  };
37
19
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/context/index.ts"],
4
- "sourcesContent": ["/**\n * Context Module Exports\n * Maintains compatibility while providing access to refactored components\n */\n\n// Export refactored components as primary\nexport { RefactoredFrameManager as FrameManager } from './refactored-frame-manager.js';\n\n// Export types\nexport {\n Frame,\n FrameContext,\n Anchor,\n Event,\n FrameType,\n FrameState,\n FrameCreationOptions,\n FrameManagerConfig,\n DigestResult,\n} from './frame-types.js';\n\n// Export focused modules for direct access\nexport { FrameDatabase } from './frame-database.js';\nexport { FrameStack } from './frame-stack.js';\nexport { FrameDigestGenerator } from './frame-digest.js';\n\n// Re-export from old frame-manager for backwards compatibility\n// This allows existing code to continue working without changes\nexport { FrameManager as LegacyFrameManager } from './frame-manager.js';\n"],
5
- "mappings": ";;;;AAMA,SAAmC,8BAAoB;AAGvD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AAIrC,SAAyB,oBAA0B;",
4
+ "sourcesContent": ["/**\n * Context Module Exports\n * Maintains compatibility while providing access to refactored components\n */\n\n// Export refactored components as primary\nexport { RefactoredFrameManager as FrameManager } from './refactored-frame-manager.js';\n\n// Export types (type-only, no runtime value)\nexport type {\n Frame,\n FrameContext,\n Anchor,\n Event,\n FrameType,\n FrameState,\n FrameCreationOptions,\n FrameManagerConfig,\n DigestResult,\n} from './frame-types.js';\n\n// Export focused modules for direct access\nexport { FrameDatabase } from './frame-database.js';\nexport { FrameStack } from './frame-stack.js';\nexport { FrameDigestGenerator } from './frame-digest.js';\n\n// Export lifecycle hooks for external integrations\nexport {\n frameLifecycleHooks,\n type FrameCloseData,\n type FrameCloseHook,\n type FrameCreateHook,\n} from './frame-lifecycle-hooks.js';\n"],
5
+ "mappings": ";;;;AAMA,SAAmC,8BAAoB;AAgBvD,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AAGrC;AAAA,EACE;AAAA,OAIK;",
6
6
  "names": []
7
7
  }
@@ -11,6 +11,7 @@ import {
11
11
  ErrorCode
12
12
  } from "../errors/index.js";
13
13
  import { FrameQueryMode } from "../session/index.js";
14
+ import { frameLifecycleHooks } from "./frame-lifecycle-hooks.js";
14
15
  const MAX_FRAME_DEPTH = 100;
15
16
  const DEFAULT_MAX_DEPTH = 100;
16
17
  import { FrameDatabase } from "./frame-database.js";
@@ -212,6 +213,10 @@ class RefactoredFrameManager {
212
213
  });
213
214
  this.frameStack.popFrame(targetFrameId);
214
215
  this.closeChildFrames(targetFrameId);
216
+ const events = this.frameDb.getFrameEvents(targetFrameId);
217
+ const anchors = this.frameDb.getFrameAnchors(targetFrameId);
218
+ frameLifecycleHooks.triggerClose({ frame: { ...frame, state: "closed" }, events, anchors }).catch(() => {
219
+ });
215
220
  logger.info("Closed frame", {
216
221
  frameId: targetFrameId,
217
222
  name: frame.name,
@@ -364,9 +369,17 @@ class RefactoredFrameManager {
364
369
  return this.frameDb.getStatistics();
365
370
  }
366
371
  /**
367
- * Close all child frames recursively
372
+ * Close all child frames recursively with depth limit to prevent stack overflow
368
373
  */
369
- closeChildFrames(parentFrameId) {
374
+ closeChildFrames(parentFrameId, depth = 0) {
375
+ if (depth > this.maxFrameDepth) {
376
+ logger.warn("closeChildFrames depth limit exceeded", {
377
+ parentFrameId,
378
+ depth,
379
+ maxDepth: this.maxFrameDepth
380
+ });
381
+ return;
382
+ }
370
383
  try {
371
384
  const activeFrames = this.frameDb.getFramesByProject(
372
385
  this.projectId,
@@ -377,13 +390,32 @@ class RefactoredFrameManager {
377
390
  );
378
391
  for (const childFrame of childFrames) {
379
392
  if (this.frameStack.isFrameActive(childFrame.frame_id)) {
380
- this.closeFrame(childFrame.frame_id);
393
+ this.closeChildFrames(childFrame.frame_id, depth + 1);
394
+ this.closeFrameDirectly(childFrame.frame_id);
381
395
  }
382
396
  }
383
397
  } catch (error) {
384
398
  logger.warn("Failed to close child frames", { parentFrameId, error });
385
399
  }
386
400
  }
401
+ /**
402
+ * Close a frame directly without triggering child frame closure
403
+ * Used by closeChildFrames to avoid double recursion
404
+ */
405
+ closeFrameDirectly(frameId) {
406
+ const frame = this.frameDb.getFrame(frameId);
407
+ if (!frame || frame.state === "closed") return;
408
+ const digest = this.digestGenerator.generateDigest(frameId);
409
+ this.frameDb.updateFrame(frameId, {
410
+ state: "closed",
411
+ outputs: digest.structured,
412
+ digest_text: digest.text,
413
+ digest_json: digest.structured,
414
+ closed_at: Math.floor(Date.now() / 1e3)
415
+ });
416
+ this.frameStack.popFrame(frameId);
417
+ logger.debug("Closed child frame directly", { frameId });
418
+ }
387
419
  /**
388
420
  * Extract active artifacts from frame events
389
421
  */
@@ -468,6 +500,14 @@ class RefactoredFrameManager {
468
500
  );
469
501
  }
470
502
  if (newParentFrameId) {
503
+ const newParentFrame = this.frameDb.getFrame(newParentFrameId);
504
+ if (!newParentFrame) {
505
+ throw new FrameError(
506
+ `Parent frame not found: ${newParentFrameId}`,
507
+ ErrorCode.FRAME_NOT_FOUND,
508
+ { frameId, newParentFrameId }
509
+ );
510
+ }
471
511
  const cycle = this.detectCycle(frameId, newParentFrameId);
472
512
  if (cycle) {
473
513
  throw new FrameError(
@@ -481,25 +521,30 @@ class RefactoredFrameManager {
481
521
  }
482
522
  );
483
523
  }
524
+ const newDepth2 = newParentFrame.depth + 1;
525
+ if (newDepth2 > this.maxFrameDepth) {
526
+ throw new FrameError(
527
+ `Cannot set parent: would exceed maximum frame depth`,
528
+ ErrorCode.FRAME_STACK_OVERFLOW,
529
+ {
530
+ frameId,
531
+ newParentFrameId,
532
+ newDepth: newDepth2,
533
+ maxDepth: this.maxFrameDepth
534
+ }
535
+ );
536
+ }
537
+ }
538
+ let newDepth = 0;
539
+ if (newParentFrameId) {
484
540
  const newParentFrame = this.frameDb.getFrame(newParentFrameId);
485
541
  if (newParentFrame) {
486
- const newDepth = newParentFrame.depth + 1;
487
- if (newDepth > this.maxFrameDepth) {
488
- throw new FrameError(
489
- `Cannot set parent: would exceed maximum frame depth`,
490
- ErrorCode.FRAME_STACK_OVERFLOW,
491
- {
492
- frameId,
493
- newParentFrameId,
494
- newDepth,
495
- maxDepth: this.maxFrameDepth
496
- }
497
- );
498
- }
542
+ newDepth = newParentFrame.depth + 1;
499
543
  }
500
544
  }
501
545
  this.frameDb.updateFrame(frameId, {
502
- parent_frame_id: newParentFrameId
546
+ parent_frame_id: newParentFrameId,
547
+ depth: newDepth
503
548
  });
504
549
  logger.info("Updated parent frame", {
505
550
  frameId,
@@ -558,28 +603,116 @@ class RefactoredFrameManager {
558
603
  warnings
559
604
  };
560
605
  }
606
+ /**
607
+ * Set query mode for frame retrieval
608
+ */
609
+ setQueryMode(mode) {
610
+ this.queryMode = mode;
611
+ this.frameStack.setQueryMode(mode);
612
+ }
613
+ /**
614
+ * Get recent frames for context sharing
615
+ */
616
+ async getRecentFrames(limit = 100) {
617
+ try {
618
+ const frames = this.frameDb.getFramesByProject(this.projectId);
619
+ return frames.sort((a, b) => (b.created_at || 0) - (a.created_at || 0)).slice(0, limit).map((frame) => ({
620
+ ...frame,
621
+ // Add compatibility fields
622
+ frameId: frame.frame_id,
623
+ runId: frame.run_id,
624
+ projectId: frame.project_id,
625
+ parentFrameId: frame.parent_frame_id,
626
+ title: frame.name,
627
+ timestamp: frame.created_at,
628
+ metadata: {
629
+ tags: this.extractTagsFromFrame(frame),
630
+ importance: this.calculateFrameImportance(frame)
631
+ },
632
+ data: {
633
+ inputs: frame.inputs,
634
+ outputs: frame.outputs,
635
+ digest: frame.digest_json
636
+ }
637
+ }));
638
+ } catch (error) {
639
+ logger.error("Failed to get recent frames", error);
640
+ return [];
641
+ }
642
+ }
643
+ /**
644
+ * Add context metadata to the current frame
645
+ */
646
+ async addContext(key, value) {
647
+ const currentFrameId = this.frameStack.getCurrentFrameId();
648
+ if (!currentFrameId) return;
649
+ try {
650
+ const frame = this.frameDb.getFrame(currentFrameId);
651
+ if (!frame) return;
652
+ const metadata = frame.outputs || {};
653
+ metadata[key] = value;
654
+ this.frameDb.updateFrame(currentFrameId, {
655
+ outputs: metadata
656
+ });
657
+ } catch (error) {
658
+ logger.warn("Failed to add context to frame", { error, key });
659
+ }
660
+ }
661
+ /**
662
+ * Delete a frame completely from the database (used in handoffs)
663
+ */
664
+ deleteFrame(frameId) {
665
+ try {
666
+ this.frameStack.removeFrame(frameId);
667
+ this.frameDb.deleteFrame(frameId);
668
+ logger.debug("Deleted frame completely", { frameId });
669
+ } catch (error) {
670
+ logger.error("Failed to delete frame", { frameId, error });
671
+ throw error;
672
+ }
673
+ }
674
+ /**
675
+ * Extract tags from frame for categorization
676
+ */
677
+ extractTagsFromFrame(frame) {
678
+ const tags = [];
679
+ if (frame.type) tags.push(frame.type);
680
+ if (frame.name) {
681
+ const nameLower = frame.name.toLowerCase();
682
+ if (nameLower.includes("error")) tags.push("error");
683
+ if (nameLower.includes("fix")) tags.push("resolution");
684
+ if (nameLower.includes("decision")) tags.push("decision");
685
+ if (nameLower.includes("milestone")) tags.push("milestone");
686
+ }
687
+ try {
688
+ if (frame.digest_json && typeof frame.digest_json === "object") {
689
+ const digest = frame.digest_json;
690
+ if (Array.isArray(digest.tags)) {
691
+ tags.push(...digest.tags);
692
+ }
693
+ }
694
+ } catch {
695
+ }
696
+ return [...new Set(tags)];
697
+ }
698
+ /**
699
+ * Calculate frame importance for prioritization
700
+ */
701
+ calculateFrameImportance(frame) {
702
+ if (frame.type === "milestone" || frame.name?.includes("decision")) {
703
+ return "high";
704
+ }
705
+ if (frame.type === "error" || frame.type === "resolution") {
706
+ return "medium";
707
+ }
708
+ if (frame.closed_at && frame.created_at) {
709
+ const duration = frame.closed_at - frame.created_at;
710
+ if (duration > 300) return "medium";
711
+ }
712
+ return "low";
713
+ }
561
714
  }
562
- import {
563
- Frame as Frame2,
564
- FrameContext as FrameContext2,
565
- Anchor as Anchor2,
566
- Event as Event2,
567
- FrameType as FrameType2,
568
- FrameState as FrameState2,
569
- FrameCreationOptions as FrameCreationOptions2,
570
- FrameManagerConfig as FrameManagerConfig2,
571
- DigestResult as DigestResult2
572
- } from "./frame-types.js";
573
715
  export {
574
- Anchor2 as Anchor,
575
- DigestResult2 as DigestResult,
576
- Event2 as Event,
577
- Frame2 as Frame,
578
- FrameContext2 as FrameContext,
579
- FrameCreationOptions2 as FrameCreationOptions,
580
- FrameManagerConfig2 as FrameManagerConfig,
581
- FrameState2 as FrameState,
582
- FrameType2 as FrameType,
583
716
  RefactoredFrameManager
584
717
  };
585
718
  //# sourceMappingURL=refactored-frame-manager.js.map