@stackmemoryai/stackmemory 0.5.58 → 0.5.59

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 (596) hide show
  1. package/dist/cli/commands/search.js +20 -3
  2. package/dist/cli/commands/search.js.map +2 -2
  3. package/dist/core/database/sqlite-adapter.js +13 -3
  4. package/dist/core/database/sqlite-adapter.js.map +2 -2
  5. package/dist/core/errors/error-utils.js +208 -0
  6. package/dist/core/errors/error-utils.js.map +7 -0
  7. package/dist/core/errors/index.js +13 -4
  8. package/dist/core/errors/index.js.map +2 -2
  9. package/dist/core/merge/unified-merge-resolver.js +303 -0
  10. package/dist/core/merge/unified-merge-resolver.js.map +7 -0
  11. package/dist/core/monitoring/logger.js +61 -9
  12. package/dist/core/monitoring/logger.js.map +2 -2
  13. package/dist/core/security/index.js +35 -0
  14. package/dist/core/security/index.js.map +7 -0
  15. package/dist/core/security/input-sanitizer.js +321 -0
  16. package/dist/core/security/input-sanitizer.js.map +7 -0
  17. package/dist/integrations/linear/client.js +5 -1
  18. package/dist/integrations/linear/client.js.map +2 -2
  19. package/dist/integrations/mcp/remote-server.js +27 -36
  20. package/dist/integrations/mcp/remote-server.js.map +2 -2
  21. package/dist/integrations/mcp/server.js +44 -29
  22. package/dist/integrations/mcp/server.js.map +3 -3
  23. package/dist/scripts/benchmark-performance.js +48 -0
  24. package/dist/scripts/benchmark-performance.js.map +7 -0
  25. package/dist/scripts/check-redis.js +42 -0
  26. package/dist/scripts/check-redis.js.map +7 -0
  27. package/dist/scripts/initialize.js +116 -0
  28. package/dist/scripts/initialize.js.map +7 -0
  29. package/dist/scripts/list-linear-tasks.js +124 -0
  30. package/dist/scripts/list-linear-tasks.js.map +7 -0
  31. package/dist/scripts/measure-handoff-impact.js +340 -0
  32. package/dist/scripts/measure-handoff-impact.js.map +7 -0
  33. package/dist/scripts/query-chromadb.js +160 -0
  34. package/dist/scripts/query-chromadb.js.map +7 -0
  35. package/dist/scripts/show-linear-summary.js +119 -0
  36. package/dist/scripts/show-linear-summary.js.map +7 -0
  37. package/dist/scripts/simple-swarm-demo.js +90 -0
  38. package/dist/scripts/simple-swarm-demo.js.map +7 -0
  39. package/dist/scripts/status.js +155 -0
  40. package/dist/scripts/status.js.map +7 -0
  41. package/dist/scripts/test-chromadb-sync.js +192 -0
  42. package/dist/scripts/test-chromadb-sync.js.map +7 -0
  43. package/dist/scripts/test-ralph-iteration-fix.js +86 -0
  44. package/dist/scripts/test-ralph-iteration-fix.js.map +7 -0
  45. package/dist/scripts/test-ralph-iterations.js +121 -0
  46. package/dist/scripts/test-ralph-iterations.js.map +7 -0
  47. package/dist/scripts/test-redis-storage.js +389 -0
  48. package/dist/scripts/test-redis-storage.js.map +7 -0
  49. package/dist/scripts/test-simple-ralph-state-sync.js +115 -0
  50. package/dist/scripts/test-simple-ralph-state-sync.js.map +7 -0
  51. package/dist/scripts/test-swarm-fixes.js +125 -0
  52. package/dist/scripts/test-swarm-fixes.js.map +7 -0
  53. package/dist/scripts/test-swarm-tui.js +23 -0
  54. package/dist/scripts/test-swarm-tui.js.map +7 -0
  55. package/dist/scripts/test-tui-shortcuts.js +52 -0
  56. package/dist/scripts/test-tui-shortcuts.js.map +7 -0
  57. package/dist/scripts/validate-tui-shortcuts.js +60 -0
  58. package/dist/scripts/validate-tui-shortcuts.js.map +7 -0
  59. package/dist/src/agents/core/agent-task-manager.js +527 -0
  60. package/dist/src/agents/core/agent-task-manager.js.map +7 -0
  61. package/dist/src/agents/verifiers/base-verifier.js +133 -0
  62. package/dist/src/agents/verifiers/base-verifier.js.map +7 -0
  63. package/dist/src/agents/verifiers/formatter-verifier.js +130 -0
  64. package/dist/src/agents/verifiers/formatter-verifier.js.map +7 -0
  65. package/dist/src/agents/verifiers/llm-judge.js +252 -0
  66. package/dist/src/agents/verifiers/llm-judge.js.map +7 -0
  67. package/dist/src/cli/auto-detect.js +321 -0
  68. package/dist/src/cli/auto-detect.js.map +7 -0
  69. package/dist/src/cli/claude-sm-danger.js +21 -0
  70. package/dist/src/cli/claude-sm-danger.js.map +7 -0
  71. package/dist/src/cli/claude-sm.js +1156 -0
  72. package/dist/src/cli/claude-sm.js.map +7 -0
  73. package/dist/src/cli/codex-sm-danger.js +21 -0
  74. package/dist/src/cli/codex-sm-danger.js.map +7 -0
  75. package/dist/src/cli/codex-sm.js +349 -0
  76. package/dist/src/cli/codex-sm.js.map +7 -0
  77. package/dist/src/cli/commands/api.js +232 -0
  78. package/dist/src/cli/commands/api.js.map +7 -0
  79. package/dist/src/cli/commands/auto-background.js +180 -0
  80. package/dist/src/cli/commands/auto-background.js.map +7 -0
  81. package/dist/src/cli/commands/cleanup-processes.js +68 -0
  82. package/dist/src/cli/commands/cleanup-processes.js.map +7 -0
  83. package/dist/src/cli/commands/clear.js +202 -0
  84. package/dist/src/cli/commands/clear.js.map +7 -0
  85. package/dist/src/cli/commands/config.js +445 -0
  86. package/dist/src/cli/commands/config.js.map +7 -0
  87. package/dist/src/cli/commands/context-rehydrate.js +751 -0
  88. package/dist/src/cli/commands/context-rehydrate.js.map +7 -0
  89. package/dist/src/cli/commands/context.js +343 -0
  90. package/dist/src/cli/commands/context.js.map +7 -0
  91. package/dist/src/cli/commands/daemon.js +392 -0
  92. package/dist/src/cli/commands/daemon.js.map +7 -0
  93. package/dist/src/cli/commands/dashboard.js +210 -0
  94. package/dist/src/cli/commands/dashboard.js.map +7 -0
  95. package/dist/src/cli/commands/db.js +147 -0
  96. package/dist/src/cli/commands/db.js.map +7 -0
  97. package/dist/src/cli/commands/decision.js +266 -0
  98. package/dist/src/cli/commands/decision.js.map +7 -0
  99. package/dist/src/cli/commands/discovery.js +279 -0
  100. package/dist/src/cli/commands/discovery.js.map +7 -0
  101. package/dist/src/cli/commands/handoff.js +624 -0
  102. package/dist/src/cli/commands/handoff.js.map +7 -0
  103. package/dist/src/cli/commands/hooks.js +298 -0
  104. package/dist/src/cli/commands/hooks.js.map +7 -0
  105. package/dist/src/cli/commands/linear.js +529 -0
  106. package/dist/src/cli/commands/linear.js.map +7 -0
  107. package/dist/src/cli/commands/log.js +169 -0
  108. package/dist/src/cli/commands/log.js.map +7 -0
  109. package/dist/src/cli/commands/login.js +172 -0
  110. package/dist/src/cli/commands/login.js.map +7 -0
  111. package/dist/src/cli/commands/migrate.js +240 -0
  112. package/dist/src/cli/commands/migrate.js.map +7 -0
  113. package/dist/src/cli/commands/model.js +533 -0
  114. package/dist/src/cli/commands/model.js.map +7 -0
  115. package/dist/src/cli/commands/onboard.js +536 -0
  116. package/dist/src/cli/commands/onboard.js.map +7 -0
  117. package/dist/src/cli/commands/projects.js +199 -0
  118. package/dist/src/cli/commands/projects.js.map +7 -0
  119. package/dist/src/cli/commands/ralph.js +909 -0
  120. package/dist/src/cli/commands/ralph.js.map +7 -0
  121. package/dist/src/cli/commands/retrieval.js +248 -0
  122. package/dist/src/cli/commands/retrieval.js.map +7 -0
  123. package/dist/src/cli/commands/search.js +173 -0
  124. package/dist/src/cli/commands/search.js.map +7 -0
  125. package/dist/src/cli/commands/service.js +749 -0
  126. package/dist/src/cli/commands/service.js.map +7 -0
  127. package/dist/src/cli/commands/session.js +200 -0
  128. package/dist/src/cli/commands/session.js.map +7 -0
  129. package/dist/src/cli/commands/settings.js +306 -0
  130. package/dist/src/cli/commands/settings.js.map +7 -0
  131. package/dist/src/cli/commands/setup.js +701 -0
  132. package/dist/src/cli/commands/setup.js.map +7 -0
  133. package/dist/src/cli/commands/shell.js +249 -0
  134. package/dist/src/cli/commands/shell.js.map +7 -0
  135. package/dist/src/cli/commands/signup.js +50 -0
  136. package/dist/src/cli/commands/signup.js.map +7 -0
  137. package/dist/src/cli/commands/skills.js +470 -0
  138. package/dist/src/cli/commands/skills.js.map +7 -0
  139. package/dist/src/cli/commands/sms-notify.js +795 -0
  140. package/dist/src/cli/commands/sms-notify.js.map +7 -0
  141. package/dist/src/cli/commands/storage-tier.js +183 -0
  142. package/dist/src/cli/commands/storage-tier.js.map +7 -0
  143. package/dist/src/cli/commands/sweep.js +249 -0
  144. package/dist/src/cli/commands/sweep.js.map +7 -0
  145. package/dist/src/cli/commands/tasks.js +213 -0
  146. package/dist/src/cli/commands/tasks.js.map +7 -0
  147. package/dist/src/cli/commands/worktree.js +319 -0
  148. package/dist/src/cli/commands/worktree.js.map +7 -0
  149. package/dist/src/cli/index.js +594 -0
  150. package/dist/src/cli/index.js.map +7 -0
  151. package/dist/src/cli/opencode-sm.js +448 -0
  152. package/dist/src/cli/opencode-sm.js.map +7 -0
  153. package/dist/src/cli/utils/viewer.js +96 -0
  154. package/dist/src/cli/utils/viewer.js.map +7 -0
  155. package/dist/src/core/config/config-manager.js +398 -0
  156. package/dist/src/core/config/config-manager.js.map +7 -0
  157. package/dist/src/core/config/feature-flags.js +76 -0
  158. package/dist/src/core/config/feature-flags.js.map +7 -0
  159. package/dist/src/core/config/storage-config.js +115 -0
  160. package/dist/src/core/config/storage-config.js.map +7 -0
  161. package/dist/src/core/config/types.js +144 -0
  162. package/dist/src/core/config/types.js.map +7 -0
  163. package/dist/src/core/context/auto-context.js +80 -0
  164. package/dist/src/core/context/auto-context.js.map +7 -0
  165. package/dist/src/core/context/dual-stack-manager.js +870 -0
  166. package/dist/src/core/context/dual-stack-manager.js.map +7 -0
  167. package/dist/src/core/context/enhanced-rehydration.js +994 -0
  168. package/dist/src/core/context/enhanced-rehydration.js.map +7 -0
  169. package/dist/src/core/context/frame-database.js +479 -0
  170. package/dist/src/core/context/frame-database.js.map +7 -0
  171. package/dist/src/core/context/frame-digest.js +250 -0
  172. package/dist/src/core/context/frame-digest.js.map +7 -0
  173. package/dist/src/core/context/frame-handoff-manager.js +778 -0
  174. package/dist/src/core/context/frame-handoff-manager.js.map +7 -0
  175. package/dist/src/core/context/frame-lifecycle-hooks.js +119 -0
  176. package/dist/src/core/context/frame-lifecycle-hooks.js.map +7 -0
  177. package/dist/src/core/context/frame-recovery.js +302 -0
  178. package/dist/src/core/context/frame-recovery.js.map +7 -0
  179. package/dist/src/core/context/frame-stack.js +314 -0
  180. package/dist/src/core/context/frame-stack.js.map +7 -0
  181. package/dist/src/core/context/frame-types.js +5 -0
  182. package/dist/src/core/context/frame-types.js.map +7 -0
  183. package/dist/src/core/context/index.js +25 -0
  184. package/dist/src/core/context/index.js.map +7 -0
  185. package/dist/src/core/context/permission-manager.js +185 -0
  186. package/dist/src/core/context/permission-manager.js.map +7 -0
  187. package/dist/src/core/context/recursive-context-manager.js +592 -0
  188. package/dist/src/core/context/recursive-context-manager.js.map +7 -0
  189. package/dist/src/core/context/refactored-frame-manager.js +754 -0
  190. package/dist/src/core/context/refactored-frame-manager.js.map +7 -0
  191. package/dist/src/core/context/shared-context-layer.js +621 -0
  192. package/dist/src/core/context/shared-context-layer.js.map +7 -0
  193. package/dist/src/core/context/stack-merge-resolver.js +749 -0
  194. package/dist/src/core/context/stack-merge-resolver.js.map +7 -0
  195. package/dist/src/core/context/validation.js +130 -0
  196. package/dist/src/core/context/validation.js.map +7 -0
  197. package/dist/src/core/database/batch-operations.js +384 -0
  198. package/dist/src/core/database/batch-operations.js.map +7 -0
  199. package/dist/src/core/database/connection-pool.js +330 -0
  200. package/dist/src/core/database/connection-pool.js.map +7 -0
  201. package/dist/src/core/database/database-adapter.js +60 -0
  202. package/dist/src/core/database/database-adapter.js.map +7 -0
  203. package/dist/src/core/database/migration-manager.js +614 -0
  204. package/dist/src/core/database/migration-manager.js.map +7 -0
  205. package/dist/src/core/database/query-cache.js +298 -0
  206. package/dist/src/core/database/query-cache.js.map +7 -0
  207. package/dist/src/core/database/query-router.js +430 -0
  208. package/dist/src/core/database/query-router.js.map +7 -0
  209. package/dist/src/core/database/sqlite-adapter.js +738 -0
  210. package/dist/src/core/database/sqlite-adapter.js.map +7 -0
  211. package/dist/src/core/digest/enhanced-hybrid-digest.js +277 -0
  212. package/dist/src/core/digest/enhanced-hybrid-digest.js.map +7 -0
  213. package/dist/src/core/digest/frame-digest-integration.js +176 -0
  214. package/dist/src/core/digest/frame-digest-integration.js.map +7 -0
  215. package/dist/src/core/digest/hybrid-digest-generator.js +553 -0
  216. package/dist/src/core/digest/hybrid-digest-generator.js.map +7 -0
  217. package/dist/src/core/digest/index.js +9 -0
  218. package/dist/src/core/digest/index.js.map +7 -0
  219. package/dist/src/core/digest/types.js +25 -0
  220. package/dist/src/core/digest/types.js.map +7 -0
  221. package/dist/src/core/errors/error-utils.js +208 -0
  222. package/dist/src/core/errors/error-utils.js.map +7 -0
  223. package/dist/src/core/errors/index.js +521 -0
  224. package/dist/src/core/errors/index.js.map +7 -0
  225. package/dist/src/core/errors/recovery.js +269 -0
  226. package/dist/src/core/errors/recovery.js.map +7 -0
  227. package/dist/src/core/execution/parallel-executor.js +258 -0
  228. package/dist/src/core/execution/parallel-executor.js.map +7 -0
  229. package/dist/src/core/frame/workflow-templates.js +319 -0
  230. package/dist/src/core/frame/workflow-templates.js.map +7 -0
  231. package/dist/src/core/merge/conflict-detector.js +431 -0
  232. package/dist/src/core/merge/conflict-detector.js.map +7 -0
  233. package/dist/src/core/merge/index.js +9 -0
  234. package/dist/src/core/merge/index.js.map +7 -0
  235. package/dist/src/core/merge/resolution-engine.js +558 -0
  236. package/dist/src/core/merge/resolution-engine.js.map +7 -0
  237. package/dist/src/core/merge/stack-diff.js +532 -0
  238. package/dist/src/core/merge/stack-diff.js.map +7 -0
  239. package/dist/src/core/merge/types.js +5 -0
  240. package/dist/src/core/merge/types.js.map +7 -0
  241. package/dist/src/core/merge/unified-merge-resolver.js +303 -0
  242. package/dist/src/core/merge/unified-merge-resolver.js.map +7 -0
  243. package/dist/src/core/models/fallback-monitor.js +232 -0
  244. package/dist/src/core/models/fallback-monitor.js.map +7 -0
  245. package/dist/src/core/models/model-router.js +340 -0
  246. package/dist/src/core/models/model-router.js.map +7 -0
  247. package/dist/src/core/monitoring/error-handler.js +49 -0
  248. package/dist/src/core/monitoring/error-handler.js.map +7 -0
  249. package/dist/src/core/monitoring/logger.js +202 -0
  250. package/dist/src/core/monitoring/logger.js.map +7 -0
  251. package/dist/src/core/monitoring/metrics.js +172 -0
  252. package/dist/src/core/monitoring/metrics.js.map +7 -0
  253. package/dist/src/core/monitoring/progress-tracker.js +189 -0
  254. package/dist/src/core/monitoring/progress-tracker.js.map +7 -0
  255. package/dist/src/core/monitoring/session-monitor.js +300 -0
  256. package/dist/src/core/monitoring/session-monitor.js.map +7 -0
  257. package/dist/src/core/performance/context-cache.js +273 -0
  258. package/dist/src/core/performance/context-cache.js.map +7 -0
  259. package/dist/src/core/performance/index.js +11 -0
  260. package/dist/src/core/performance/index.js.map +7 -0
  261. package/dist/src/core/performance/lazy-context-loader.js +327 -0
  262. package/dist/src/core/performance/lazy-context-loader.js.map +7 -0
  263. package/dist/src/core/performance/monitor.js +221 -0
  264. package/dist/src/core/performance/monitor.js.map +7 -0
  265. package/dist/src/core/performance/optimized-frame-context.js +345 -0
  266. package/dist/src/core/performance/optimized-frame-context.js.map +7 -0
  267. package/dist/src/core/performance/performance-benchmark.js +277 -0
  268. package/dist/src/core/performance/performance-benchmark.js.map +7 -0
  269. package/dist/src/core/performance/performance-profiler.js +370 -0
  270. package/dist/src/core/performance/performance-profiler.js.map +7 -0
  271. package/dist/src/core/performance/streaming-jsonl-parser.js +195 -0
  272. package/dist/src/core/performance/streaming-jsonl-parser.js.map +7 -0
  273. package/dist/src/core/persistence/postgres-adapter.js +349 -0
  274. package/dist/src/core/persistence/postgres-adapter.js.map +7 -0
  275. package/dist/src/core/projects/project-isolation.js +201 -0
  276. package/dist/src/core/projects/project-isolation.js.map +7 -0
  277. package/dist/src/core/projects/project-manager.js +697 -0
  278. package/dist/src/core/projects/project-manager.js.map +7 -0
  279. package/dist/src/core/query/query-parser.js +370 -0
  280. package/dist/src/core/query/query-parser.js.map +7 -0
  281. package/dist/src/core/query/query-templates.js +321 -0
  282. package/dist/src/core/query/query-templates.js.map +7 -0
  283. package/dist/src/core/retrieval/context-retriever.js +479 -0
  284. package/dist/src/core/retrieval/context-retriever.js.map +7 -0
  285. package/dist/src/core/retrieval/index.js +8 -0
  286. package/dist/src/core/retrieval/index.js.map +7 -0
  287. package/dist/src/core/retrieval/llm-context-retrieval.js +613 -0
  288. package/dist/src/core/retrieval/llm-context-retrieval.js.map +7 -0
  289. package/dist/src/core/retrieval/llm-provider.js +151 -0
  290. package/dist/src/core/retrieval/llm-provider.js.map +7 -0
  291. package/dist/src/core/retrieval/retrieval-audit.js +236 -0
  292. package/dist/src/core/retrieval/retrieval-audit.js.map +7 -0
  293. package/dist/src/core/retrieval/summary-generator.js +589 -0
  294. package/dist/src/core/retrieval/summary-generator.js.map +7 -0
  295. package/dist/src/core/retrieval/types.js +21 -0
  296. package/dist/src/core/retrieval/types.js.map +7 -0
  297. package/dist/src/core/security/index.js +35 -0
  298. package/dist/src/core/security/index.js.map +7 -0
  299. package/dist/src/core/security/input-sanitizer.js +321 -0
  300. package/dist/src/core/security/input-sanitizer.js.map +7 -0
  301. package/dist/src/core/session/clear-survival.js +465 -0
  302. package/dist/src/core/session/clear-survival.js.map +7 -0
  303. package/dist/src/core/session/enhanced-handoff.js +792 -0
  304. package/dist/src/core/session/enhanced-handoff.js.map +7 -0
  305. package/dist/src/core/session/handoff-generator.js +343 -0
  306. package/dist/src/core/session/handoff-generator.js.map +7 -0
  307. package/dist/src/core/session/index.js +15 -0
  308. package/dist/src/core/session/index.js.map +7 -0
  309. package/dist/src/core/session/session-manager.js +347 -0
  310. package/dist/src/core/session/session-manager.js.map +7 -0
  311. package/dist/src/core/skills/index.js +7 -0
  312. package/dist/src/core/skills/index.js.map +7 -0
  313. package/dist/src/core/skills/skill-storage.js +764 -0
  314. package/dist/src/core/skills/skill-storage.js.map +7 -0
  315. package/dist/src/core/skills/types.js +193 -0
  316. package/dist/src/core/skills/types.js.map +7 -0
  317. package/dist/src/core/storage/chromadb-adapter.js +354 -0
  318. package/dist/src/core/storage/chromadb-adapter.js.map +7 -0
  319. package/dist/src/core/storage/infinite-storage.js +510 -0
  320. package/dist/src/core/storage/infinite-storage.js.map +7 -0
  321. package/dist/src/core/storage/remote-storage.js +489 -0
  322. package/dist/src/core/storage/remote-storage.js.map +7 -0
  323. package/dist/src/core/storage/two-tier-storage.js +766 -0
  324. package/dist/src/core/storage/two-tier-storage.js.map +7 -0
  325. package/dist/src/core/trace/cli-trace-wrapper.js +132 -0
  326. package/dist/src/core/trace/cli-trace-wrapper.js.map +7 -0
  327. package/dist/src/core/trace/db-trace-wrapper.js +247 -0
  328. package/dist/src/core/trace/db-trace-wrapper.js.map +7 -0
  329. package/dist/src/core/trace/debug-trace.js +417 -0
  330. package/dist/src/core/trace/debug-trace.js.map +7 -0
  331. package/dist/src/core/trace/index.js +109 -0
  332. package/dist/src/core/trace/index.js.map +7 -0
  333. package/dist/src/core/trace/linear-api-wrapper.js +178 -0
  334. package/dist/src/core/trace/linear-api-wrapper.js.map +7 -0
  335. package/dist/src/core/trace/trace-detector.js +528 -0
  336. package/dist/src/core/trace/trace-detector.js.map +7 -0
  337. package/dist/src/core/trace/trace-store.js +345 -0
  338. package/dist/src/core/trace/trace-store.js.map +7 -0
  339. package/dist/src/core/trace/types.js +77 -0
  340. package/dist/src/core/trace/types.js.map +7 -0
  341. package/dist/src/core/types.js +5 -0
  342. package/dist/src/core/types.js.map +7 -0
  343. package/dist/src/core/utils/async-mutex.js +114 -0
  344. package/dist/src/core/utils/async-mutex.js.map +7 -0
  345. package/dist/src/core/utils/compression.js +83 -0
  346. package/dist/src/core/utils/compression.js.map +7 -0
  347. package/dist/src/core/utils/update-checker.js +218 -0
  348. package/dist/src/core/utils/update-checker.js.map +7 -0
  349. package/dist/src/core/worktree/worktree-manager.js +465 -0
  350. package/dist/src/core/worktree/worktree-manager.js.map +7 -0
  351. package/dist/src/daemon/daemon-config.js +149 -0
  352. package/dist/src/daemon/daemon-config.js.map +7 -0
  353. package/dist/src/daemon/services/context-service.js +122 -0
  354. package/dist/src/daemon/services/context-service.js.map +7 -0
  355. package/dist/src/daemon/services/linear-service.js +136 -0
  356. package/dist/src/daemon/services/linear-service.js.map +7 -0
  357. package/dist/src/daemon/session-daemon.js +312 -0
  358. package/dist/src/daemon/session-daemon.js.map +7 -0
  359. package/dist/src/daemon/unified-daemon.js +276 -0
  360. package/dist/src/daemon/unified-daemon.js.map +7 -0
  361. package/dist/src/features/analytics/api/analytics-api.js +287 -0
  362. package/dist/src/features/analytics/api/analytics-api.js.map +7 -0
  363. package/dist/src/features/analytics/core/analytics-service.js +282 -0
  364. package/dist/src/features/analytics/core/analytics-service.js.map +7 -0
  365. package/dist/src/features/analytics/index.js +18 -0
  366. package/dist/src/features/analytics/index.js.map +7 -0
  367. package/dist/src/features/analytics/queries/metrics-queries.js +277 -0
  368. package/dist/src/features/analytics/queries/metrics-queries.js.map +7 -0
  369. package/dist/src/features/analytics/types/metrics.js +5 -0
  370. package/dist/src/features/analytics/types/metrics.js.map +7 -0
  371. package/dist/src/features/browser/browser-mcp.js +492 -0
  372. package/dist/src/features/browser/browser-mcp.js.map +7 -0
  373. package/dist/src/features/sweep/index.js +20 -0
  374. package/dist/src/features/sweep/index.js.map +7 -0
  375. package/dist/src/features/sweep/prediction-client.js +155 -0
  376. package/dist/src/features/sweep/prediction-client.js.map +7 -0
  377. package/dist/src/features/sweep/prompt-builder.js +85 -0
  378. package/dist/src/features/sweep/prompt-builder.js.map +7 -0
  379. package/dist/src/features/sweep/pty-wrapper.js +171 -0
  380. package/dist/src/features/sweep/pty-wrapper.js.map +7 -0
  381. package/dist/src/features/sweep/state-watcher.js +87 -0
  382. package/dist/src/features/sweep/state-watcher.js.map +7 -0
  383. package/dist/src/features/sweep/status-bar.js +88 -0
  384. package/dist/src/features/sweep/status-bar.js.map +7 -0
  385. package/dist/src/features/sweep/sweep-server-manager.js +226 -0
  386. package/dist/src/features/sweep/sweep-server-manager.js.map +7 -0
  387. package/dist/src/features/sweep/tab-interceptor.js +38 -0
  388. package/dist/src/features/sweep/tab-interceptor.js.map +7 -0
  389. package/dist/src/features/sweep/types.js +18 -0
  390. package/dist/src/features/sweep/types.js.map +7 -0
  391. package/dist/src/features/tasks/linear-task-manager.js +487 -0
  392. package/dist/src/features/tasks/linear-task-manager.js.map +7 -0
  393. package/dist/src/features/tasks/task-aware-context.js +410 -0
  394. package/dist/src/features/tasks/task-aware-context.js.map +7 -0
  395. package/dist/src/features/tui/simple-monitor.js +116 -0
  396. package/dist/src/features/tui/simple-monitor.js.map +7 -0
  397. package/dist/src/features/tui/swarm-monitor.js +648 -0
  398. package/dist/src/features/tui/swarm-monitor.js.map +7 -0
  399. package/dist/src/features/web/client/stores/task-store.js +26 -0
  400. package/dist/src/features/web/client/stores/task-store.js.map +7 -0
  401. package/dist/src/features/web/server/index.js +194 -0
  402. package/dist/src/features/web/server/index.js.map +7 -0
  403. package/dist/src/hooks/auto-background.js +151 -0
  404. package/dist/src/hooks/auto-background.js.map +7 -0
  405. package/dist/src/hooks/claude-code-whatsapp-hook.js +197 -0
  406. package/dist/src/hooks/claude-code-whatsapp-hook.js.map +7 -0
  407. package/dist/src/hooks/config.js +150 -0
  408. package/dist/src/hooks/config.js.map +7 -0
  409. package/dist/src/hooks/daemon.js +364 -0
  410. package/dist/src/hooks/daemon.js.map +7 -0
  411. package/dist/src/hooks/events.js +58 -0
  412. package/dist/src/hooks/events.js.map +7 -0
  413. package/dist/src/hooks/index.js +12 -0
  414. package/dist/src/hooks/index.js.map +7 -0
  415. package/dist/src/hooks/linear-task-picker.js +186 -0
  416. package/dist/src/hooks/linear-task-picker.js.map +7 -0
  417. package/dist/src/hooks/schemas.js +197 -0
  418. package/dist/src/hooks/schemas.js.map +7 -0
  419. package/dist/src/hooks/secure-fs.js +49 -0
  420. package/dist/src/hooks/secure-fs.js.map +7 -0
  421. package/dist/src/hooks/security-logger.js +155 -0
  422. package/dist/src/hooks/security-logger.js.map +7 -0
  423. package/dist/src/hooks/session-summary.js +222 -0
  424. package/dist/src/hooks/session-summary.js.map +7 -0
  425. package/dist/src/hooks/sms-action-runner.js +371 -0
  426. package/dist/src/hooks/sms-action-runner.js.map +7 -0
  427. package/dist/src/hooks/sms-notify.js +506 -0
  428. package/dist/src/hooks/sms-notify.js.map +7 -0
  429. package/dist/src/hooks/sms-watcher.js +93 -0
  430. package/dist/src/hooks/sms-watcher.js.map +7 -0
  431. package/dist/src/hooks/sms-webhook.js +555 -0
  432. package/dist/src/hooks/sms-webhook.js.map +7 -0
  433. package/dist/src/hooks/whatsapp-commands.js +479 -0
  434. package/dist/src/hooks/whatsapp-commands.js.map +7 -0
  435. package/dist/src/hooks/whatsapp-scheduler.js +317 -0
  436. package/dist/src/hooks/whatsapp-scheduler.js.map +7 -0
  437. package/dist/src/hooks/whatsapp-sync.js +409 -0
  438. package/dist/src/hooks/whatsapp-sync.js.map +7 -0
  439. package/dist/src/index.js +25 -0
  440. package/dist/src/index.js.map +7 -0
  441. package/dist/src/integrations/anthropic/client.js +263 -0
  442. package/dist/src/integrations/anthropic/client.js.map +7 -0
  443. package/dist/src/integrations/claude-code/agent-bridge.js +768 -0
  444. package/dist/src/integrations/claude-code/agent-bridge.js.map +7 -0
  445. package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +459 -0
  446. package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js.map +7 -0
  447. package/dist/src/integrations/claude-code/lifecycle-hooks.js +254 -0
  448. package/dist/src/integrations/claude-code/lifecycle-hooks.js.map +7 -0
  449. package/dist/src/integrations/claude-code/post-task-hooks.js +545 -0
  450. package/dist/src/integrations/claude-code/post-task-hooks.js.map +7 -0
  451. package/dist/src/integrations/claude-code/subagent-client-stub.js +20 -0
  452. package/dist/src/integrations/claude-code/subagent-client-stub.js.map +7 -0
  453. package/dist/src/integrations/claude-code/subagent-client.js +511 -0
  454. package/dist/src/integrations/claude-code/subagent-client.js.map +7 -0
  455. package/dist/src/integrations/claude-code/task-coordinator.js +360 -0
  456. package/dist/src/integrations/claude-code/task-coordinator.js.map +7 -0
  457. package/dist/src/integrations/linear/auth.js +337 -0
  458. package/dist/src/integrations/linear/auth.js.map +7 -0
  459. package/dist/src/integrations/linear/auto-sync.js +258 -0
  460. package/dist/src/integrations/linear/auto-sync.js.map +7 -0
  461. package/dist/src/integrations/linear/client.js +634 -0
  462. package/dist/src/integrations/linear/client.js.map +7 -0
  463. package/dist/src/integrations/linear/config.js +130 -0
  464. package/dist/src/integrations/linear/config.js.map +7 -0
  465. package/dist/src/integrations/linear/migration.js +361 -0
  466. package/dist/src/integrations/linear/migration.js.map +7 -0
  467. package/dist/src/integrations/linear/oauth-server.js +454 -0
  468. package/dist/src/integrations/linear/oauth-server.js.map +7 -0
  469. package/dist/src/integrations/linear/rest-client.js +213 -0
  470. package/dist/src/integrations/linear/rest-client.js.map +7 -0
  471. package/dist/src/integrations/linear/sync-manager.js +236 -0
  472. package/dist/src/integrations/linear/sync-manager.js.map +7 -0
  473. package/dist/src/integrations/linear/sync-service.js +231 -0
  474. package/dist/src/integrations/linear/sync-service.js.map +7 -0
  475. package/dist/src/integrations/linear/sync.js +782 -0
  476. package/dist/src/integrations/linear/sync.js.map +7 -0
  477. package/dist/src/integrations/linear/types.js +5 -0
  478. package/dist/src/integrations/linear/types.js.map +7 -0
  479. package/dist/src/integrations/linear/unified-sync.js +589 -0
  480. package/dist/src/integrations/linear/unified-sync.js.map +7 -0
  481. package/dist/src/integrations/linear/webhook-handler.js +219 -0
  482. package/dist/src/integrations/linear/webhook-handler.js.map +7 -0
  483. package/dist/src/integrations/linear/webhook-server.js +218 -0
  484. package/dist/src/integrations/linear/webhook-server.js.map +7 -0
  485. package/dist/src/integrations/linear/webhook.js +291 -0
  486. package/dist/src/integrations/linear/webhook.js.map +7 -0
  487. package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +266 -0
  488. package/dist/src/integrations/mcp/handlers/code-execution-handlers.js.map +7 -0
  489. package/dist/src/integrations/mcp/handlers/context-handlers.js +257 -0
  490. package/dist/src/integrations/mcp/handlers/context-handlers.js.map +7 -0
  491. package/dist/src/integrations/mcp/handlers/discovery-handlers.js +497 -0
  492. package/dist/src/integrations/mcp/handlers/discovery-handlers.js.map +7 -0
  493. package/dist/src/integrations/mcp/handlers/index.js +166 -0
  494. package/dist/src/integrations/mcp/handlers/index.js.map +7 -0
  495. package/dist/src/integrations/mcp/handlers/linear-handlers.js +247 -0
  496. package/dist/src/integrations/mcp/handlers/linear-handlers.js.map +7 -0
  497. package/dist/src/integrations/mcp/handlers/skill-handlers.js +529 -0
  498. package/dist/src/integrations/mcp/handlers/skill-handlers.js.map +7 -0
  499. package/dist/src/integrations/mcp/handlers/task-handlers.js +239 -0
  500. package/dist/src/integrations/mcp/handlers/task-handlers.js.map +7 -0
  501. package/dist/src/integrations/mcp/handlers/trace-handlers.js +308 -0
  502. package/dist/src/integrations/mcp/handlers/trace-handlers.js.map +7 -0
  503. package/dist/src/integrations/mcp/index.js +23 -0
  504. package/dist/src/integrations/mcp/index.js.map +7 -0
  505. package/dist/src/integrations/mcp/middleware/tool-scoring.js +356 -0
  506. package/dist/src/integrations/mcp/middleware/tool-scoring.js.map +7 -0
  507. package/dist/src/integrations/mcp/refactored-server.js +374 -0
  508. package/dist/src/integrations/mcp/refactored-server.js.map +7 -0
  509. package/dist/src/integrations/mcp/remote-server.js +682 -0
  510. package/dist/src/integrations/mcp/remote-server.js.map +7 -0
  511. package/dist/src/integrations/mcp/schemas.js +147 -0
  512. package/dist/src/integrations/mcp/schemas.js.map +7 -0
  513. package/dist/src/integrations/mcp/server.js +1975 -0
  514. package/dist/src/integrations/mcp/server.js.map +7 -0
  515. package/dist/src/integrations/mcp/tool-definitions-code.js +125 -0
  516. package/dist/src/integrations/mcp/tool-definitions-code.js.map +7 -0
  517. package/dist/src/integrations/mcp/tool-definitions.js +702 -0
  518. package/dist/src/integrations/mcp/tool-definitions.js.map +7 -0
  519. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +860 -0
  520. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
  521. package/dist/src/integrations/ralph/context/context-budget-manager.js +301 -0
  522. package/dist/src/integrations/ralph/context/context-budget-manager.js.map +7 -0
  523. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +360 -0
  524. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
  525. package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +410 -0
  526. package/dist/src/integrations/ralph/coordination/enhanced-coordination.js.map +7 -0
  527. package/dist/src/integrations/ralph/index.js +18 -0
  528. package/dist/src/integrations/ralph/index.js.map +7 -0
  529. package/dist/src/integrations/ralph/learning/pattern-learner.js +401 -0
  530. package/dist/src/integrations/ralph/learning/pattern-learner.js.map +7 -0
  531. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +448 -0
  532. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
  533. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +294 -0
  534. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js.map +7 -0
  535. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +108 -0
  536. package/dist/src/integrations/ralph/monitoring/swarm-registry.js.map +7 -0
  537. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +463 -0
  538. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
  539. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +400 -0
  540. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js.map +7 -0
  541. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +473 -0
  542. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js.map +7 -0
  543. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +388 -0
  544. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +7 -0
  545. package/dist/src/integrations/ralph/performance/performance-optimizer.js +358 -0
  546. package/dist/src/integrations/ralph/performance/performance-optimizer.js.map +7 -0
  547. package/dist/src/integrations/ralph/recovery/crash-recovery.js +462 -0
  548. package/dist/src/integrations/ralph/recovery/crash-recovery.js.map +7 -0
  549. package/dist/src/integrations/ralph/state/state-reconciler.js +404 -0
  550. package/dist/src/integrations/ralph/state/state-reconciler.js.map +7 -0
  551. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +428 -0
  552. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js.map +7 -0
  553. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +996 -0
  554. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
  555. package/dist/src/integrations/ralph/types.js +5 -0
  556. package/dist/src/integrations/ralph/types.js.map +7 -0
  557. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +585 -0
  558. package/dist/src/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
  559. package/dist/src/mcp/stackmemory-mcp-server.js +554 -0
  560. package/dist/src/mcp/stackmemory-mcp-server.js.map +7 -0
  561. package/dist/src/middleware/exponential-rate-limiter.js +289 -0
  562. package/dist/src/middleware/exponential-rate-limiter.js.map +7 -0
  563. package/dist/src/models/user.model.js +358 -0
  564. package/dist/src/models/user.model.js.map +7 -0
  565. package/dist/src/servers/production/auth-middleware.js +528 -0
  566. package/dist/src/servers/production/auth-middleware.js.map +7 -0
  567. package/dist/src/services/config-service.js +65 -0
  568. package/dist/src/services/config-service.js.map +7 -0
  569. package/dist/src/services/context-service.js +194 -0
  570. package/dist/src/services/context-service.js.map +7 -0
  571. package/dist/src/skills/api-discovery.js +354 -0
  572. package/dist/src/skills/api-discovery.js.map +7 -0
  573. package/dist/src/skills/api-skill.js +475 -0
  574. package/dist/src/skills/api-skill.js.map +7 -0
  575. package/dist/src/skills/claude-skills.js +1061 -0
  576. package/dist/src/skills/claude-skills.js.map +7 -0
  577. package/dist/src/skills/dashboard-launcher.js +216 -0
  578. package/dist/src/skills/dashboard-launcher.js.map +7 -0
  579. package/dist/src/skills/recursive-agent-orchestrator.js +575 -0
  580. package/dist/src/skills/recursive-agent-orchestrator.js.map +7 -0
  581. package/dist/src/skills/repo-ingestion-skill.js +609 -0
  582. package/dist/src/skills/repo-ingestion-skill.js.map +7 -0
  583. package/dist/src/skills/unified-rlm-orchestrator.js +404 -0
  584. package/dist/src/skills/unified-rlm-orchestrator.js.map +7 -0
  585. package/dist/src/types/task.js +5 -0
  586. package/dist/src/types/task.js.map +7 -0
  587. package/dist/src/utils/env.js +50 -0
  588. package/dist/src/utils/env.js.map +7 -0
  589. package/dist/src/utils/formatting.js +62 -0
  590. package/dist/src/utils/formatting.js.map +7 -0
  591. package/dist/src/utils/process-cleanup.js +136 -0
  592. package/dist/src/utils/process-cleanup.js.map +7 -0
  593. package/package.json +3 -3
  594. package/scripts/initialize.ts +16 -7
  595. package/scripts/install.sh +14 -62
  596. package/scripts/status.ts +111 -46
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/core/context/frame-handoff-manager.ts"],
4
+ "sourcesContent": ["/**\n * Frame Handoff Manager - STA-100\n * Handles frame transfers between individual and team stacks with approval workflows\n */\n\nimport type { Frame, Event, Anchor } from './frame-types.js';\nimport {\n DualStackManager,\n type StackContext,\n type HandoffRequest,\n} from './dual-stack-manager.js';\nimport { logger } from '../monitoring/logger.js';\nimport { ValidationError, DatabaseError, ErrorCode } from '../errors/index.js';\nimport {\n validateInput,\n InitiateHandoffSchema,\n HandoffApprovalSchema,\n type InitiateHandoffInput,\n type HandoffApprovalInput,\n} from './validation.js';\n\nexport interface HandoffMetadata {\n initiatedAt: Date;\n initiatorId: string;\n targetUserId?: string;\n targetTeamId?: string;\n frameContext: {\n totalFrames: number;\n frameTypes: string[];\n estimatedSize: number;\n dependencies: string[];\n };\n businessContext?: {\n milestone?: string;\n priority: 'low' | 'medium' | 'high' | 'critical';\n deadline?: Date;\n stakeholders: string[];\n };\n}\n\nexport interface HandoffApproval {\n requestId: string;\n reviewerId: string;\n decision: 'approved' | 'rejected' | 'needs_changes';\n feedback?: string;\n suggestedChanges?: Array<{\n frameId: string;\n suggestion: string;\n reason: string;\n }>;\n reviewedAt: Date;\n}\n\nexport interface HandoffNotification {\n id: string;\n type: 'request' | 'approval' | 'rejection' | 'completion' | 'reminder';\n requestId: string;\n recipientId: string;\n title: string;\n message: string;\n actionRequired: boolean;\n expiresAt?: Date;\n createdAt: Date;\n}\n\nexport interface HandoffProgress {\n requestId: string;\n status:\n | 'pending_review'\n | 'approved'\n | 'in_transfer'\n | 'completed'\n | 'failed'\n | 'cancelled';\n transferredFrames: number;\n totalFrames: number;\n currentStep: string;\n estimatedCompletion?: Date;\n errors: Array<{\n step: string;\n error: string;\n timestamp: Date;\n }>;\n}\n\nexport class FrameHandoffManager {\n private dualStackManager: DualStackManager;\n private activeHandoffs: Map<string, HandoffProgress> = new Map();\n private pendingApprovals: Map<string, HandoffApproval[]> = new Map();\n private notifications: Map<string, HandoffNotification[]> = new Map();\n\n constructor(dualStackManager: DualStackManager) {\n this.dualStackManager = dualStackManager;\n }\n\n /**\n * Initiate a frame handoff with rich metadata and approval workflow\n */\n async initiateHandoff(\n targetStackId: string,\n frameIds: string[],\n metadata: HandoffMetadata,\n targetUserId?: string,\n message?: string\n ): Promise<string> {\n // Validate input parameters\n const input = validateInput(InitiateHandoffSchema, {\n targetStackId,\n frameIds,\n handoffRequest: metadata,\n reviewerId: targetUserId,\n description: message,\n });\n\n try {\n // Check handoff permissions\n await this.dualStackManager\n .getPermissionManager()\n .enforcePermission(\n this.dualStackManager\n .getPermissionManager()\n .createContext(\n input.handoffRequest.initiatorId,\n 'handoff',\n 'handoff',\n input.targetStackId\n )\n );\n\n // Validate frames exist and are transferable\n await this.validateFramesForHandoff(input.frameIds);\n\n // Create enhanced handoff request\n const requestId = await this.dualStackManager.initiateHandoff(\n input.targetStackId,\n input.frameIds,\n input.reviewerId,\n input.description\n );\n\n // Initialize handoff progress tracking\n const progress: HandoffProgress = {\n requestId,\n status: 'pending_review',\n transferredFrames: 0,\n totalFrames: input.frameIds.length,\n currentStep: 'Awaiting approval',\n errors: [],\n };\n\n this.activeHandoffs.set(requestId, progress);\n\n // Create notifications for relevant stakeholders\n await this.createHandoffNotifications(requestId, metadata, targetUserId);\n\n // Set up automatic reminders\n await this.scheduleHandoffReminders(requestId, metadata);\n\n logger.info(`Initiated enhanced handoff: ${requestId}`, {\n frameCount: frameIds.length,\n priority: metadata.businessContext?.priority,\n targetUser: targetUserId,\n });\n\n return requestId;\n } catch (error: unknown) {\n throw new DatabaseError(\n 'Failed to initiate handoff',\n ErrorCode.OPERATION_FAILED,\n { targetStackId, frameIds },\n error instanceof Error ? error : undefined\n );\n }\n }\n\n /**\n * Submit approval/rejection for handoff request\n */\n async submitHandoffApproval(\n requestId: string,\n approval: Omit<HandoffApproval, 'requestId' | 'reviewedAt'>\n ): Promise<void> {\n // Validate input parameters\n const input = validateInput(HandoffApprovalSchema, {\n ...approval,\n reviewerId: approval.reviewerId,\n });\n const progress = this.activeHandoffs.get(requestId);\n if (!progress) {\n throw new ValidationError(\n `Handoff request not found: ${requestId}`,\n ErrorCode.HANDOFF_REQUEST_EXPIRED\n );\n }\n\n const fullApproval: HandoffApproval = {\n ...input,\n requestId,\n reviewedAt: new Date(),\n };\n\n // Store approval\n const existingApprovals = this.pendingApprovals.get(requestId) || [];\n existingApprovals.push(fullApproval);\n this.pendingApprovals.set(requestId, existingApprovals);\n\n // Update progress based on decision\n if (input.decision === 'approved') {\n progress.status = 'approved';\n progress.currentStep = 'Ready for transfer';\n\n // Automatically start transfer if approved\n await this.executeHandoffTransfer(requestId);\n } else if (input.decision === 'rejected') {\n progress.status = 'failed';\n progress.currentStep = 'Rejected by reviewer';\n progress.errors.push({\n step: 'approval',\n error: input.feedback || 'Request rejected',\n timestamp: new Date(),\n });\n } else if (input.decision === 'needs_changes') {\n progress.status = 'pending_review';\n progress.currentStep = 'Changes requested';\n\n // Notify requester of needed changes\n await this.notifyChangesRequested(requestId, approval);\n }\n\n this.activeHandoffs.set(requestId, progress);\n\n logger.info(`Handoff approval submitted: ${requestId}`, {\n decision: approval.decision,\n reviewer: approval.reviewerId,\n });\n }\n\n /**\n * Execute the actual frame transfer after approval\n */\n private async executeHandoffTransfer(requestId: string): Promise<void> {\n logger.debug('executeHandoffTransfer called', {\n requestId,\n availableHandoffs: Array.from(this.activeHandoffs.keys()),\n });\n const progress = this.activeHandoffs.get(requestId);\n if (!progress) {\n logger.error('Handoff progress not found', {\n requestId,\n availableHandoffs: Array.from(this.activeHandoffs.keys()),\n });\n throw new DatabaseError(\n `Handoff progress not found: ${requestId}`,\n ErrorCode.INVALID_STATE\n );\n }\n\n try {\n logger.debug('Setting progress status to in_transfer', { requestId });\n progress.status = 'in_transfer';\n progress.currentStep = 'Transferring frames';\n progress.estimatedCompletion = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes\n\n // Execute the handoff through DualStackManager\n logger.debug('About to call acceptHandoff', { requestId });\n const result = await this.dualStackManager.acceptHandoff(requestId);\n logger.debug('acceptHandoff returned', {\n requestId,\n success: result.success,\n });\n\n if (result.success) {\n progress.status = 'completed';\n progress.currentStep = 'Transfer completed';\n progress.transferredFrames = result.mergedFrames.length;\n\n // Create completion notifications\n await this.notifyHandoffCompletion(requestId, result);\n\n logger.info(`Handoff transfer completed: ${requestId}`, {\n transferredFrames: progress.transferredFrames,\n conflicts: result.conflictFrames.length,\n });\n } else {\n progress.status = 'failed';\n progress.currentStep = 'Transfer failed';\n\n // Log errors\n result.errors.forEach((error) => {\n progress.errors.push({\n step: 'transfer',\n error: `Frame ${error.frameId}: ${error.error}`,\n timestamp: new Date(),\n });\n });\n\n throw new DatabaseError(\n 'Handoff transfer failed',\n ErrorCode.OPERATION_FAILED,\n { errors: result.errors }\n );\n }\n } catch (error: unknown) {\n progress.status = 'failed';\n progress.currentStep = 'Transfer error';\n progress.errors.push({\n step: 'transfer',\n error: error instanceof Error ? error.message : String(error),\n timestamp: new Date(),\n });\n\n logger.error(`Handoff transfer failed: ${requestId}`, error);\n throw error;\n } finally {\n this.activeHandoffs.set(requestId, progress);\n }\n }\n\n /**\n * Get handoff progress and status\n */\n async getHandoffProgress(requestId: string): Promise<HandoffProgress | null> {\n return this.activeHandoffs.get(requestId) || null;\n }\n\n /**\n * Cancel a pending handoff request\n */\n async cancelHandoff(requestId: string, reason: string): Promise<void> {\n const progress = this.activeHandoffs.get(requestId);\n if (!progress) {\n throw new DatabaseError(\n `Handoff request not found: ${requestId}`,\n ErrorCode.RESOURCE_NOT_FOUND\n );\n }\n\n if (progress.status === 'in_transfer') {\n throw new DatabaseError(\n 'Cannot cancel handoff that is currently transferring',\n ErrorCode.INVALID_STATE\n );\n }\n\n progress.status = 'cancelled';\n progress.currentStep = 'Cancelled by user';\n progress.errors.push({\n step: 'cancellation',\n error: reason,\n timestamp: new Date(),\n });\n\n this.activeHandoffs.set(requestId, progress);\n\n // Notify relevant parties\n await this.notifyHandoffCancellation(requestId, reason);\n\n logger.info(`Handoff cancelled: ${requestId}`, { reason });\n }\n\n /**\n * Get all active handoffs for a user or team\n */\n async getActiveHandoffs(\n userId?: string,\n teamId?: string\n ): Promise<HandoffProgress[]> {\n const handoffs = Array.from(this.activeHandoffs.values());\n\n // Filter by user/team if specified\n if (userId || teamId) {\n // Would need to cross-reference with handoff metadata\n return handoffs.filter(\n (handoff) =>\n handoff.status === 'pending_review' ||\n handoff.status === 'approved' ||\n handoff.status === 'in_transfer'\n );\n }\n\n return handoffs;\n }\n\n /**\n * Get notifications for a user\n */\n async getUserNotifications(userId: string): Promise<HandoffNotification[]> {\n return this.notifications.get(userId) || [];\n }\n\n /**\n * Mark notification as read\n */\n async markNotificationRead(\n notificationId: string,\n userId: string\n ): Promise<void> {\n const userNotifications = this.notifications.get(userId) || [];\n const updatedNotifications = userNotifications.filter(\n (n) => n.id !== notificationId\n );\n this.notifications.set(userId, updatedNotifications);\n }\n\n /**\n * Validate frames are suitable for handoff\n */\n private async validateFramesForHandoff(frameIds: string[]): Promise<void> {\n const activeStack = this.dualStackManager.getActiveStack();\n\n for (const frameId of frameIds) {\n const frame = await activeStack.getFrame(frameId);\n if (!frame) {\n throw new DatabaseError(\n `Frame not found: ${frameId}`,\n ErrorCode.RESOURCE_NOT_FOUND\n );\n }\n\n // Check if frame is in a transferable state\n if (frame.state === 'active') {\n logger.warn(`Transferring active frame: ${frameId}`, {\n frameName: frame.name,\n });\n }\n }\n }\n\n /**\n * Create notifications for handoff stakeholders\n */\n private async createHandoffNotifications(\n requestId: string,\n metadata: HandoffMetadata,\n targetUserId?: string\n ): Promise<void> {\n const notifications: HandoffNotification[] = [];\n\n // Notify target user\n if (targetUserId) {\n notifications.push({\n id: `${requestId}-target`,\n type: 'request',\n requestId,\n recipientId: targetUserId,\n title: 'Frame Handoff Request',\n message: `${metadata.initiatorId} wants to transfer ${metadata.frameContext.totalFrames} frames to you`,\n actionRequired: true,\n expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),\n createdAt: new Date(),\n });\n }\n\n // Notify stakeholders\n if (metadata.businessContext?.stakeholders) {\n for (const stakeholderId of metadata.businessContext.stakeholders) {\n notifications.push({\n id: `${requestId}-stakeholder-${stakeholderId}`,\n type: 'request',\n requestId,\n recipientId: stakeholderId,\n title: 'Frame Handoff Notification',\n message: `Frame transfer initiated for ${metadata.businessContext?.milestone || 'project milestone'}`,\n actionRequired: false,\n createdAt: new Date(),\n });\n }\n }\n\n // Store notifications\n for (const notification of notifications) {\n const userNotifications =\n this.notifications.get(notification.recipientId) || [];\n userNotifications.push(notification);\n this.notifications.set(notification.recipientId, userNotifications);\n }\n }\n\n /**\n * Schedule reminder notifications\n */\n private async scheduleHandoffReminders(\n requestId: string,\n metadata: HandoffMetadata\n ): Promise<void> {\n // Schedule reminder in 4 hours if high priority\n if (\n metadata.businessContext?.priority === 'high' ||\n metadata.businessContext?.priority === 'critical'\n ) {\n setTimeout(\n async () => {\n const progress = this.activeHandoffs.get(requestId);\n if (progress && progress.status === 'pending_review') {\n await this.sendHandoffReminder(requestId, metadata);\n }\n },\n 4 * 60 * 60 * 1000\n ); // 4 hours\n }\n }\n\n /**\n * Send handoff reminder\n */\n private async sendHandoffReminder(\n requestId: string,\n metadata: HandoffMetadata\n ): Promise<void> {\n const progress = this.activeHandoffs.get(requestId);\n if (!progress || progress.status !== 'pending_review') {\n return;\n }\n\n const reminderNotification: HandoffNotification = {\n id: `${requestId}-reminder-${Date.now()}`,\n type: 'reminder',\n requestId,\n recipientId: metadata.targetUserId || 'unknown',\n title: '\u23F0 Handoff Request Reminder',\n message: `Reminder: ${metadata.initiatorId} is waiting for approval on ${metadata.frameContext.totalFrames} frames. Priority: ${metadata.businessContext?.priority || 'medium'}`,\n actionRequired: true,\n expiresAt: new Date(Date.now() + 12 * 60 * 60 * 1000), // 12 hours\n createdAt: new Date(),\n };\n\n // Store the notification\n if (metadata.targetUserId) {\n const userNotifications =\n this.notifications.get(metadata.targetUserId) || [];\n userNotifications.push(reminderNotification);\n this.notifications.set(metadata.targetUserId, userNotifications);\n\n logger.info(`Sent handoff reminder: ${requestId}`, {\n priority: metadata.businessContext?.priority,\n recipient: metadata.targetUserId,\n });\n }\n\n // Also notify stakeholders\n if (metadata.businessContext?.stakeholders) {\n for (const stakeholderId of metadata.businessContext.stakeholders) {\n const stakeholderNotification: HandoffNotification = {\n ...reminderNotification,\n id: `${requestId}-reminder-stakeholder-${stakeholderId}-${Date.now()}`,\n recipientId: stakeholderId,\n title: '\uD83D\uDCCB Handoff Status Update',\n message: `Pending handoff approval: ${metadata.businessContext?.milestone || 'development work'} requires attention`,\n actionRequired: false,\n };\n\n const stakeholderNotifications =\n this.notifications.get(stakeholderId) || [];\n stakeholderNotifications.push(stakeholderNotification);\n this.notifications.set(stakeholderId, stakeholderNotifications);\n }\n }\n }\n\n /**\n * Notify when changes are requested\n */\n private async notifyChangesRequested(\n requestId: string,\n approval: Omit<HandoffApproval, 'requestId' | 'reviewedAt'>\n ): Promise<void> {\n const progress = this.activeHandoffs.get(requestId);\n if (!progress) return;\n\n // Find the original requester (we'll need to enhance this with better metadata tracking)\n const changeRequestNotification: HandoffNotification = {\n id: `${requestId}-changes-${Date.now()}`,\n type: 'request',\n requestId,\n recipientId: 'requester', // TODO: Get actual requester from handoff metadata\n title: 'Changes Requested for Handoff',\n message: `${approval.reviewerId} has requested changes: ${approval.feedback || 'See detailed suggestions'}`,\n actionRequired: true,\n expiresAt: new Date(Date.now() + 48 * 60 * 60 * 1000), // 48 hours\n createdAt: new Date(),\n };\n\n // Store notification (for now using a placeholder recipient)\n const notifications = this.notifications.get('requester') || [];\n notifications.push(changeRequestNotification);\n this.notifications.set('requester', notifications);\n\n // Log detailed feedback and suggestions\n logger.info(`Changes requested for handoff: ${requestId}`, {\n reviewer: approval.reviewerId,\n feedback: approval.feedback,\n suggestedChangesCount: approval.suggestedChanges?.length || 0,\n });\n\n if (approval.suggestedChanges && approval.suggestedChanges.length > 0) {\n logger.info(`Detailed change suggestions:`, {\n requestId,\n suggestions: approval.suggestedChanges.map((change) => ({\n frameId: change.frameId,\n suggestion: change.suggestion,\n reason: change.reason,\n })),\n });\n }\n }\n\n /**\n * Notify handoff completion\n */\n private async notifyHandoffCompletion(\n requestId: string,\n result: any\n ): Promise<void> {\n const progress = this.activeHandoffs.get(requestId);\n if (!progress) return;\n\n // Create completion notification\n const completionNotification: HandoffNotification = {\n id: `${requestId}-completion-${Date.now()}`,\n type: 'completion',\n requestId,\n recipientId: 'all', // Will be distributed to all stakeholders\n title: 'Handoff Completed Successfully',\n message: `Frame transfer completed: ${result.mergedFrames.length} frames transferred${result.conflictFrames.length > 0 ? `, ${result.conflictFrames.length} conflicts resolved` : ''}`,\n actionRequired: false,\n createdAt: new Date(),\n };\n\n // Notify all stakeholders from the notifications map\n const allUsers = Array.from(this.notifications.keys());\n for (const userId of allUsers) {\n const userSpecificNotification: HandoffNotification = {\n ...completionNotification,\n id: `${requestId}-completion-${userId}-${Date.now()}`,\n recipientId: userId,\n };\n\n const userNotifications = this.notifications.get(userId) || [];\n userNotifications.push(userSpecificNotification);\n this.notifications.set(userId, userNotifications);\n }\n\n logger.info(`Handoff completed: ${requestId}`, {\n mergedFrames: result.mergedFrames.length,\n conflicts: result.conflictFrames.length,\n notifiedUsers: allUsers.length,\n });\n\n // Log detailed completion statistics\n if (result.conflictFrames.length > 0) {\n logger.info(`Handoff completion details:`, {\n requestId,\n transferredFrames: result.mergedFrames.map(\n (f: any) => f.frameId || f.id\n ),\n conflictFrames: result.conflictFrames.map(\n (f: any) => f.frameId || f.id\n ),\n });\n }\n }\n\n /**\n * Notify handoff cancellation\n */\n private async notifyHandoffCancellation(\n requestId: string,\n reason: string\n ): Promise<void> {\n // Create cancellation notification\n const cancellationNotification: HandoffNotification = {\n id: `${requestId}-cancellation-${Date.now()}`,\n type: 'request', // Using 'request' type as it's informational\n requestId,\n recipientId: 'all', // Will be distributed to all stakeholders\n title: 'Handoff Cancelled',\n message: `Handoff request has been cancelled. Reason: ${reason}`,\n actionRequired: false,\n createdAt: new Date(),\n };\n\n // Notify all users who have been involved in this handoff\n const allUsers = Array.from(this.notifications.keys());\n for (const userId of allUsers) {\n const userSpecificNotification: HandoffNotification = {\n ...cancellationNotification,\n id: `${requestId}-cancellation-${userId}-${Date.now()}`,\n recipientId: userId,\n };\n\n const userNotifications = this.notifications.get(userId) || [];\n userNotifications.push(userSpecificNotification);\n this.notifications.set(userId, userNotifications);\n }\n\n logger.info(`Handoff cancelled: ${requestId}`, {\n reason,\n notifiedUsers: allUsers.length,\n });\n }\n\n /**\n * Get handoff analytics and metrics\n */\n async getHandoffMetrics(timeRange?: { start: Date; end: Date }): Promise<{\n totalHandoffs: number;\n completedHandoffs: number;\n averageProcessingTime: number;\n topFrameTypes: Array<{ type: string; count: number }>;\n collaborationPatterns: Array<{\n sourceUser: string;\n targetUser: string;\n count: number;\n }>;\n }> {\n const handoffs = Array.from(this.activeHandoffs.values());\n\n // Filter by time range if specified\n const filteredHandoffs = timeRange\n ? handoffs.filter((h) => {\n // Would need to add timestamps to track creation time\n return true; // Placeholder\n })\n : handoffs;\n\n const completedHandoffs = filteredHandoffs.filter(\n (h) => h.status === 'completed'\n );\n\n return {\n totalHandoffs: filteredHandoffs.length,\n completedHandoffs: completedHandoffs.length,\n averageProcessingTime:\n this.calculateAverageProcessingTime(completedHandoffs),\n topFrameTypes: this.analyzeFrameTypes(filteredHandoffs),\n collaborationPatterns:\n this.analyzeCollaborationPatterns(filteredHandoffs),\n };\n }\n\n private calculateAverageProcessingTime(handoffs: HandoffProgress[]): number {\n if (handoffs.length === 0) return 0;\n\n let totalProcessingTime = 0;\n let validHandoffs = 0;\n\n for (const handoff of handoffs) {\n // Only calculate for completed handoffs that have timing data\n if (handoff.status === 'completed' && handoff.estimatedCompletion) {\n // Estimate processing time based on frame count and complexity\n // This is a simplified calculation - in practice you'd track actual timestamps\n const frameComplexity = handoff.totalFrames * 0.5; // Base time per frame\n const errorPenalty = handoff.errors.length * 2; // Extra time for errors\n const processingTime = Math.max(1, frameComplexity + errorPenalty);\n\n totalProcessingTime += processingTime;\n validHandoffs++;\n }\n }\n\n return validHandoffs > 0\n ? Math.round(totalProcessingTime / validHandoffs)\n : 0;\n }\n\n private analyzeFrameTypes(\n handoffs: HandoffProgress[]\n ): Array<{ type: string; count: number }> {\n const frameTypeCount = new Map<string, number>();\n\n for (const handoff of handoffs) {\n // Extract frame type information from handoff metadata\n // This would need to be enhanced with actual frame type tracking\n const estimatedTypes = this.estimateFrameTypes(handoff);\n\n for (const type of estimatedTypes) {\n frameTypeCount.set(type, (frameTypeCount.get(type) || 0) + 1);\n }\n }\n\n return Array.from(frameTypeCount.entries())\n .map(([type, count]) => ({ type, count }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 10); // Top 10 frame types\n }\n\n private estimateFrameTypes(handoff: HandoffProgress): string[] {\n // Simplified frame type estimation based on handoff characteristics\n const types: string[] = [];\n\n if (handoff.totalFrames > 10) {\n types.push('bulk_transfer');\n }\n if (handoff.errors.length > 0) {\n types.push('complex_handoff');\n }\n if (handoff.transferredFrames === handoff.totalFrames) {\n types.push('complete_transfer');\n } else {\n types.push('partial_transfer');\n }\n\n // Add some common frame types based on patterns\n types.push('development', 'collaboration');\n\n return types;\n }\n\n private analyzeCollaborationPatterns(\n handoffs: HandoffProgress[]\n ): Array<{ sourceUser: string; targetUser: string; count: number }> {\n const collaborationCount = new Map<string, number>();\n\n for (const handoff of handoffs) {\n // Extract collaboration pattern from handoff data\n // Note: This is simplified - we'd need to track actual source/target users\n const pattern = this.extractCollaborationPattern(handoff);\n if (pattern) {\n const key = `${pattern.sourceUser}->${pattern.targetUser}`;\n collaborationCount.set(key, (collaborationCount.get(key) || 0) + 1);\n }\n }\n\n return Array.from(collaborationCount.entries())\n .map(([pattern, count]) => {\n const [sourceUser, targetUser] = pattern.split('->');\n return { sourceUser, targetUser, count };\n })\n .sort((a, b) => b.count - a.count)\n .slice(0, 20); // Top 20 collaboration patterns\n }\n\n private extractCollaborationPattern(\n handoff: HandoffProgress\n ): { sourceUser: string; targetUser: string } | null {\n // Simplified pattern extraction - in practice this would come from handoff metadata\n // For now, we'll create sample patterns based on handoff characteristics\n\n if (handoff.status === 'completed') {\n return {\n sourceUser: 'developer',\n targetUser: 'reviewer',\n };\n } else if (handoff.status === 'failed') {\n return {\n sourceUser: 'developer',\n targetUser: 'lead',\n };\n }\n\n return null;\n }\n\n /**\n * Real-time collaboration features\n */\n\n /**\n * Get real-time handoff status updates\n */\n async getHandoffStatusStream(\n requestId: string\n ): Promise<AsyncIterableIterator<HandoffProgress>> {\n const progress = this.activeHandoffs.get(requestId);\n if (!progress) {\n throw new DatabaseError(\n `Handoff request not found: ${requestId}`,\n ErrorCode.RESOURCE_NOT_FOUND\n );\n }\n\n // Simple implementation - in a real system this would use WebSockets or Server-Sent Events\n const self = this;\n return {\n async *[Symbol.asyncIterator]() {\n let lastStatus = progress.status;\n while (\n lastStatus !== 'completed' &&\n lastStatus !== 'failed' &&\n lastStatus !== 'cancelled'\n ) {\n const currentProgress = self.activeHandoffs.get(requestId);\n if (currentProgress && currentProgress.status !== lastStatus) {\n lastStatus = currentProgress.status;\n yield currentProgress;\n }\n // Simulate real-time polling\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n },\n };\n }\n\n /**\n * Update handoff progress in real-time\n */\n async updateHandoffProgress(\n requestId: string,\n update: Partial<HandoffProgress>\n ): Promise<void> {\n let progress = this.activeHandoffs.get(requestId);\n\n // If progress doesn't exist and update includes required fields, create it\n if (\n !progress &&\n update.requestId &&\n update.status &&\n update.totalFrames !== undefined\n ) {\n progress = {\n requestId: update.requestId,\n status: update.status,\n transferredFrames: 0,\n totalFrames: update.totalFrames,\n currentStep: 'Initialized',\n errors: [],\n ...update,\n };\n } else if (!progress) {\n throw new DatabaseError(\n `Handoff request not found: ${requestId}`,\n ErrorCode.RESOURCE_NOT_FOUND\n );\n } else {\n // Update existing progress with provided fields\n progress = {\n ...progress,\n ...update,\n };\n }\n\n this.activeHandoffs.set(requestId, progress);\n\n logger.info(`Handoff progress updated: ${requestId}`, {\n status: progress.status,\n currentStep: progress.currentStep,\n transferredFrames: progress.transferredFrames,\n });\n\n // Notify stakeholders of progress update\n await this.notifyProgressUpdate(requestId, progress);\n }\n\n /**\n * Notify stakeholders of progress updates\n */\n private async notifyProgressUpdate(\n requestId: string,\n progress: HandoffProgress\n ): Promise<void> {\n const updateNotification: HandoffNotification = {\n id: `${requestId}-progress-${Date.now()}`,\n type: 'request',\n requestId,\n recipientId: 'all',\n title: 'Handoff Progress Update',\n message: `Status: ${progress.status} | Step: ${progress.currentStep} | Progress: ${progress.transferredFrames}/${progress.totalFrames} frames`,\n actionRequired: false,\n createdAt: new Date(),\n };\n\n // Distribute to all stakeholders\n const allUsers = Array.from(this.notifications.keys());\n for (const userId of allUsers) {\n const userNotifications = this.notifications.get(userId) || [];\n userNotifications.push({\n ...updateNotification,\n id: `${requestId}-progress-${userId}-${Date.now()}`,\n recipientId: userId,\n });\n this.notifications.set(userId, userNotifications);\n }\n }\n\n /**\n * Get active handoffs with real-time filtering\n */\n async getActiveHandoffsRealTime(filters?: {\n status?: HandoffProgress['status'];\n userId?: string;\n priority?: 'low' | 'medium' | 'high' | 'critical';\n }): Promise<HandoffProgress[]> {\n let handoffs = Array.from(this.activeHandoffs.values());\n\n if (filters?.status) {\n handoffs = handoffs.filter((h) => h.status === filters.status);\n }\n\n if (filters?.userId) {\n // In a real implementation, we'd have proper user tracking in handoff metadata\n // For now, filter based on requestId pattern or other heuristics\n handoffs = handoffs.filter((h) =>\n h.requestId.includes(filters.userId || '')\n );\n }\n\n if (filters?.priority) {\n // Filter by priority (this would need priority tracking in HandoffProgress)\n // For now, estimate priority based on frame count and errors\n handoffs = handoffs.filter((h) => {\n const estimatedPriority = this.estimateHandoffPriority(h);\n return estimatedPriority === filters.priority;\n });\n }\n\n return handoffs.sort((a, b) => {\n // Sort by status priority, then by creation time\n const statusPriority = {\n in_transfer: 4,\n approved: 3,\n pending_review: 2,\n completed: 1,\n failed: 1,\n cancelled: 0,\n };\n return (statusPriority[b.status] || 0) - (statusPriority[a.status] || 0);\n });\n }\n\n private estimateHandoffPriority(\n handoff: HandoffProgress\n ): 'low' | 'medium' | 'high' | 'critical' {\n if (handoff.errors.length > 2 || handoff.totalFrames > 50)\n return 'critical';\n if (handoff.errors.length > 0 || handoff.totalFrames > 20) return 'high';\n if (handoff.totalFrames > 5) return 'medium';\n return 'low';\n }\n\n /**\n * Bulk handoff operations for team collaboration\n */\n async bulkHandoffOperation(operation: {\n action: 'approve' | 'reject' | 'cancel';\n requestIds: string[];\n reviewerId: string;\n feedback?: string;\n }): Promise<{\n successful: string[];\n failed: Array<{ requestId: string; error: string }>;\n }> {\n const results = {\n successful: [],\n failed: [] as Array<{ requestId: string; error: string }>,\n };\n\n for (const requestId of operation.requestIds) {\n try {\n switch (operation.action) {\n case 'approve':\n await this.submitHandoffApproval(requestId, {\n reviewerId: operation.reviewerId,\n decision: 'approved',\n feedback: operation.feedback,\n });\n results.successful.push(requestId);\n break;\n\n case 'reject':\n await this.submitHandoffApproval(requestId, {\n reviewerId: operation.reviewerId,\n decision: 'rejected',\n feedback: operation.feedback || 'Bulk rejection',\n });\n results.successful.push(requestId);\n break;\n\n case 'cancel':\n await this.cancelHandoff(\n requestId,\n operation.feedback || 'Bulk cancellation'\n );\n results.successful.push(requestId);\n break;\n }\n } catch (error: unknown) {\n results.failed.push({\n requestId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n logger.info(`Bulk handoff operation completed`, {\n action: operation.action,\n successful: results.successful.length,\n failed: results.failed.length,\n reviewerId: operation.reviewerId,\n });\n\n return results;\n }\n\n /**\n * Enhanced notification management with cleanup\n */\n async cleanupExpiredNotifications(userId?: string): Promise<number> {\n let cleanedCount = 0;\n const now = new Date();\n\n const userIds = userId ? [userId] : Array.from(this.notifications.keys());\n\n for (const uid of userIds) {\n const userNotifications = this.notifications.get(uid) || [];\n const activeNotifications = userNotifications.filter((notification) => {\n if (notification.expiresAt && notification.expiresAt < now) {\n cleanedCount++;\n return false;\n }\n return true;\n });\n\n this.notifications.set(uid, activeNotifications);\n }\n\n if (cleanedCount > 0) {\n logger.info(`Cleaned up expired notifications`, {\n count: cleanedCount,\n userId: userId || 'all',\n });\n }\n\n return cleanedCount;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,119 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { logger } from "../monitoring/logger.js";
6
+ class FrameLifecycleHooksRegistry {
7
+ closeHooks = [];
8
+ createHooks = [];
9
+ /**
10
+ * Register a hook to be called when a frame is closed
11
+ * @param name - Unique name for the hook (for logging/debugging)
12
+ * @param handler - Async function to call when frame closes
13
+ * @param priority - Higher priority hooks run first (default: 0)
14
+ */
15
+ onFrameClosed(name, handler, priority = 0) {
16
+ const hook = { name, handler, priority };
17
+ this.closeHooks.push(hook);
18
+ this.closeHooks.sort((a, b) => b.priority - a.priority);
19
+ logger.debug("Registered frame close hook", { name, priority });
20
+ return () => {
21
+ this.closeHooks = this.closeHooks.filter((h) => h !== hook);
22
+ logger.debug("Unregistered frame close hook", { name });
23
+ };
24
+ }
25
+ /**
26
+ * Register a hook to be called when a frame is created
27
+ * @param name - Unique name for the hook (for logging/debugging)
28
+ * @param handler - Async function to call when frame is created
29
+ * @param priority - Higher priority hooks run first (default: 0)
30
+ */
31
+ onFrameCreated(name, handler, priority = 0) {
32
+ const hook = { name, handler, priority };
33
+ this.createHooks.push(hook);
34
+ this.createHooks.sort((a, b) => b.priority - a.priority);
35
+ logger.debug("Registered frame create hook", { name, priority });
36
+ return () => {
37
+ this.createHooks = this.createHooks.filter((h) => h !== hook);
38
+ logger.debug("Unregistered frame create hook", { name });
39
+ };
40
+ }
41
+ /**
42
+ * Trigger all close hooks (called by FrameManager)
43
+ * Hooks are fire-and-forget - errors don't affect frame closure
44
+ */
45
+ async triggerClose(data) {
46
+ if (this.closeHooks.length === 0) return;
47
+ const results = await Promise.allSettled(
48
+ this.closeHooks.map(async (hook) => {
49
+ try {
50
+ await hook.handler(data);
51
+ } catch (error) {
52
+ logger.warn(`Frame close hook "${hook.name}" failed`, {
53
+ error: error instanceof Error ? error.message : String(error),
54
+ frameId: data.frame.frame_id,
55
+ frameName: data.frame.name
56
+ });
57
+ }
58
+ })
59
+ );
60
+ const failed = results.filter((r) => r.status === "rejected").length;
61
+ if (failed > 0) {
62
+ logger.debug("Some frame close hooks failed", {
63
+ total: this.closeHooks.length,
64
+ failed,
65
+ frameId: data.frame.frame_id
66
+ });
67
+ }
68
+ }
69
+ /**
70
+ * Trigger all create hooks (called by FrameManager)
71
+ * Hooks are fire-and-forget - errors don't affect frame creation
72
+ */
73
+ async triggerCreate(frame) {
74
+ if (this.createHooks.length === 0) return;
75
+ const results = await Promise.allSettled(
76
+ this.createHooks.map(async (hook) => {
77
+ try {
78
+ await hook.handler(frame);
79
+ } catch (error) {
80
+ logger.warn(`Frame create hook "${hook.name}" failed`, {
81
+ error: error instanceof Error ? error.message : String(error),
82
+ frameId: frame.frame_id,
83
+ frameName: frame.name
84
+ });
85
+ }
86
+ })
87
+ );
88
+ const failed = results.filter((r) => r.status === "rejected").length;
89
+ if (failed > 0) {
90
+ logger.debug("Some frame create hooks failed", {
91
+ total: this.createHooks.length,
92
+ failed,
93
+ frameId: frame.frame_id
94
+ });
95
+ }
96
+ }
97
+ /**
98
+ * Get count of registered hooks (useful for testing)
99
+ */
100
+ getHookCounts() {
101
+ return {
102
+ close: this.closeHooks.length,
103
+ create: this.createHooks.length
104
+ };
105
+ }
106
+ /**
107
+ * Clear all hooks (useful for testing)
108
+ */
109
+ clearAll() {
110
+ this.closeHooks = [];
111
+ this.createHooks = [];
112
+ logger.debug("Cleared all frame lifecycle hooks");
113
+ }
114
+ }
115
+ const frameLifecycleHooks = new FrameLifecycleHooksRegistry();
116
+ export {
117
+ frameLifecycleHooks
118
+ };
119
+ //# sourceMappingURL=frame-lifecycle-hooks.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/core/context/frame-lifecycle-hooks.ts"],
4
+ "sourcesContent": ["/**\n * Frame Lifecycle Hooks\n * Allows external modules to subscribe to frame events without coupling to FrameManager\n */\n\nimport { logger } from '../monitoring/logger.js';\nimport type { Frame, Event, Anchor } from './frame-types.js';\n\n/**\n * Data passed to frame close hooks\n */\nexport interface FrameCloseData {\n frame: Frame;\n events: Event[];\n anchors: Anchor[];\n}\n\n/**\n * Hook function type for frame close events\n */\nexport type FrameCloseHook = (data: FrameCloseData) => Promise<void>;\n\n/**\n * Hook function type for frame create events\n */\nexport type FrameCreateHook = (frame: Frame) => Promise<void>;\n\n/**\n * Registered hook with metadata\n */\ninterface RegisteredHook<T> {\n name: string;\n handler: T;\n priority: number;\n}\n\n/**\n * Frame Lifecycle Hooks Registry\n * Singleton that manages all frame lifecycle hooks\n */\nclass FrameLifecycleHooksRegistry {\n private closeHooks: RegisteredHook<FrameCloseHook>[] = [];\n private createHooks: RegisteredHook<FrameCreateHook>[] = [];\n\n /**\n * Register a hook to be called when a frame is closed\n * @param name - Unique name for the hook (for logging/debugging)\n * @param handler - Async function to call when frame closes\n * @param priority - Higher priority hooks run first (default: 0)\n */\n onFrameClosed(\n name: string,\n handler: FrameCloseHook,\n priority: number = 0\n ): () => void {\n const hook: RegisteredHook<FrameCloseHook> = { name, handler, priority };\n this.closeHooks.push(hook);\n this.closeHooks.sort((a, b) => b.priority - a.priority);\n\n logger.debug('Registered frame close hook', { name, priority });\n\n // Return unregister function\n return () => {\n this.closeHooks = this.closeHooks.filter((h) => h !== hook);\n logger.debug('Unregistered frame close hook', { name });\n };\n }\n\n /**\n * Register a hook to be called when a frame is created\n * @param name - Unique name for the hook (for logging/debugging)\n * @param handler - Async function to call when frame is created\n * @param priority - Higher priority hooks run first (default: 0)\n */\n onFrameCreated(\n name: string,\n handler: FrameCreateHook,\n priority: number = 0\n ): () => void {\n const hook: RegisteredHook<FrameCreateHook> = { name, handler, priority };\n this.createHooks.push(hook);\n this.createHooks.sort((a, b) => b.priority - a.priority);\n\n logger.debug('Registered frame create hook', { name, priority });\n\n // Return unregister function\n return () => {\n this.createHooks = this.createHooks.filter((h) => h !== hook);\n logger.debug('Unregistered frame create hook', { name });\n };\n }\n\n /**\n * Trigger all close hooks (called by FrameManager)\n * Hooks are fire-and-forget - errors don't affect frame closure\n */\n async triggerClose(data: FrameCloseData): Promise<void> {\n if (this.closeHooks.length === 0) return;\n\n const results = await Promise.allSettled(\n this.closeHooks.map(async (hook) => {\n try {\n await hook.handler(data);\n } catch (error) {\n logger.warn(`Frame close hook \"${hook.name}\" failed`, {\n error: error instanceof Error ? error.message : String(error),\n frameId: data.frame.frame_id,\n frameName: data.frame.name,\n });\n }\n })\n );\n\n const failed = results.filter((r) => r.status === 'rejected').length;\n if (failed > 0) {\n logger.debug('Some frame close hooks failed', {\n total: this.closeHooks.length,\n failed,\n frameId: data.frame.frame_id,\n });\n }\n }\n\n /**\n * Trigger all create hooks (called by FrameManager)\n * Hooks are fire-and-forget - errors don't affect frame creation\n */\n async triggerCreate(frame: Frame): Promise<void> {\n if (this.createHooks.length === 0) return;\n\n const results = await Promise.allSettled(\n this.createHooks.map(async (hook) => {\n try {\n await hook.handler(frame);\n } catch (error) {\n logger.warn(`Frame create hook \"${hook.name}\" failed`, {\n error: error instanceof Error ? error.message : String(error),\n frameId: frame.frame_id,\n frameName: frame.name,\n });\n }\n })\n );\n\n const failed = results.filter((r) => r.status === 'rejected').length;\n if (failed > 0) {\n logger.debug('Some frame create hooks failed', {\n total: this.createHooks.length,\n failed,\n frameId: frame.frame_id,\n });\n }\n }\n\n /**\n * Get count of registered hooks (useful for testing)\n */\n getHookCounts(): { close: number; create: number } {\n return {\n close: this.closeHooks.length,\n create: this.createHooks.length,\n };\n }\n\n /**\n * Clear all hooks (useful for testing)\n */\n clearAll(): void {\n this.closeHooks = [];\n this.createHooks = [];\n logger.debug('Cleared all frame lifecycle hooks');\n }\n}\n\n/**\n * Singleton instance of the hooks registry\n */\nexport const frameLifecycleHooks = new FrameLifecycleHooksRegistry();\n"],
5
+ "mappings": ";;;;AAKA,SAAS,cAAc;AAmCvB,MAAM,4BAA4B;AAAA,EACxB,aAA+C,CAAC;AAAA,EAChD,cAAiD,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1D,cACE,MACA,SACA,WAAmB,GACP;AACZ,UAAM,OAAuC,EAAE,MAAM,SAAS,SAAS;AACvE,SAAK,WAAW,KAAK,IAAI;AACzB,SAAK,WAAW,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAEtD,WAAO,MAAM,+BAA+B,EAAE,MAAM,SAAS,CAAC;AAG9D,WAAO,MAAM;AACX,WAAK,aAAa,KAAK,WAAW,OAAO,CAAC,MAAM,MAAM,IAAI;AAC1D,aAAO,MAAM,iCAAiC,EAAE,KAAK,CAAC;AAAA,IACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eACE,MACA,SACA,WAAmB,GACP;AACZ,UAAM,OAAwC,EAAE,MAAM,SAAS,SAAS;AACxE,SAAK,YAAY,KAAK,IAAI;AAC1B,SAAK,YAAY,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAEvD,WAAO,MAAM,gCAAgC,EAAE,MAAM,SAAS,CAAC;AAG/D,WAAO,MAAM;AACX,WAAK,cAAc,KAAK,YAAY,OAAO,CAAC,MAAM,MAAM,IAAI;AAC5D,aAAO,MAAM,kCAAkC,EAAE,KAAK,CAAC;AAAA,IACzD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,MAAqC;AACtD,QAAI,KAAK,WAAW,WAAW,EAAG;AAElC,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,WAAW,IAAI,OAAO,SAAS;AAClC,YAAI;AACF,gBAAM,KAAK,QAAQ,IAAI;AAAA,QACzB,SAAS,OAAO;AACd,iBAAO,KAAK,qBAAqB,KAAK,IAAI,YAAY;AAAA,YACpD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC5D,SAAS,KAAK,MAAM;AAAA,YACpB,WAAW,KAAK,MAAM;AAAA,UACxB,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE;AAC9D,QAAI,SAAS,GAAG;AACd,aAAO,MAAM,iCAAiC;AAAA,QAC5C,OAAO,KAAK,WAAW;AAAA,QACvB;AAAA,QACA,SAAS,KAAK,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,OAA6B;AAC/C,QAAI,KAAK,YAAY,WAAW,EAAG;AAEnC,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,KAAK,YAAY,IAAI,OAAO,SAAS;AACnC,YAAI;AACF,gBAAM,KAAK,QAAQ,KAAK;AAAA,QAC1B,SAAS,OAAO;AACd,iBAAO,KAAK,sBAAsB,KAAK,IAAI,YAAY;AAAA,YACrD,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,YAC5D,SAAS,MAAM;AAAA,YACf,WAAW,MAAM;AAAA,UACnB,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,SAAS,QAAQ,OAAO,CAAC,MAAM,EAAE,WAAW,UAAU,EAAE;AAC9D,QAAI,SAAS,GAAG;AACd,aAAO,MAAM,kCAAkC;AAAA,QAC7C,OAAO,KAAK,YAAY;AAAA,QACxB;AAAA,QACA,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAmD;AACjD,WAAO;AAAA,MACL,OAAO,KAAK,WAAW;AAAA,MACvB,QAAQ,KAAK,YAAY;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAiB;AACf,SAAK,aAAa,CAAC;AACnB,SAAK,cAAc,CAAC;AACpB,WAAO,MAAM,mCAAmC;AAAA,EAClD;AACF;AAKO,MAAM,sBAAsB,IAAI,4BAA4B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,302 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { logger } from "../monitoring/logger.js";
6
+ class FrameRecovery {
7
+ constructor(db, options = {}) {
8
+ this.db = db;
9
+ this.orphanThresholdMs = (options.orphanThresholdHours ?? 24) * 60 * 60 * 1e3;
10
+ }
11
+ // Sessions older than this are considered orphaned (default: 24 hours)
12
+ orphanThresholdMs;
13
+ // Current session/run ID to exclude from orphan detection
14
+ currentRunId = null;
15
+ /**
16
+ * Set the current run ID to exclude from orphan detection
17
+ */
18
+ setCurrentRunId(runId) {
19
+ this.currentRunId = runId;
20
+ }
21
+ /**
22
+ * Main recovery entry point - call on startup
23
+ */
24
+ async recoverOnStartup() {
25
+ const errors = [];
26
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
27
+ logger.info("Starting crash recovery check");
28
+ const walStatus = this.checkWalStatus();
29
+ if (walStatus.checkpointNeeded) {
30
+ try {
31
+ this.checkpointWal();
32
+ logger.info("WAL checkpoint completed");
33
+ } catch (err) {
34
+ const msg = `WAL checkpoint failed: ${err instanceof Error ? err.message : String(err)}`;
35
+ errors.push(msg);
36
+ logger.warn(msg);
37
+ }
38
+ }
39
+ const integrityCheck = this.runIntegrityCheck();
40
+ if (!integrityCheck.passed) {
41
+ logger.error("Database integrity check failed", {
42
+ violations: integrityCheck.foreignKeyViolations,
43
+ corrupted: integrityCheck.corruptedRows
44
+ });
45
+ }
46
+ const orphanedFrames = this.recoverOrphanedFrames();
47
+ if (orphanedFrames.detected > 0) {
48
+ logger.info("Orphaned frames processed", {
49
+ detected: orphanedFrames.detected,
50
+ recovered: orphanedFrames.recovered,
51
+ closed: orphanedFrames.closed
52
+ });
53
+ }
54
+ const report = {
55
+ timestamp,
56
+ integrityCheck,
57
+ orphanedFrames,
58
+ walStatus,
59
+ recovered: integrityCheck.passed && orphanedFrames.detected === orphanedFrames.closed,
60
+ errors
61
+ };
62
+ logger.info("Crash recovery completed", {
63
+ recovered: report.recovered,
64
+ orphansFound: orphanedFrames.detected,
65
+ integrityPassed: integrityCheck.passed
66
+ });
67
+ return report;
68
+ }
69
+ /**
70
+ * Check WAL mode status
71
+ */
72
+ checkWalStatus() {
73
+ try {
74
+ const journalMode = this.db.pragma("journal_mode");
75
+ const isWal = journalMode[0]?.journal_mode === "wal";
76
+ if (!isWal) {
77
+ return { enabled: false, checkpointNeeded: false, walSize: 0 };
78
+ }
79
+ const walInfo = this.db.pragma("wal_checkpoint(PASSIVE)");
80
+ const walSize = walInfo[0]?.log ?? 0;
81
+ const checkpointed = walInfo[0]?.checkpointed ?? 0;
82
+ return {
83
+ enabled: true,
84
+ checkpointNeeded: walSize > 1e3,
85
+ // Checkpoint if WAL has > 1000 pages
86
+ walSize: walSize - checkpointed
87
+ };
88
+ } catch (err) {
89
+ logger.warn("Failed to check WAL status", { error: err });
90
+ return { enabled: false, checkpointNeeded: false, walSize: 0 };
91
+ }
92
+ }
93
+ /**
94
+ * Force WAL checkpoint
95
+ */
96
+ checkpointWal() {
97
+ this.db.pragma("wal_checkpoint(TRUNCATE)");
98
+ }
99
+ /**
100
+ * Run database integrity checks
101
+ */
102
+ runIntegrityCheck() {
103
+ const errors = [];
104
+ let foreignKeyViolations = 0;
105
+ let corruptedRows = 0;
106
+ try {
107
+ const fkViolations = this.db.pragma(
108
+ "foreign_key_check"
109
+ );
110
+ foreignKeyViolations = fkViolations.length;
111
+ if (foreignKeyViolations > 0) {
112
+ errors.push(`Found ${foreignKeyViolations} foreign key violations`);
113
+ logger.warn("Foreign key violations detected", {
114
+ count: foreignKeyViolations,
115
+ samples: fkViolations.slice(0, 5)
116
+ });
117
+ }
118
+ const integrity = this.db.pragma("integrity_check");
119
+ const integrityResult = integrity[0]?.integrity_check;
120
+ if (integrityResult !== "ok") {
121
+ corruptedRows = integrity.length;
122
+ errors.push(`Integrity check failed: ${integrityResult}`);
123
+ logger.error("Database corruption detected", {
124
+ result: integrityResult
125
+ });
126
+ }
127
+ } catch (err) {
128
+ errors.push(
129
+ `Integrity check error: ${err instanceof Error ? err.message : String(err)}`
130
+ );
131
+ }
132
+ return {
133
+ passed: foreignKeyViolations === 0 && corruptedRows === 0,
134
+ foreignKeyViolations,
135
+ corruptedRows,
136
+ errors
137
+ };
138
+ }
139
+ /**
140
+ * Detect and recover orphaned frames
141
+ * Orphaned = active frames from sessions that are no longer running
142
+ */
143
+ recoverOrphanedFrames() {
144
+ const orphanThreshold = Date.now() - this.orphanThresholdMs;
145
+ const thresholdUnix = Math.floor(orphanThreshold / 1e3);
146
+ const query = `
147
+ SELECT frame_id, run_id, project_id, name, type, created_at, depth
148
+ FROM frames
149
+ WHERE state = 'active'
150
+ AND created_at < ?
151
+ ${this.currentRunId ? "AND run_id != ?" : ""}
152
+ ORDER BY created_at ASC
153
+ `;
154
+ const params = this.currentRunId ? [thresholdUnix, this.currentRunId] : [thresholdUnix];
155
+ const orphaned = this.db.prepare(query).all(...params);
156
+ if (orphaned.length === 0) {
157
+ return { detected: 0, recovered: 0, closed: 0, frameIds: [] };
158
+ }
159
+ const frameIds = orphaned.map((f) => f.frame_id);
160
+ let closed = 0;
161
+ const closeStmt = this.db.prepare(`
162
+ UPDATE frames
163
+ SET state = 'closed',
164
+ closed_at = unixepoch(),
165
+ outputs = json_set(COALESCE(outputs, '{}'), '$.recovered', true, '$.recoveryReason', 'orphan_cleanup')
166
+ WHERE frame_id = ?
167
+ `);
168
+ const transaction = this.db.transaction(() => {
169
+ for (const frame of orphaned) {
170
+ try {
171
+ closeStmt.run(frame.frame_id);
172
+ closed++;
173
+ logger.debug("Closed orphaned frame", {
174
+ frameId: frame.frame_id,
175
+ runId: frame.run_id,
176
+ name: frame.name,
177
+ age: Math.round((Date.now() / 1e3 - frame.created_at) / 3600) + "h"
178
+ });
179
+ } catch (err) {
180
+ logger.warn("Failed to close orphaned frame", {
181
+ frameId: frame.frame_id,
182
+ error: err
183
+ });
184
+ }
185
+ }
186
+ });
187
+ transaction();
188
+ return {
189
+ detected: orphaned.length,
190
+ recovered: 0,
191
+ // Future: could attempt to resume some frames
192
+ closed,
193
+ frameIds
194
+ };
195
+ }
196
+ /**
197
+ * Validate data integrity for a specific project
198
+ */
199
+ validateProjectIntegrity(projectId) {
200
+ const issues = [];
201
+ const invalidParents = this.db.prepare(
202
+ `
203
+ SELECT f1.frame_id, f1.parent_frame_id
204
+ FROM frames f1
205
+ LEFT JOIN frames f2 ON f1.parent_frame_id = f2.frame_id
206
+ WHERE f1.project_id = ?
207
+ AND f1.parent_frame_id IS NOT NULL
208
+ AND f2.frame_id IS NULL
209
+ `
210
+ ).all(projectId);
211
+ if (invalidParents.length > 0) {
212
+ issues.push(
213
+ `${invalidParents.length} frames with invalid parent references`
214
+ );
215
+ }
216
+ const depthIssues = this.db.prepare(
217
+ `
218
+ SELECT f1.frame_id, f1.depth as child_depth, f2.depth as parent_depth
219
+ FROM frames f1
220
+ JOIN frames f2 ON f1.parent_frame_id = f2.frame_id
221
+ WHERE f1.project_id = ?
222
+ AND f1.depth != f2.depth + 1
223
+ `
224
+ ).all(projectId);
225
+ if (depthIssues.length > 0) {
226
+ issues.push(`${depthIssues.length} frames with incorrect depth values`);
227
+ }
228
+ const orphanEvents = this.db.prepare(
229
+ `
230
+ SELECT COUNT(*) as count
231
+ FROM events e
232
+ LEFT JOIN frames f ON e.frame_id = f.frame_id
233
+ WHERE f.frame_id IS NULL
234
+ `
235
+ ).get();
236
+ if (orphanEvents.count > 0) {
237
+ issues.push(`${orphanEvents.count} orphaned events without valid frames`);
238
+ }
239
+ return {
240
+ valid: issues.length === 0,
241
+ issues
242
+ };
243
+ }
244
+ /**
245
+ * Clean up orphaned events (events without valid frames)
246
+ */
247
+ cleanupOrphanedEvents() {
248
+ const result = this.db.prepare(
249
+ `
250
+ DELETE FROM events
251
+ WHERE frame_id NOT IN (SELECT frame_id FROM frames)
252
+ `
253
+ ).run();
254
+ if (result.changes > 0) {
255
+ logger.info("Cleaned up orphaned events", { count: result.changes });
256
+ }
257
+ return result.changes;
258
+ }
259
+ /**
260
+ * Get recovery statistics
261
+ */
262
+ getRecoveryStats() {
263
+ const stats = this.db.prepare(
264
+ `
265
+ SELECT
266
+ COUNT(*) as total,
267
+ SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) as active,
268
+ SUM(CASE WHEN state = 'closed' THEN 1 ELSE 0 END) as closed,
269
+ SUM(CASE WHEN json_extract(outputs, '$.recovered') = true THEN 1 ELSE 0 END) as recovered
270
+ FROM frames
271
+ `
272
+ ).get();
273
+ const oldest = this.db.prepare(
274
+ `
275
+ SELECT datetime(created_at, 'unixepoch') as created
276
+ FROM frames
277
+ WHERE state = 'active'
278
+ ORDER BY created_at ASC
279
+ LIMIT 1
280
+ `
281
+ ).get();
282
+ return {
283
+ totalFrames: stats.total,
284
+ activeFrames: stats.active,
285
+ closedFrames: stats.closed,
286
+ recoveredFrames: stats.recovered,
287
+ oldestActiveFrame: oldest?.created ?? null
288
+ };
289
+ }
290
+ }
291
+ async function recoverDatabase(db, currentRunId) {
292
+ const recovery = new FrameRecovery(db);
293
+ if (currentRunId) {
294
+ recovery.setCurrentRunId(currentRunId);
295
+ }
296
+ return recovery.recoverOnStartup();
297
+ }
298
+ export {
299
+ FrameRecovery,
300
+ recoverDatabase
301
+ };
302
+ //# sourceMappingURL=frame-recovery.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/core/context/frame-recovery.ts"],
4
+ "sourcesContent": ["/**\n * Frame Recovery System\n * Handles crash recovery, integrity checks, and orphan cleanup\n *\n * Key responsibilities:\n * 1. Verify database integrity on startup\n * 2. Detect orphaned frames from crashed sessions\n * 3. Recover or close orphaned frames\n * 4. Provide data integrity validation\n */\n\nimport Database from 'better-sqlite3';\nimport { logger } from '../monitoring/logger.js';\n\nexport interface RecoveryReport {\n timestamp: string;\n integrityCheck: IntegrityCheckResult;\n orphanedFrames: OrphanedFrameResult;\n walStatus: WalStatus;\n recovered: boolean;\n errors: string[];\n}\n\nexport interface IntegrityCheckResult {\n passed: boolean;\n foreignKeyViolations: number;\n corruptedRows: number;\n errors: string[];\n}\n\nexport interface OrphanedFrameResult {\n detected: number;\n recovered: number;\n closed: number;\n frameIds: string[];\n}\n\nexport interface WalStatus {\n enabled: boolean;\n checkpointNeeded: boolean;\n walSize: number;\n}\n\ninterface OrphanedFrameRow {\n frame_id: string;\n run_id: string;\n project_id: string;\n name: string;\n type: string;\n created_at: number;\n depth: number;\n}\n\ninterface ForeignKeyViolation {\n table: string;\n rowid: number;\n parent: string;\n fkid: number;\n}\n\ninterface IntegrityRow {\n integrity_check: string;\n}\n\ninterface WalRow {\n busy: number;\n log: number;\n checkpointed: number;\n}\n\ninterface JournalModeRow {\n journal_mode: string;\n}\n\n/**\n * Frame Recovery Manager\n * Call recoverOnStartup() when initializing StackMemory\n */\nexport class FrameRecovery {\n // Sessions older than this are considered orphaned (default: 24 hours)\n private orphanThresholdMs: number;\n // Current session/run ID to exclude from orphan detection\n private currentRunId: string | null = null;\n\n constructor(\n private db: Database.Database,\n options: { orphanThresholdHours?: number } = {}\n ) {\n this.orphanThresholdMs =\n (options.orphanThresholdHours ?? 24) * 60 * 60 * 1000;\n }\n\n /**\n * Set the current run ID to exclude from orphan detection\n */\n setCurrentRunId(runId: string): void {\n this.currentRunId = runId;\n }\n\n /**\n * Main recovery entry point - call on startup\n */\n async recoverOnStartup(): Promise<RecoveryReport> {\n const errors: string[] = [];\n const timestamp = new Date().toISOString();\n\n logger.info('Starting crash recovery check');\n\n // 1. Check WAL status and checkpoint if needed\n const walStatus = this.checkWalStatus();\n if (walStatus.checkpointNeeded) {\n try {\n this.checkpointWal();\n logger.info('WAL checkpoint completed');\n } catch (err) {\n const msg = `WAL checkpoint failed: ${err instanceof Error ? err.message : String(err)}`;\n errors.push(msg);\n logger.warn(msg);\n }\n }\n\n // 2. Run integrity check\n const integrityCheck = this.runIntegrityCheck();\n if (!integrityCheck.passed) {\n logger.error('Database integrity check failed', {\n violations: integrityCheck.foreignKeyViolations,\n corrupted: integrityCheck.corruptedRows,\n });\n }\n\n // 3. Detect and handle orphaned frames\n const orphanedFrames = this.recoverOrphanedFrames();\n if (orphanedFrames.detected > 0) {\n logger.info('Orphaned frames processed', {\n detected: orphanedFrames.detected,\n recovered: orphanedFrames.recovered,\n closed: orphanedFrames.closed,\n });\n }\n\n const report: RecoveryReport = {\n timestamp,\n integrityCheck,\n orphanedFrames,\n walStatus,\n recovered:\n integrityCheck.passed &&\n orphanedFrames.detected === orphanedFrames.closed,\n errors,\n };\n\n logger.info('Crash recovery completed', {\n recovered: report.recovered,\n orphansFound: orphanedFrames.detected,\n integrityPassed: integrityCheck.passed,\n });\n\n return report;\n }\n\n /**\n * Check WAL mode status\n */\n checkWalStatus(): WalStatus {\n try {\n const journalMode = this.db.pragma('journal_mode') as JournalModeRow[];\n const isWal = journalMode[0]?.journal_mode === 'wal';\n\n if (!isWal) {\n return { enabled: false, checkpointNeeded: false, walSize: 0 };\n }\n\n // Check WAL size\n const walInfo = this.db.pragma('wal_checkpoint(PASSIVE)') as WalRow[];\n const walSize = walInfo[0]?.log ?? 0;\n const checkpointed = walInfo[0]?.checkpointed ?? 0;\n\n return {\n enabled: true,\n checkpointNeeded: walSize > 1000, // Checkpoint if WAL has > 1000 pages\n walSize: walSize - checkpointed,\n };\n } catch (err) {\n logger.warn('Failed to check WAL status', { error: err });\n return { enabled: false, checkpointNeeded: false, walSize: 0 };\n }\n }\n\n /**\n * Force WAL checkpoint\n */\n checkpointWal(): void {\n this.db.pragma('wal_checkpoint(TRUNCATE)');\n }\n\n /**\n * Run database integrity checks\n */\n runIntegrityCheck(): IntegrityCheckResult {\n const errors: string[] = [];\n let foreignKeyViolations = 0;\n let corruptedRows = 0;\n\n try {\n // Check foreign key constraints\n const fkViolations = this.db.pragma(\n 'foreign_key_check'\n ) as ForeignKeyViolation[];\n foreignKeyViolations = fkViolations.length;\n\n if (foreignKeyViolations > 0) {\n errors.push(`Found ${foreignKeyViolations} foreign key violations`);\n logger.warn('Foreign key violations detected', {\n count: foreignKeyViolations,\n samples: fkViolations.slice(0, 5),\n });\n }\n\n // Run integrity check\n const integrity = this.db.pragma('integrity_check') as IntegrityRow[];\n const integrityResult = integrity[0]?.integrity_check;\n\n if (integrityResult !== 'ok') {\n corruptedRows = integrity.length;\n errors.push(`Integrity check failed: ${integrityResult}`);\n logger.error('Database corruption detected', {\n result: integrityResult,\n });\n }\n } catch (err) {\n errors.push(\n `Integrity check error: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n return {\n passed: foreignKeyViolations === 0 && corruptedRows === 0,\n foreignKeyViolations,\n corruptedRows,\n errors,\n };\n }\n\n /**\n * Detect and recover orphaned frames\n * Orphaned = active frames from sessions that are no longer running\n */\n recoverOrphanedFrames(): OrphanedFrameResult {\n const orphanThreshold = Date.now() - this.orphanThresholdMs;\n const thresholdUnix = Math.floor(orphanThreshold / 1000);\n\n // Find active frames older than threshold (likely from crashed sessions)\n const query = `\n SELECT frame_id, run_id, project_id, name, type, created_at, depth\n FROM frames\n WHERE state = 'active'\n AND created_at < ?\n ${this.currentRunId ? 'AND run_id != ?' : ''}\n ORDER BY created_at ASC\n `;\n\n const params = this.currentRunId\n ? [thresholdUnix, this.currentRunId]\n : [thresholdUnix];\n\n const orphaned = this.db\n .prepare(query)\n .all(...params) as OrphanedFrameRow[];\n\n if (orphaned.length === 0) {\n return { detected: 0, recovered: 0, closed: 0, frameIds: [] };\n }\n\n const frameIds = orphaned.map((f) => f.frame_id);\n let closed = 0;\n\n // Close orphaned frames with a \"recovered\" state marker\n const closeStmt = this.db.prepare(`\n UPDATE frames\n SET state = 'closed',\n closed_at = unixepoch(),\n outputs = json_set(COALESCE(outputs, '{}'), '$.recovered', true, '$.recoveryReason', 'orphan_cleanup')\n WHERE frame_id = ?\n `);\n\n const transaction = this.db.transaction(() => {\n for (const frame of orphaned) {\n try {\n closeStmt.run(frame.frame_id);\n closed++;\n logger.debug('Closed orphaned frame', {\n frameId: frame.frame_id,\n runId: frame.run_id,\n name: frame.name,\n age:\n Math.round((Date.now() / 1000 - frame.created_at) / 3600) + 'h',\n });\n } catch (err) {\n logger.warn('Failed to close orphaned frame', {\n frameId: frame.frame_id,\n error: err,\n });\n }\n }\n });\n\n transaction();\n\n return {\n detected: orphaned.length,\n recovered: 0, // Future: could attempt to resume some frames\n closed,\n frameIds,\n };\n }\n\n /**\n * Validate data integrity for a specific project\n */\n validateProjectIntegrity(projectId: string): {\n valid: boolean;\n issues: string[];\n } {\n const issues: string[] = [];\n\n // Check for frames with invalid parent references\n const invalidParents = this.db\n .prepare(\n `\n SELECT f1.frame_id, f1.parent_frame_id\n FROM frames f1\n LEFT JOIN frames f2 ON f1.parent_frame_id = f2.frame_id\n WHERE f1.project_id = ?\n AND f1.parent_frame_id IS NOT NULL\n AND f2.frame_id IS NULL\n `\n )\n .all(projectId) as Array<{ frame_id: string; parent_frame_id: string }>;\n\n if (invalidParents.length > 0) {\n issues.push(\n `${invalidParents.length} frames with invalid parent references`\n );\n }\n\n // Check for depth inconsistencies\n const depthIssues = this.db\n .prepare(\n `\n SELECT f1.frame_id, f1.depth as child_depth, f2.depth as parent_depth\n FROM frames f1\n JOIN frames f2 ON f1.parent_frame_id = f2.frame_id\n WHERE f1.project_id = ?\n AND f1.depth != f2.depth + 1\n `\n )\n .all(projectId) as Array<{\n frame_id: string;\n child_depth: number;\n parent_depth: number;\n }>;\n\n if (depthIssues.length > 0) {\n issues.push(`${depthIssues.length} frames with incorrect depth values`);\n }\n\n // Check for events without valid frames\n const orphanEvents = this.db\n .prepare(\n `\n SELECT COUNT(*) as count\n FROM events e\n LEFT JOIN frames f ON e.frame_id = f.frame_id\n WHERE f.frame_id IS NULL\n `\n )\n .get() as { count: number };\n\n if (orphanEvents.count > 0) {\n issues.push(`${orphanEvents.count} orphaned events without valid frames`);\n }\n\n return {\n valid: issues.length === 0,\n issues,\n };\n }\n\n /**\n * Clean up orphaned events (events without valid frames)\n */\n cleanupOrphanedEvents(): number {\n const result = this.db\n .prepare(\n `\n DELETE FROM events\n WHERE frame_id NOT IN (SELECT frame_id FROM frames)\n `\n )\n .run();\n\n if (result.changes > 0) {\n logger.info('Cleaned up orphaned events', { count: result.changes });\n }\n\n return result.changes;\n }\n\n /**\n * Get recovery statistics\n */\n getRecoveryStats(): {\n totalFrames: number;\n activeFrames: number;\n closedFrames: number;\n recoveredFrames: number;\n oldestActiveFrame: string | null;\n } {\n const stats = this.db\n .prepare(\n `\n SELECT\n COUNT(*) as total,\n SUM(CASE WHEN state = 'active' THEN 1 ELSE 0 END) as active,\n SUM(CASE WHEN state = 'closed' THEN 1 ELSE 0 END) as closed,\n SUM(CASE WHEN json_extract(outputs, '$.recovered') = true THEN 1 ELSE 0 END) as recovered\n FROM frames\n `\n )\n .get() as {\n total: number;\n active: number;\n closed: number;\n recovered: number;\n };\n\n const oldest = this.db\n .prepare(\n `\n SELECT datetime(created_at, 'unixepoch') as created\n FROM frames\n WHERE state = 'active'\n ORDER BY created_at ASC\n LIMIT 1\n `\n )\n .get() as { created: string } | undefined;\n\n return {\n totalFrames: stats.total,\n activeFrames: stats.active,\n closedFrames: stats.closed,\n recoveredFrames: stats.recovered,\n oldestActiveFrame: oldest?.created ?? null,\n };\n }\n}\n\n/**\n * Convenience function to run recovery on a database\n */\nexport async function recoverDatabase(\n db: Database.Database,\n currentRunId?: string\n): Promise<RecoveryReport> {\n const recovery = new FrameRecovery(db);\n if (currentRunId) {\n recovery.setCurrentRunId(currentRunId);\n }\n return recovery.recoverOnStartup();\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }