@claude-flow/shared 3.0.0-alpha.1 → 3.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (241) hide show
  1. package/.claude-flow/daemon-state.json +135 -0
  2. package/.claude-flow/data/pending-insights.jsonl +2 -0
  3. package/.claude-flow/data/ranked-context.json +5 -0
  4. package/.claude-flow/logs/daemon.log +45 -0
  5. package/.claude-flow/logs/headless/audit_1777379186972_h5un5x_prompt.log +3210 -0
  6. package/.claude-flow/logs/headless/audit_1777379186972_h5un5x_result.log +117 -0
  7. package/.claude-flow/logs/headless/audit_1777379816437_w0eaul_prompt.log +3210 -0
  8. package/.claude-flow/logs/headless/audit_1777379816437_w0eaul_result.log +53 -0
  9. package/.claude-flow/logs/headless/audit_1777380440097_621y8m_prompt.log +3210 -0
  10. package/.claude-flow/logs/headless/audit_1777380440097_621y8m_result.log +75 -0
  11. package/.claude-flow/logs/headless/optimize_1777379306973_an4lmy_prompt.log +3504 -0
  12. package/.claude-flow/logs/headless/optimize_1777379306973_an4lmy_result.log +166 -0
  13. package/.claude-flow/logs/headless/optimize_1777380274732_apxz3s_prompt.log +3504 -0
  14. package/.claude-flow/logs/headless/optimize_1777380274732_apxz3s_result.log +219 -0
  15. package/.claude-flow/logs/headless/testgaps_1777379546969_dvf2a1_prompt.log +3189 -0
  16. package/.claude-flow/logs/headless/testgaps_1777379546969_dvf2a1_result.log +155 -0
  17. package/.claude-flow/metrics/codebase-map.json +11 -0
  18. package/.claude-flow/metrics/consolidation.json +6 -0
  19. package/.claude-flow/sessions/current.json +13 -0
  20. package/.swarm/hnsw.index +0 -0
  21. package/.swarm/hnsw.metadata.json +1 -0
  22. package/.swarm/memory.db +0 -0
  23. package/.swarm/memory.db-shm +0 -0
  24. package/.swarm/memory.db-wal +0 -0
  25. package/.swarm/schema.sql +305 -0
  26. package/dist/core/config/loader.d.ts.map +1 -1
  27. package/dist/core/config/loader.js +17 -1
  28. package/dist/core/config/loader.js.map +1 -1
  29. package/dist/core/config/schema.d.ts +697 -103
  30. package/dist/core/config/schema.d.ts.map +1 -1
  31. package/dist/core/config/schema.js +3 -1
  32. package/dist/core/config/schema.js.map +1 -1
  33. package/dist/events/event-store.d.ts.map +1 -1
  34. package/dist/events/event-store.js +20 -9
  35. package/dist/events/event-store.js.map +1 -1
  36. package/dist/events/example-usage.js +1 -1
  37. package/dist/events/example-usage.js.map +1 -1
  38. package/dist/events/index.d.ts +2 -0
  39. package/dist/events/index.d.ts.map +1 -1
  40. package/dist/events/index.js +2 -0
  41. package/dist/events/index.js.map +1 -1
  42. package/dist/events/rvf-event-log.d.ts +82 -0
  43. package/dist/events/rvf-event-log.d.ts.map +1 -0
  44. package/dist/events/rvf-event-log.js +340 -0
  45. package/dist/events/rvf-event-log.js.map +1 -0
  46. package/dist/hooks/example-usage.js +3 -3
  47. package/dist/hooks/example-usage.js.map +1 -1
  48. package/dist/hooks/executor.d.ts.map +1 -1
  49. package/dist/hooks/executor.js +7 -4
  50. package/dist/hooks/executor.js.map +1 -1
  51. package/dist/hooks/verify-exports.test.js +6 -6
  52. package/dist/hooks/verify-exports.test.js.map +1 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/mcp/server.d.ts.map +1 -1
  58. package/dist/mcp/server.js +3 -6
  59. package/dist/mcp/server.js.map +1 -1
  60. package/dist/mcp/types.d.ts +4 -6
  61. package/dist/mcp/types.d.ts.map +1 -1
  62. package/dist/mcp/types.js.map +1 -1
  63. package/dist/plugins/official/hive-mind-plugin.js +2 -2
  64. package/dist/plugins/official/hive-mind-plugin.js.map +1 -1
  65. package/dist/plugins/official/maestro-plugin.js +3 -3
  66. package/dist/plugins/official/maestro-plugin.js.map +1 -1
  67. package/dist/services/index.d.ts +7 -0
  68. package/dist/services/index.d.ts.map +1 -0
  69. package/dist/services/index.js +7 -0
  70. package/dist/services/index.js.map +1 -0
  71. package/dist/services/v3-progress.service.d.ts +124 -0
  72. package/dist/services/v3-progress.service.d.ts.map +1 -0
  73. package/dist/services/v3-progress.service.js +402 -0
  74. package/dist/services/v3-progress.service.js.map +1 -0
  75. package/package.json +12 -3
  76. package/ruvector.db +0 -0
  77. package/src/core/config/loader.ts +17 -1
  78. package/src/core/config/schema.ts +3 -1
  79. package/src/events/event-store.ts +18 -9
  80. package/src/events/example-usage.ts +1 -1
  81. package/src/events/index.ts +4 -0
  82. package/src/events/rvf-event-log.ts +427 -0
  83. package/src/hooks/example-usage.ts +3 -3
  84. package/src/hooks/executor.ts +7 -5
  85. package/src/hooks/verify-exports.test.ts +6 -6
  86. package/src/index.ts +5 -0
  87. package/src/mcp/server.ts +3 -6
  88. package/src/mcp/types.ts +4 -6
  89. package/src/plugins/official/hive-mind-plugin.ts +2 -2
  90. package/src/plugins/official/maestro-plugin.ts +3 -3
  91. package/src/services/index.ts +16 -0
  92. package/src/services/v3-progress.service.ts +505 -0
  93. package/tmp.json +0 -0
  94. package/tsconfig.tsbuildinfo +1 -1
  95. package/.agentic-flow/intelligence.json +0 -16
  96. package/__tests__/coverage/base.css +0 -224
  97. package/__tests__/coverage/block-navigation.js +0 -87
  98. package/__tests__/coverage/coverage-final.json +0 -50
  99. package/__tests__/coverage/favicon.png +0 -0
  100. package/__tests__/coverage/index.html +0 -326
  101. package/__tests__/coverage/lcov-report/base.css +0 -224
  102. package/__tests__/coverage/lcov-report/block-navigation.js +0 -87
  103. package/__tests__/coverage/lcov-report/favicon.png +0 -0
  104. package/__tests__/coverage/lcov-report/index.html +0 -326
  105. package/__tests__/coverage/lcov-report/prettify.css +0 -1
  106. package/__tests__/coverage/lcov-report/prettify.js +0 -2
  107. package/__tests__/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  108. package/__tests__/coverage/lcov-report/sorter.js +0 -210
  109. package/__tests__/coverage/lcov-report/src/core/config/defaults.ts.html +0 -706
  110. package/__tests__/coverage/lcov-report/src/core/config/index.html +0 -161
  111. package/__tests__/coverage/lcov-report/src/core/config/loader.ts.html +0 -898
  112. package/__tests__/coverage/lcov-report/src/core/config/schema.ts.html +0 -649
  113. package/__tests__/coverage/lcov-report/src/core/config/validator.ts.html +0 -712
  114. package/__tests__/coverage/lcov-report/src/core/event-bus.ts.html +0 -793
  115. package/__tests__/coverage/lcov-report/src/core/index.html +0 -116
  116. package/__tests__/coverage/lcov-report/src/core/interfaces/event.interface.ts.html +0 -886
  117. package/__tests__/coverage/lcov-report/src/core/interfaces/index.html +0 -116
  118. package/__tests__/coverage/lcov-report/src/core/orchestrator/event-coordinator.ts.html +0 -451
  119. package/__tests__/coverage/lcov-report/src/core/orchestrator/health-monitor.ts.html +0 -727
  120. package/__tests__/coverage/lcov-report/src/core/orchestrator/index.html +0 -176
  121. package/__tests__/coverage/lcov-report/src/core/orchestrator/lifecycle-manager.ts.html +0 -874
  122. package/__tests__/coverage/lcov-report/src/core/orchestrator/session-manager.ts.html +0 -922
  123. package/__tests__/coverage/lcov-report/src/core/orchestrator/task-manager.ts.html +0 -1036
  124. package/__tests__/coverage/lcov-report/src/events/domain-events.ts.html +0 -1837
  125. package/__tests__/coverage/lcov-report/src/events/event-store.ts.html +0 -1849
  126. package/__tests__/coverage/lcov-report/src/events/example-usage.ts.html +0 -964
  127. package/__tests__/coverage/lcov-report/src/events/index.html +0 -176
  128. package/__tests__/coverage/lcov-report/src/events/projections.ts.html +0 -1768
  129. package/__tests__/coverage/lcov-report/src/events/state-reconstructor.ts.html +0 -1132
  130. package/__tests__/coverage/lcov-report/src/events.ts.html +0 -1186
  131. package/__tests__/coverage/lcov-report/src/hooks/example-usage.ts.html +0 -1582
  132. package/__tests__/coverage/lcov-report/src/hooks/executor.ts.html +0 -1222
  133. package/__tests__/coverage/lcov-report/src/hooks/index.html +0 -191
  134. package/__tests__/coverage/lcov-report/src/hooks/registry.ts.html +0 -1084
  135. package/__tests__/coverage/lcov-report/src/hooks/safety/bash-safety.ts.html +0 -1897
  136. package/__tests__/coverage/lcov-report/src/hooks/safety/file-organization.ts.html +0 -1504
  137. package/__tests__/coverage/lcov-report/src/hooks/safety/git-commit.ts.html +0 -1954
  138. package/__tests__/coverage/lcov-report/src/hooks/safety/index.html +0 -146
  139. package/__tests__/coverage/lcov-report/src/hooks/session-hooks.ts.html +0 -1762
  140. package/__tests__/coverage/lcov-report/src/hooks/task-hooks.ts.html +0 -1624
  141. package/__tests__/coverage/lcov-report/src/hooks/types.ts.html +0 -1156
  142. package/__tests__/coverage/lcov-report/src/index.html +0 -176
  143. package/__tests__/coverage/lcov-report/src/mcp/connection-pool.ts.html +0 -1399
  144. package/__tests__/coverage/lcov-report/src/mcp/index.html +0 -176
  145. package/__tests__/coverage/lcov-report/src/mcp/server.ts.html +0 -2407
  146. package/__tests__/coverage/lcov-report/src/mcp/session-manager.ts.html +0 -1369
  147. package/__tests__/coverage/lcov-report/src/mcp/tool-registry.ts.html +0 -1783
  148. package/__tests__/coverage/lcov-report/src/mcp/transport/http.ts.html +0 -1756
  149. package/__tests__/coverage/lcov-report/src/mcp/transport/index.html +0 -146
  150. package/__tests__/coverage/lcov-report/src/mcp/transport/stdio.ts.html +0 -1057
  151. package/__tests__/coverage/lcov-report/src/mcp/transport/websocket.ts.html +0 -1537
  152. package/__tests__/coverage/lcov-report/src/mcp/types.ts.html +0 -1780
  153. package/__tests__/coverage/lcov-report/src/plugin-interface.ts.html +0 -2074
  154. package/__tests__/coverage/lcov-report/src/plugin-loader.ts.html +0 -1999
  155. package/__tests__/coverage/lcov-report/src/plugin-registry.ts.html +0 -1897
  156. package/__tests__/coverage/lcov-report/src/plugins/official/hive-mind-plugin.ts.html +0 -1075
  157. package/__tests__/coverage/lcov-report/src/plugins/official/index.html +0 -131
  158. package/__tests__/coverage/lcov-report/src/plugins/official/maestro-plugin.ts.html +0 -1609
  159. package/__tests__/coverage/lcov-report/src/resilience/bulkhead.ts.html +0 -916
  160. package/__tests__/coverage/lcov-report/src/resilience/circuit-breaker.ts.html +0 -1063
  161. package/__tests__/coverage/lcov-report/src/resilience/index.html +0 -161
  162. package/__tests__/coverage/lcov-report/src/resilience/rate-limiter.ts.html +0 -1345
  163. package/__tests__/coverage/lcov-report/src/resilience/retry.ts.html +0 -757
  164. package/__tests__/coverage/lcov-report/src/security/index.html +0 -131
  165. package/__tests__/coverage/lcov-report/src/security/input-validation.ts.html +0 -880
  166. package/__tests__/coverage/lcov-report/src/security/secure-random.ts.html +0 -562
  167. package/__tests__/coverage/lcov-report/src/types/index.html +0 -131
  168. package/__tests__/coverage/lcov-report/src/types/swarm.types.ts.html +0 -850
  169. package/__tests__/coverage/lcov-report/src/types/task.types.ts.html +0 -700
  170. package/__tests__/coverage/lcov-report/src/types.ts.html +0 -1186
  171. package/__tests__/coverage/lcov-report/src/utils/index.html +0 -116
  172. package/__tests__/coverage/lcov-report/src/utils/secure-logger.ts.html +0 -856
  173. package/__tests__/coverage/lcov.info +0 -19877
  174. package/__tests__/coverage/prettify.css +0 -1
  175. package/__tests__/coverage/prettify.js +0 -2
  176. package/__tests__/coverage/sort-arrow-sprite.png +0 -0
  177. package/__tests__/coverage/sorter.js +0 -210
  178. package/__tests__/coverage/src/core/config/defaults.ts.html +0 -706
  179. package/__tests__/coverage/src/core/config/index.html +0 -161
  180. package/__tests__/coverage/src/core/config/loader.ts.html +0 -898
  181. package/__tests__/coverage/src/core/config/schema.ts.html +0 -649
  182. package/__tests__/coverage/src/core/config/validator.ts.html +0 -712
  183. package/__tests__/coverage/src/core/event-bus.ts.html +0 -793
  184. package/__tests__/coverage/src/core/index.html +0 -116
  185. package/__tests__/coverage/src/core/interfaces/event.interface.ts.html +0 -886
  186. package/__tests__/coverage/src/core/interfaces/index.html +0 -116
  187. package/__tests__/coverage/src/core/orchestrator/event-coordinator.ts.html +0 -451
  188. package/__tests__/coverage/src/core/orchestrator/health-monitor.ts.html +0 -727
  189. package/__tests__/coverage/src/core/orchestrator/index.html +0 -176
  190. package/__tests__/coverage/src/core/orchestrator/lifecycle-manager.ts.html +0 -874
  191. package/__tests__/coverage/src/core/orchestrator/session-manager.ts.html +0 -922
  192. package/__tests__/coverage/src/core/orchestrator/task-manager.ts.html +0 -1036
  193. package/__tests__/coverage/src/events/domain-events.ts.html +0 -1837
  194. package/__tests__/coverage/src/events/event-store.ts.html +0 -1849
  195. package/__tests__/coverage/src/events/example-usage.ts.html +0 -964
  196. package/__tests__/coverage/src/events/index.html +0 -176
  197. package/__tests__/coverage/src/events/projections.ts.html +0 -1768
  198. package/__tests__/coverage/src/events/state-reconstructor.ts.html +0 -1132
  199. package/__tests__/coverage/src/events.ts.html +0 -1186
  200. package/__tests__/coverage/src/hooks/example-usage.ts.html +0 -1582
  201. package/__tests__/coverage/src/hooks/executor.ts.html +0 -1222
  202. package/__tests__/coverage/src/hooks/index.html +0 -191
  203. package/__tests__/coverage/src/hooks/registry.ts.html +0 -1084
  204. package/__tests__/coverage/src/hooks/safety/bash-safety.ts.html +0 -1897
  205. package/__tests__/coverage/src/hooks/safety/file-organization.ts.html +0 -1504
  206. package/__tests__/coverage/src/hooks/safety/git-commit.ts.html +0 -1954
  207. package/__tests__/coverage/src/hooks/safety/index.html +0 -146
  208. package/__tests__/coverage/src/hooks/session-hooks.ts.html +0 -1762
  209. package/__tests__/coverage/src/hooks/task-hooks.ts.html +0 -1624
  210. package/__tests__/coverage/src/hooks/types.ts.html +0 -1156
  211. package/__tests__/coverage/src/index.html +0 -176
  212. package/__tests__/coverage/src/mcp/connection-pool.ts.html +0 -1399
  213. package/__tests__/coverage/src/mcp/index.html +0 -176
  214. package/__tests__/coverage/src/mcp/server.ts.html +0 -2407
  215. package/__tests__/coverage/src/mcp/session-manager.ts.html +0 -1369
  216. package/__tests__/coverage/src/mcp/tool-registry.ts.html +0 -1783
  217. package/__tests__/coverage/src/mcp/transport/http.ts.html +0 -1756
  218. package/__tests__/coverage/src/mcp/transport/index.html +0 -146
  219. package/__tests__/coverage/src/mcp/transport/stdio.ts.html +0 -1057
  220. package/__tests__/coverage/src/mcp/transport/websocket.ts.html +0 -1537
  221. package/__tests__/coverage/src/mcp/types.ts.html +0 -1780
  222. package/__tests__/coverage/src/plugin-interface.ts.html +0 -2074
  223. package/__tests__/coverage/src/plugin-loader.ts.html +0 -1999
  224. package/__tests__/coverage/src/plugin-registry.ts.html +0 -1897
  225. package/__tests__/coverage/src/plugins/official/hive-mind-plugin.ts.html +0 -1075
  226. package/__tests__/coverage/src/plugins/official/index.html +0 -131
  227. package/__tests__/coverage/src/plugins/official/maestro-plugin.ts.html +0 -1609
  228. package/__tests__/coverage/src/resilience/bulkhead.ts.html +0 -916
  229. package/__tests__/coverage/src/resilience/circuit-breaker.ts.html +0 -1063
  230. package/__tests__/coverage/src/resilience/index.html +0 -161
  231. package/__tests__/coverage/src/resilience/rate-limiter.ts.html +0 -1345
  232. package/__tests__/coverage/src/resilience/retry.ts.html +0 -757
  233. package/__tests__/coverage/src/security/index.html +0 -131
  234. package/__tests__/coverage/src/security/input-validation.ts.html +0 -880
  235. package/__tests__/coverage/src/security/secure-random.ts.html +0 -562
  236. package/__tests__/coverage/src/types/index.html +0 -131
  237. package/__tests__/coverage/src/types/swarm.types.ts.html +0 -850
  238. package/__tests__/coverage/src/types/task.types.ts.html +0 -700
  239. package/__tests__/coverage/src/types.ts.html +0 -1186
  240. package/__tests__/coverage/src/utils/index.html +0 -116
  241. package/__tests__/coverage/src/utils/secure-logger.ts.html +0 -856
@@ -0,0 +1,219 @@
1
+ [2026-04-28T12:46:43.991Z] RESULT
2
+ ============================================================
3
+ {
4
+ "success": true,
5
+ "output": "Now I have a complete picture. Here's the full performance analysis:\n\n---\n\n## Performance Analysis\n\nThis is a TypeScript backend orchestration system (no React). I've identified issues across memory leaks, redundant computation, missing caches, and one query pattern.\n\n---\n\n### 1. Memory Leaks\n\n#### `session-manager.ts` — Terminated sessions never removed\n`terminateSession()` marks a session terminated but never removes it from `this.sessions`. Terminated sessions accumulate indefinitely.\n\n```typescript\n// BUG: session stays in map forever after termination\nasync terminateSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n session.status = 'terminated';\n session.endTime = new Date();\n this.sessionProfiles.delete(sessionId); // only cleans up profile\n // this.sessions still holds the entry\n}\n```\n\n**Fix — remove from map at termination:**\n```typescript\nasync terminateSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) throw new Error(`Session not found: ${sessionId}`);\n\n session.status = 'terminated';\n session.endTime = new Date();\n const duration = session.endTime.getTime() - session.startTime.getTime();\n\n this.eventBus.emit(SystemEventTypes.SESSION_TERMINATED, {\n sessionId, agentId: session.agentId, duration,\n });\n\n // Clean up both maps immediately\n this.sessions.delete(sessionId);\n this.sessionProfiles.delete(sessionId);\n\n this.persistSessions().catch(() => {});\n}\n```\n\n#### `event-coordinator.ts` — `shutdown()` leaves event bus handlers registered\n`registerHandler` subscribes to the event bus AND stores locally. `shutdown()` only clears the local map — the event bus keeps all subscriptions alive and will keep invoking them.\n\n```typescript\n// Only clears local map, NOT event bus subscriptions\nasync shutdown(): Promise<void> {\n this.handlers.clear(); // ← event bus is still subscribed\n this.initialized = false;\n}\n```\n\n**Fix — unregister from event bus on shutdown:**\n```typescript\nasync shutdown(): Promise<void> {\n for (const [type, handlers] of this.handlers.entries()) {\n for (const handler of handlers) {\n this.eventBus.off(type, handler);\n }\n }\n this.handlers.clear();\n this.initialized = false;\n}\n```\n\n#### `task-manager.ts` — Tasks accumulate without auto-pruning\n`this.tasks` (a Map) grows forever. `cleanup()` exists but is never called automatically. In a long-running process with thousands of tasks per day this is a significant leak.\n\n**Fix — add a periodic cleanup or prune on completion:**\n```typescript\nconstructor(private eventBus: IEventBus, queue?: ITaskQueue) {\n this.queue = queue ?? new TaskQueue();\n // Prune completed/failed tasks older than 1 hour every 10 minutes\n setInterval(() => {\n const cutoff = new Date(Date.now() - 3_600_000);\n this.cleanup(cutoff).catch(() => {});\n }, 600_000).unref();\n}\n```\n\n#### `health-monitor.ts` — `listeners` array not cleared on `stop()`\nWhen `stop()` is called the interval is cleared, but `this.listeners` keeps all callbacks referenced.\n\n```typescript\nstop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n this.running = false;\n // BUG: listeners array still holds external references\n}\n```\n\n**Fix:**\n```typescript\nstop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n this.listeners = [];\n this.running = false;\n}\n```\n\n---\n\n### 2. Redundant Computations\n\n#### `task-manager.ts` — `TaskQueue.enqueue()` re-sorts entire array on every insert\n\n`O(n log n)` per insertion; with a priority queue (min-heap) this is `O(log n)`.\n\n```typescript\n// Current: O(n log n) per insert\nasync enqueue(task: ITask): Promise<void> {\n this.tasks.push(task);\n this.tasks.sort((a, b) => b.priority - a.priority);\n}\n```\n\n**Fix — insertion into sorted position, `O(n)` worst case but avoids full re-sort:**\n```typescript\nasync enqueue(task: ITask): Promise<void> {\n // Binary search for insertion point\n let lo = 0, hi = this.tasks.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this.tasks[mid].priority >= task.priority) lo = mid + 1;\n else hi = mid;\n }\n this.tasks.splice(lo, 0, task);\n}\n```\n\nFor very high enqueue rates, replace `ITask[]` with a proper binary heap. The current approach re-sorts even when the queue has hundreds of entries.\n\n#### `event-bus.ts` — `dispatchEvent()` allocates a new `Set` on every single event\n\n```typescript\nprivate dispatchEvent<T>(event: IEvent<T>): void {\n const typeHandlers = this.handlers.get(event.type);\n const wildcardHandlers = this.handlers.get('*');\n\n // NEW Set + copy on every dispatch — unnecessary when no wildcards\n const allHandlers = new Set<IEventHandler>();\n if (typeHandlers) {\n for (const handler of typeHandlers) allHandlers.add(handler);\n }\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) allHandlers.add(handler);\n }\n // ...\n}\n```\n\n**Fix — fast-path when no wildcards (the common case):**\n```typescript\nprivate dispatchEvent<T>(event: IEvent<T>): void {\n const typeHandlers = this.handlers.get(event.type);\n const wildcardHandlers = this.handlers.get('*');\n\n if (!wildcardHandlers || wildcardHandlers.size === 0) {\n // Fast path: no allocation needed\n if (typeHandlers) {\n for (const handler of typeHandlers) this.invokeHandler(handler, event);\n }\n return;\n }\n\n // Slow path: need dedup between type + wildcard handlers\n const allHandlers = new Set<IEventHandler>([\n ...(typeHandlers ?? []),\n ...wildcardHandlers,\n ]);\n for (const handler of allHandlers) this.invokeHandler(handler, event);\n}\n\nprivate invokeHandler<T>(handler: IEventHandler, event: IEvent<T>): void {\n try {\n const result = handler(event);\n if (result instanceof Promise) result.catch(err =>\n console.error(`Error in async event handler for ${event.type}:`, err)\n );\n } catch (error) {\n console.error(`Error in event handler for ${event.type}:`, error);\n }\n}\n```\n\n#### `health-monitor.ts` — `addToHistory` creates a new array on every trim\n\n```typescript\nprivate addToHistory(status: IHealthStatus): void {\n this.history.push(status);\n if (this.history.length > this.config.historyLimit) {\n this.history = this.history.slice(-this.config.historyLimit); // new array every time\n }\n}\n```\n\n**Fix — shift instead of slice (keeps array in place):**\n```typescript\nprivate addToHistory(status: IHealthStatus): void {\n this.history.push(status);\n if (this.history.length > this.config.historyLimit) {\n this.history.shift(); // O(n) but avoids allocation; or use a circular buffer\n }\n}\n```\n\nFor high-frequency health checks, a fixed-size circular buffer is ideal (`O(1)` insert/trim).\n\n#### `lifecycle-manager.ts` — multiple calls to `getAll()` create redundant copies\n\n`getByStatus()`, `getByType()`, and `getAvailable()` each call `getAll()`, which allocates a new array via `Array.from()`.\n\n```typescript\ngetByStatus(status: AgentStatus): IAgent[] {\n return this.getAll().filter(agent => agent.status === status); // new array\n}\ngetByType(type: string): IAgent[] {\n return this.getAll().filter(agent => agent.type === type); // new array\n}\n```\n\n**Fix — iterate the Map directly:**\n```typescript\ngetByStatus(status: AgentStatus): IAgent[] {\n const result: IAgent[] = [];\n for (const agent of this.agents.values()) {\n if (agent.status === status) result.push(agent);\n }\n return result;\n}\n```\n\n#### `task-manager.ts` — `getTasks()` makes multiple passes over the same array\n\n```typescript\ngetTasks(filter?) {\n let tasks = Array.from(this.tasks.values()); // copy\n if (filter?.status) tasks = tasks.filter(t => t.status === filter.status);\n if (filter?.type) tasks = tasks.filter(t => t.type === filter.type);\n if (filter?.assignedAgent) tasks = tasks.filter(t => t.assignedAgent === filter.assignedAgent);\n return tasks;\n}\n```\n\nThree separate `.filter()` passes, each producing an intermediate array.\n\n**Fix — single pass:**\n```typescript\ngetTasks(filter?: Partial<Pick<ITask, 'status' | 'type' | 'assignedAgent'>>): ITask[] {\n const result: ITask[] = [];\n for (const task of this.tasks.values()) {\n if (filter?.status && task.status !== filter.status) continue;\n if (filter?.type && task.type !== filter.type) continue;\n if (filter?.assignedAgent && task.assignedAgent !== filter.assignedAgent) continue;\n result.push(task);\n }\n return result;\n}\n```\n\n---\n\n### 3. Caching Opportunities\n\n#### `config/loader.ts` — no result caching; re-reads disk on every `load()` call\n\n`ConfigLoader.load()` hits the filesystem and validates on every invocation. In a CLI that calls it from multiple commands, this is wasteful.\n\n**Fix — memoize after first successful load:**\n```typescript\nexport class ConfigLoader {\n private cachedConfig: LoadedConfig | null = null;\n\n async load(forceReload = false): Promise<LoadedConfig> {\n if (this.cachedConfig && !forceReload) return this.cachedConfig;\n const result = await this._load();\n this.cachedConfig = result;\n return result;\n }\n\n invalidate(): void {\n this.cachedConfig = null;\n }\n}\n```\n\n#### `event-store.ts` — `getStats()` runs 5 sequential SQL queries\n\nEvery call to `getStats()` issues five separate SQL statements that could be combined or cached.\n\n```typescript\n// 5 separate round-trips to SQLite\nconst totalStmt = this.db!.prepare('SELECT COUNT(*) ...');\nconst typeStmt = this.db!.prepare('SELECT type, COUNT(*) ... GROUP BY type');\nconst aggStmt = this.db!.prepare('SELECT aggregate_id, COUNT(*) ... GROUP BY aggregate_id');\nconst rangeStmt = this.db!.prepare('SELECT MIN(timestamp), MAX(timestamp) ...');\nconst snapshotStmt = this.db!.prepare('SELECT COUNT(*) FROM snapshots');\n```\n\n**Fix — cache stats with a short TTL, or merge into fewer queries:**\n```typescript\nprivate statsCache: { stats: EventStoreStats; cachedAt: number } | null = null;\nprivate readonly STATS_TTL_MS = 5000;\n\nasync getStats(): Promise<EventStoreStats> {\n const now = Date.now();\n if (this.statsCache && now - this.statsCache.cachedAt < this.STATS_TTL_MS) {\n return this.statsCache.stats;\n }\n const stats = await this.computeStats();\n this.statsCache = { stats, cachedAt: now };\n return stats;\n}\n```\n\n#### `connection-pool.ts` — `getStats()` iterates all connections on every call\n\n```typescript\ngetStats(): ConnectionPoolStats {\n let idleCount = 0, busyCount = 0;\n for (const connection of this.connections.values()) { // full scan every call\n if (connection.state === 'idle') idleCount++;\n else if (connection.state === 'busy') busyCount++;\n }\n // ...\n}\n```\n\n**Fix — maintain separate counters updated on `acquire()`/`release()`/`destroy()`:**\n```typescript\nprivate idleCount = 0;\nprivate busyCount = 0;\n\n// In acquire(): this.idleCount--; this.busyCount++;\n// In release(): this.busyCount--; this.idleCount++;\n// In destroy(): if (conn.state === 'idle') this.idleCount--;\n// else if (conn.state === 'busy') this.busyCount--;\n\ngetStats(): ConnectionPoolStats {\n return {\n totalConnections: this.connections.size,\n idleConnections: this.idleCount,\n busyConnections: this.busyCount,\n // ...\n };\n}\n```\n\n---\n\n### 4. N+1 Query Pattern\n\n#### `event-store.ts` — `loadAggregateVersions()` loads ALL versions at startup\n\nOn startup the store loads every aggregate's max version into memory. With millions of events this becomes a large in-memory map. The actual problem arises in `append()` — the version is fetched from the in-memory map, but if the store is used concurrently (e.g. multiple workers) there's no serialization and the version tracker diverges.\n\nThe subtler N+1 is in how consumers typically use this: calling `getEvents(aggregateId)` per aggregate in a loop rather than batching via `query({ aggregateIds: [...] })`. The `query()` method already supports batching — consumers should prefer it.\n\n**Guidance:** Prefer `query({ aggregateIds: ['a','b','c'] })` over a loop of `getEvents()` calls. Add a JSDoc note to `getEvents()` calling this out.\n\n#### `event-store.ts` — `getSnapshot()` uses `getAsObject` incorrectly (latent bug)\n\n```typescript\nconst row = stmt.getAsObject([aggregateId]); // passes bind params to getAsObject, not bind()\n```\n\n`getAsObject()` doesn't accept bind params. This should be:\n```typescript\nstmt.bind([aggregateId]);\nconst row = stmt.step() ? stmt.getAsObject() : null;\nstmt.free();\n```\n\n---\n\n### 5. Missing React Re-renders\n\nThis codebase has no React — no action needed here.\n\n---\n\n### Priority Summary\n\n| Priority | Issue | File | Impact |\n|---|---|---|---|\n| **Critical** | Terminated sessions never removed | `session-manager.ts:terminateSession` | Memory grows unbounded |\n| **Critical** | `shutdown()` doesn't unsubscribe from event bus | `event-coordinator.ts:shutdown` | Phantom handlers fire after shutdown |\n| **High** | Tasks never auto-pruned | `task-manager.ts:constructor` | Memory grows unbounded |\n| **High** | `TaskQueue.enqueue()` full re-sort on every insert | `task-manager.ts:TaskQueue` | CPU, scales as O(n²) total |\n| **High** | `getStats()` iterates pool on every call | `connection-pool.ts:getStats` | Called on every MCP health check |\n| **Medium** | New `Set` allocated on every event dispatch | `event-bus.ts:dispatchEvent` | High-frequency hot path |\n| **Medium** | `getStats()` runs 5 SQL queries every call | `event-store.ts:getStats` | No caching |\n| **Medium** | Multi-pass filter in `getTasks()` | `task-manager.ts:getTasks` | 3× allocations per call |\n| **Medium** | Config re-read from disk on every `load()` | `config/loader.ts` | Repeated I/O |\n| **Low** | History trim allocates new array | `health-monitor.ts:addToHistory` | Minor, fixed interval |\n| **Low** | `getAll()` called separately per filter method | `lifecycle-manager.ts` | Minor duplicate allocation |\n| **Bug** | `getSnapshot()` passes bind params to `getAsObject` | `event-store.ts:getSnapshot` | Always returns null |\n",
6
+ "parsedOutput": {
7
+ "sections": [
8
+ {
9
+ "title": "Performance Analysis",
10
+ "content": "\nThis is a TypeScript backend orchestration system (no React). I've identified issues across memory leaks, redundant computation, missing caches, and one query pattern.\n\n---\n\n",
11
+ "level": 2
12
+ },
13
+ {
14
+ "title": "1. Memory Leaks",
15
+ "content": "\n",
16
+ "level": 3
17
+ },
18
+ {
19
+ "title": "`session-manager.ts` — Terminated sessions never removed",
20
+ "content": "`terminateSession()` marks a session terminated but never removes it from `this.sessions`. Terminated sessions accumulate indefinitely.\n\n```typescript\n// BUG: session stays in map forever after termination\nasync terminateSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n session.status = 'terminated';\n session.endTime = new Date();\n this.sessionProfiles.delete(sessionId); // only cleans up profile\n // this.sessions still holds the entry\n}\n```\n\n**Fix — remove from map at termination:**\n```typescript\nasync terminateSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) throw new Error(`Session not found: ${sessionId}`);\n\n session.status = 'terminated';\n session.endTime = new Date();\n const duration = session.endTime.getTime() - session.startTime.getTime();\n\n this.eventBus.emit(SystemEventTypes.SESSION_TERMINATED, {\n sessionId, agentId: session.agentId, duration,\n });\n\n // Clean up both maps immediately\n this.sessions.delete(sessionId);\n this.sessionProfiles.delete(sessionId);\n\n this.persistSessions().catch(() => {});\n}\n```\n\n",
21
+ "level": 4
22
+ },
23
+ {
24
+ "title": "`event-coordinator.ts` — `shutdown()` leaves event bus handlers registered",
25
+ "content": "`registerHandler` subscribes to the event bus AND stores locally. `shutdown()` only clears the local map — the event bus keeps all subscriptions alive and will keep invoking them.\n\n```typescript\n// Only clears local map, NOT event bus subscriptions\nasync shutdown(): Promise<void> {\n this.handlers.clear(); // ← event bus is still subscribed\n this.initialized = false;\n}\n```\n\n**Fix — unregister from event bus on shutdown:**\n```typescript\nasync shutdown(): Promise<void> {\n for (const [type, handlers] of this.handlers.entries()) {\n for (const handler of handlers) {\n this.eventBus.off(type, handler);\n }\n }\n this.handlers.clear();\n this.initialized = false;\n}\n```\n\n",
26
+ "level": 4
27
+ },
28
+ {
29
+ "title": "`task-manager.ts` — Tasks accumulate without auto-pruning",
30
+ "content": "`this.tasks` (a Map) grows forever. `cleanup()` exists but is never called automatically. In a long-running process with thousands of tasks per day this is a significant leak.\n\n**Fix — add a periodic cleanup or prune on completion:**\n```typescript\nconstructor(private eventBus: IEventBus, queue?: ITaskQueue) {\n this.queue = queue ?? new TaskQueue();\n // Prune completed/failed tasks older than 1 hour every 10 minutes\n setInterval(() => {\n const cutoff = new Date(Date.now() - 3_600_000);\n this.cleanup(cutoff).catch(() => {});\n }, 600_000).unref();\n}\n```\n\n",
31
+ "level": 4
32
+ },
33
+ {
34
+ "title": "`health-monitor.ts` — `listeners` array not cleared on `stop()`",
35
+ "content": "When `stop()` is called the interval is cleared, but `this.listeners` keeps all callbacks referenced.\n\n```typescript\nstop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n this.running = false;\n // BUG: listeners array still holds external references\n}\n```\n\n**Fix:**\n```typescript\nstop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n this.listeners = [];\n this.running = false;\n}\n```\n\n---\n\n",
36
+ "level": 4
37
+ },
38
+ {
39
+ "title": "2. Redundant Computations",
40
+ "content": "\n",
41
+ "level": 3
42
+ },
43
+ {
44
+ "title": "`task-manager.ts` — `TaskQueue.enqueue()` re-sorts entire array on every insert",
45
+ "content": "\n`O(n log n)` per insertion; with a priority queue (min-heap) this is `O(log n)`.\n\n```typescript\n// Current: O(n log n) per insert\nasync enqueue(task: ITask): Promise<void> {\n this.tasks.push(task);\n this.tasks.sort((a, b) => b.priority - a.priority);\n}\n```\n\n**Fix — insertion into sorted position, `O(n)` worst case but avoids full re-sort:**\n```typescript\nasync enqueue(task: ITask): Promise<void> {\n // Binary search for insertion point\n let lo = 0, hi = this.tasks.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this.tasks[mid].priority >= task.priority) lo = mid + 1;\n else hi = mid;\n }\n this.tasks.splice(lo, 0, task);\n}\n```\n\nFor very high enqueue rates, replace `ITask[]` with a proper binary heap. The current approach re-sorts even when the queue has hundreds of entries.\n\n",
46
+ "level": 4
47
+ },
48
+ {
49
+ "title": "`event-bus.ts` — `dispatchEvent()` allocates a new `Set` on every single event",
50
+ "content": "\n```typescript\nprivate dispatchEvent<T>(event: IEvent<T>): void {\n const typeHandlers = this.handlers.get(event.type);\n const wildcardHandlers = this.handlers.get('*');\n\n // NEW Set + copy on every dispatch — unnecessary when no wildcards\n const allHandlers = new Set<IEventHandler>();\n if (typeHandlers) {\n for (const handler of typeHandlers) allHandlers.add(handler);\n }\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) allHandlers.add(handler);\n }\n // ...\n}\n```\n\n**Fix — fast-path when no wildcards (the common case):**\n```typescript\nprivate dispatchEvent<T>(event: IEvent<T>): void {\n const typeHandlers = this.handlers.get(event.type);\n const wildcardHandlers = this.handlers.get('*');\n\n if (!wildcardHandlers || wildcardHandlers.size === 0) {\n // Fast path: no allocation needed\n if (typeHandlers) {\n for (const handler of typeHandlers) this.invokeHandler(handler, event);\n }\n return;\n }\n\n // Slow path: need dedup between type + wildcard handlers\n const allHandlers = new Set<IEventHandler>([\n ...(typeHandlers ?? []),\n ...wildcardHandlers,\n ]);\n for (const handler of allHandlers) this.invokeHandler(handler, event);\n}\n\nprivate invokeHandler<T>(handler: IEventHandler, event: IEvent<T>): void {\n try {\n const result = handler(event);\n if (result instanceof Promise) result.catch(err =>\n console.error(`Error in async event handler for ${event.type}:`, err)\n );\n } catch (error) {\n console.error(`Error in event handler for ${event.type}:`, error);\n }\n}\n```\n\n",
51
+ "level": 4
52
+ },
53
+ {
54
+ "title": "`health-monitor.ts` — `addToHistory` creates a new array on every trim",
55
+ "content": "\n```typescript\nprivate addToHistory(status: IHealthStatus): void {\n this.history.push(status);\n if (this.history.length > this.config.historyLimit) {\n this.history = this.history.slice(-this.config.historyLimit); // new array every time\n }\n}\n```\n\n**Fix — shift instead of slice (keeps array in place):**\n```typescript\nprivate addToHistory(status: IHealthStatus): void {\n this.history.push(status);\n if (this.history.length > this.config.historyLimit) {\n this.history.shift(); // O(n) but avoids allocation; or use a circular buffer\n }\n}\n```\n\nFor high-frequency health checks, a fixed-size circular buffer is ideal (`O(1)` insert/trim).\n\n",
56
+ "level": 4
57
+ },
58
+ {
59
+ "title": "`lifecycle-manager.ts` — multiple calls to `getAll()` create redundant copies",
60
+ "content": "\n`getByStatus()`, `getByType()`, and `getAvailable()` each call `getAll()`, which allocates a new array via `Array.from()`.\n\n```typescript\ngetByStatus(status: AgentStatus): IAgent[] {\n return this.getAll().filter(agent => agent.status === status); // new array\n}\ngetByType(type: string): IAgent[] {\n return this.getAll().filter(agent => agent.type === type); // new array\n}\n```\n\n**Fix — iterate the Map directly:**\n```typescript\ngetByStatus(status: AgentStatus): IAgent[] {\n const result: IAgent[] = [];\n for (const agent of this.agents.values()) {\n if (agent.status === status) result.push(agent);\n }\n return result;\n}\n```\n\n",
61
+ "level": 4
62
+ },
63
+ {
64
+ "title": "`task-manager.ts` — `getTasks()` makes multiple passes over the same array",
65
+ "content": "\n```typescript\ngetTasks(filter?) {\n let tasks = Array.from(this.tasks.values()); // copy\n if (filter?.status) tasks = tasks.filter(t => t.status === filter.status);\n if (filter?.type) tasks = tasks.filter(t => t.type === filter.type);\n if (filter?.assignedAgent) tasks = tasks.filter(t => t.assignedAgent === filter.assignedAgent);\n return tasks;\n}\n```\n\nThree separate `.filter()` passes, each producing an intermediate array.\n\n**Fix — single pass:**\n```typescript\ngetTasks(filter?: Partial<Pick<ITask, 'status' | 'type' | 'assignedAgent'>>): ITask[] {\n const result: ITask[] = [];\n for (const task of this.tasks.values()) {\n if (filter?.status && task.status !== filter.status) continue;\n if (filter?.type && task.type !== filter.type) continue;\n if (filter?.assignedAgent && task.assignedAgent !== filter.assignedAgent) continue;\n result.push(task);\n }\n return result;\n}\n```\n\n---\n\n",
66
+ "level": 4
67
+ },
68
+ {
69
+ "title": "3. Caching Opportunities",
70
+ "content": "\n",
71
+ "level": 3
72
+ },
73
+ {
74
+ "title": "`config/loader.ts` — no result caching; re-reads disk on every `load()` call",
75
+ "content": "\n`ConfigLoader.load()` hits the filesystem and validates on every invocation. In a CLI that calls it from multiple commands, this is wasteful.\n\n**Fix — memoize after first successful load:**\n```typescript\nexport class ConfigLoader {\n private cachedConfig: LoadedConfig | null = null;\n\n async load(forceReload = false): Promise<LoadedConfig> {\n if (this.cachedConfig && !forceReload) return this.cachedConfig;\n const result = await this._load();\n this.cachedConfig = result;\n return result;\n }\n\n invalidate(): void {\n this.cachedConfig = null;\n }\n}\n```\n\n",
76
+ "level": 4
77
+ },
78
+ {
79
+ "title": "`event-store.ts` — `getStats()` runs 5 sequential SQL queries",
80
+ "content": "\nEvery call to `getStats()` issues five separate SQL statements that could be combined or cached.\n\n```typescript\n// 5 separate round-trips to SQLite\nconst totalStmt = this.db!.prepare('SELECT COUNT(*) ...');\nconst typeStmt = this.db!.prepare('SELECT type, COUNT(*) ... GROUP BY type');\nconst aggStmt = this.db!.prepare('SELECT aggregate_id, COUNT(*) ... GROUP BY aggregate_id');\nconst rangeStmt = this.db!.prepare('SELECT MIN(timestamp), MAX(timestamp) ...');\nconst snapshotStmt = this.db!.prepare('SELECT COUNT(*) FROM snapshots');\n```\n\n**Fix — cache stats with a short TTL, or merge into fewer queries:**\n```typescript\nprivate statsCache: { stats: EventStoreStats; cachedAt: number } | null = null;\nprivate readonly STATS_TTL_MS = 5000;\n\nasync getStats(): Promise<EventStoreStats> {\n const now = Date.now();\n if (this.statsCache && now - this.statsCache.cachedAt < this.STATS_TTL_MS) {\n return this.statsCache.stats;\n }\n const stats = await this.computeStats();\n this.statsCache = { stats, cachedAt: now };\n return stats;\n}\n```\n\n",
81
+ "level": 4
82
+ },
83
+ {
84
+ "title": "`connection-pool.ts` — `getStats()` iterates all connections on every call",
85
+ "content": "\n```typescript\ngetStats(): ConnectionPoolStats {\n let idleCount = 0, busyCount = 0;\n for (const connection of this.connections.values()) { // full scan every call\n if (connection.state === 'idle') idleCount++;\n else if (connection.state === 'busy') busyCount++;\n }\n // ...\n}\n```\n\n**Fix — maintain separate counters updated on `acquire()`/`release()`/`destroy()`:**\n```typescript\nprivate idleCount = 0;\nprivate busyCount = 0;\n\n// In acquire(): this.idleCount--; this.busyCount++;\n// In release(): this.busyCount--; this.idleCount++;\n// In destroy(): if (conn.state === 'idle') this.idleCount--;\n// else if (conn.state === 'busy') this.busyCount--;\n\ngetStats(): ConnectionPoolStats {\n return {\n totalConnections: this.connections.size,\n idleConnections: this.idleCount,\n busyConnections: this.busyCount,\n // ...\n };\n}\n```\n\n---\n\n",
86
+ "level": 4
87
+ },
88
+ {
89
+ "title": "4. N+1 Query Pattern",
90
+ "content": "\n",
91
+ "level": 3
92
+ },
93
+ {
94
+ "title": "`event-store.ts` — `loadAggregateVersions()` loads ALL versions at startup",
95
+ "content": "\nOn startup the store loads every aggregate's max version into memory. With millions of events this becomes a large in-memory map. The actual problem arises in `append()` — the version is fetched from the in-memory map, but if the store is used concurrently (e.g. multiple workers) there's no serialization and the version tracker diverges.\n\nThe subtler N+1 is in how consumers typically use this: calling `getEvents(aggregateId)` per aggregate in a loop rather than batching via `query({ aggregateIds: [...] })`. The `query()` method already supports batching — consumers should prefer it.\n\n**Guidance:** Prefer `query({ aggregateIds: ['a','b','c'] })` over a loop of `getEvents()` calls. Add a JSDoc note to `getEvents()` calling this out.\n\n",
96
+ "level": 4
97
+ },
98
+ {
99
+ "title": "`event-store.ts` — `getSnapshot()` uses `getAsObject` incorrectly (latent bug)",
100
+ "content": "\n```typescript\nconst row = stmt.getAsObject([aggregateId]); // passes bind params to getAsObject, not bind()\n```\n\n`getAsObject()` doesn't accept bind params. This should be:\n```typescript\nstmt.bind([aggregateId]);\nconst row = stmt.step() ? stmt.getAsObject() : null;\nstmt.free();\n```\n\n---\n\n",
101
+ "level": 4
102
+ },
103
+ {
104
+ "title": "5. Missing React Re-renders",
105
+ "content": "\nThis codebase has no React — no action needed here.\n\n---\n\n",
106
+ "level": 3
107
+ },
108
+ {
109
+ "title": "Priority Summary",
110
+ "content": "| Priority | Issue | File | Impact |\n|---|---|---|---|\n| **Critical** | Terminated sessions never removed | `session-manager.ts:terminateSession` | Memory grows unbounded |\n| **Critical** | `shutdown()` doesn't unsubscribe from event bus | `event-coordinator.ts:shutdown` | Phantom handlers fire after shutdown |\n| **High** | Tasks never auto-pruned | `task-manager.ts:constructor` | Memory grows unbounded |\n| **High** | `TaskQueue.enqueue()` full re-sort on every insert | `task-manager.ts:TaskQueue` | CPU, scales as O(n²) total |\n| **High** | `getStats()` iterates pool on every call | `connection-pool.ts:getStats` | Called on every MCP health check |\n| **Medium** | New `Set` allocated on every event dispatch | `event-bus.ts:dispatchEvent` | High-frequency hot path |\n| **Medium** | `getStats()` runs 5 SQL queries every call | `event-store.ts:getStats` | No caching |\n| **Medium** | Multi-pass filter in `getTasks()` | `task-manager.ts:getTasks` | 3× allocations per call |\n| **Medium** | Config re-read from disk on every `load()` | `config/loader.ts` | Repeated I/O |\n| **Low** | History trim allocates new array | `health-monitor.ts:addToHistory` | Minor, fixed interval |\n| **Low** | `getAll()` called separately per filter method | `lifecycle-manager.ts` | Minor duplicate allocation |\n| **Bug** | `getSnapshot()` passes bind params to `getAsObject` | `event-store.ts:getSnapshot` | Always returns null |",
111
+ "level": 3
112
+ }
113
+ ],
114
+ "codeBlocks": [
115
+ {
116
+ "language": "typescript",
117
+ "code": "// BUG: session stays in map forever after termination\nasync terminateSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n session.status = 'terminated';\n session.endTime = new Date();\n this.sessionProfiles.delete(sessionId); // only cleans up profile\n // this.sessions still holds the entry\n}"
118
+ },
119
+ {
120
+ "language": "typescript",
121
+ "code": "async terminateSession(sessionId: string): Promise<void> {\n const session = this.sessions.get(sessionId);\n if (!session) throw new Error(`Session not found: ${sessionId}`);\n\n session.status = 'terminated';\n session.endTime = new Date();\n const duration = session.endTime.getTime() - session.startTime.getTime();\n\n this.eventBus.emit(SystemEventTypes.SESSION_TERMINATED, {\n sessionId, agentId: session.agentId, duration,\n });\n\n // Clean up both maps immediately\n this.sessions.delete(sessionId);\n this.sessionProfiles.delete(sessionId);\n\n this.persistSessions().catch(() => {});\n}"
122
+ },
123
+ {
124
+ "language": "typescript",
125
+ "code": "// Only clears local map, NOT event bus subscriptions\nasync shutdown(): Promise<void> {\n this.handlers.clear(); // ← event bus is still subscribed\n this.initialized = false;\n}"
126
+ },
127
+ {
128
+ "language": "typescript",
129
+ "code": "async shutdown(): Promise<void> {\n for (const [type, handlers] of this.handlers.entries()) {\n for (const handler of handlers) {\n this.eventBus.off(type, handler);\n }\n }\n this.handlers.clear();\n this.initialized = false;\n}"
130
+ },
131
+ {
132
+ "language": "typescript",
133
+ "code": "constructor(private eventBus: IEventBus, queue?: ITaskQueue) {\n this.queue = queue ?? new TaskQueue();\n // Prune completed/failed tasks older than 1 hour every 10 minutes\n setInterval(() => {\n const cutoff = new Date(Date.now() - 3_600_000);\n this.cleanup(cutoff).catch(() => {});\n }, 600_000).unref();\n}"
134
+ },
135
+ {
136
+ "language": "typescript",
137
+ "code": "stop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n this.running = false;\n // BUG: listeners array still holds external references\n}"
138
+ },
139
+ {
140
+ "language": "typescript",
141
+ "code": "stop(): void {\n if (this.interval) {\n clearInterval(this.interval);\n this.interval = undefined;\n }\n this.listeners = [];\n this.running = false;\n}"
142
+ },
143
+ {
144
+ "language": "typescript",
145
+ "code": "// Current: O(n log n) per insert\nasync enqueue(task: ITask): Promise<void> {\n this.tasks.push(task);\n this.tasks.sort((a, b) => b.priority - a.priority);\n}"
146
+ },
147
+ {
148
+ "language": "typescript",
149
+ "code": "async enqueue(task: ITask): Promise<void> {\n // Binary search for insertion point\n let lo = 0, hi = this.tasks.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (this.tasks[mid].priority >= task.priority) lo = mid + 1;\n else hi = mid;\n }\n this.tasks.splice(lo, 0, task);\n}"
150
+ },
151
+ {
152
+ "language": "typescript",
153
+ "code": "private dispatchEvent<T>(event: IEvent<T>): void {\n const typeHandlers = this.handlers.get(event.type);\n const wildcardHandlers = this.handlers.get('*');\n\n // NEW Set + copy on every dispatch — unnecessary when no wildcards\n const allHandlers = new Set<IEventHandler>();\n if (typeHandlers) {\n for (const handler of typeHandlers) allHandlers.add(handler);\n }\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) allHandlers.add(handler);\n }\n // ...\n}"
154
+ },
155
+ {
156
+ "language": "typescript",
157
+ "code": "private dispatchEvent<T>(event: IEvent<T>): void {\n const typeHandlers = this.handlers.get(event.type);\n const wildcardHandlers = this.handlers.get('*');\n\n if (!wildcardHandlers || wildcardHandlers.size === 0) {\n // Fast path: no allocation needed\n if (typeHandlers) {\n for (const handler of typeHandlers) this.invokeHandler(handler, event);\n }\n return;\n }\n\n // Slow path: need dedup between type + wildcard handlers\n const allHandlers = new Set<IEventHandler>([\n ...(typeHandlers ?? []),\n ...wildcardHandlers,\n ]);\n for (const handler of allHandlers) this.invokeHandler(handler, event);\n}\n\nprivate invokeHandler<T>(handler: IEventHandler, event: IEvent<T>): void {\n try {\n const result = handler(event);\n if (result instanceof Promise) result.catch(err =>\n console.error(`Error in async event handler for ${event.type}:`, err)\n );\n } catch (error) {\n console.error(`Error in event handler for ${event.type}:`, error);\n }\n}"
158
+ },
159
+ {
160
+ "language": "typescript",
161
+ "code": "private addToHistory(status: IHealthStatus): void {\n this.history.push(status);\n if (this.history.length > this.config.historyLimit) {\n this.history = this.history.slice(-this.config.historyLimit); // new array every time\n }\n}"
162
+ },
163
+ {
164
+ "language": "typescript",
165
+ "code": "private addToHistory(status: IHealthStatus): void {\n this.history.push(status);\n if (this.history.length > this.config.historyLimit) {\n this.history.shift(); // O(n) but avoids allocation; or use a circular buffer\n }\n}"
166
+ },
167
+ {
168
+ "language": "typescript",
169
+ "code": "getByStatus(status: AgentStatus): IAgent[] {\n return this.getAll().filter(agent => agent.status === status); // new array\n}\ngetByType(type: string): IAgent[] {\n return this.getAll().filter(agent => agent.type === type); // new array\n}"
170
+ },
171
+ {
172
+ "language": "typescript",
173
+ "code": "getByStatus(status: AgentStatus): IAgent[] {\n const result: IAgent[] = [];\n for (const agent of this.agents.values()) {\n if (agent.status === status) result.push(agent);\n }\n return result;\n}"
174
+ },
175
+ {
176
+ "language": "typescript",
177
+ "code": "getTasks(filter?) {\n let tasks = Array.from(this.tasks.values()); // copy\n if (filter?.status) tasks = tasks.filter(t => t.status === filter.status);\n if (filter?.type) tasks = tasks.filter(t => t.type === filter.type);\n if (filter?.assignedAgent) tasks = tasks.filter(t => t.assignedAgent === filter.assignedAgent);\n return tasks;\n}"
178
+ },
179
+ {
180
+ "language": "typescript",
181
+ "code": "getTasks(filter?: Partial<Pick<ITask, 'status' | 'type' | 'assignedAgent'>>): ITask[] {\n const result: ITask[] = [];\n for (const task of this.tasks.values()) {\n if (filter?.status && task.status !== filter.status) continue;\n if (filter?.type && task.type !== filter.type) continue;\n if (filter?.assignedAgent && task.assignedAgent !== filter.assignedAgent) continue;\n result.push(task);\n }\n return result;\n}"
182
+ },
183
+ {
184
+ "language": "typescript",
185
+ "code": "export class ConfigLoader {\n private cachedConfig: LoadedConfig | null = null;\n\n async load(forceReload = false): Promise<LoadedConfig> {\n if (this.cachedConfig && !forceReload) return this.cachedConfig;\n const result = await this._load();\n this.cachedConfig = result;\n return result;\n }\n\n invalidate(): void {\n this.cachedConfig = null;\n }\n}"
186
+ },
187
+ {
188
+ "language": "typescript",
189
+ "code": "// 5 separate round-trips to SQLite\nconst totalStmt = this.db!.prepare('SELECT COUNT(*) ...');\nconst typeStmt = this.db!.prepare('SELECT type, COUNT(*) ... GROUP BY type');\nconst aggStmt = this.db!.prepare('SELECT aggregate_id, COUNT(*) ... GROUP BY aggregate_id');\nconst rangeStmt = this.db!.prepare('SELECT MIN(timestamp), MAX(timestamp) ...');\nconst snapshotStmt = this.db!.prepare('SELECT COUNT(*) FROM snapshots');"
190
+ },
191
+ {
192
+ "language": "typescript",
193
+ "code": "private statsCache: { stats: EventStoreStats; cachedAt: number } | null = null;\nprivate readonly STATS_TTL_MS = 5000;\n\nasync getStats(): Promise<EventStoreStats> {\n const now = Date.now();\n if (this.statsCache && now - this.statsCache.cachedAt < this.STATS_TTL_MS) {\n return this.statsCache.stats;\n }\n const stats = await this.computeStats();\n this.statsCache = { stats, cachedAt: now };\n return stats;\n}"
194
+ },
195
+ {
196
+ "language": "typescript",
197
+ "code": "getStats(): ConnectionPoolStats {\n let idleCount = 0, busyCount = 0;\n for (const connection of this.connections.values()) { // full scan every call\n if (connection.state === 'idle') idleCount++;\n else if (connection.state === 'busy') busyCount++;\n }\n // ...\n}"
198
+ },
199
+ {
200
+ "language": "typescript",
201
+ "code": "private idleCount = 0;\nprivate busyCount = 0;\n\n// In acquire(): this.idleCount--; this.busyCount++;\n// In release(): this.busyCount--; this.idleCount++;\n// In destroy(): if (conn.state === 'idle') this.idleCount--;\n// else if (conn.state === 'busy') this.busyCount--;\n\ngetStats(): ConnectionPoolStats {\n return {\n totalConnections: this.connections.size,\n idleConnections: this.idleCount,\n busyConnections: this.busyCount,\n // ...\n };\n}"
202
+ },
203
+ {
204
+ "language": "typescript",
205
+ "code": "const row = stmt.getAsObject([aggregateId]); // passes bind params to getAsObject, not bind()"
206
+ },
207
+ {
208
+ "language": "typescript",
209
+ "code": "stmt.bind([aggregateId]);\nconst row = stmt.step() ? stmt.getAsObject() : null;\nstmt.free();"
210
+ }
211
+ ]
212
+ },
213
+ "durationMs": 129259,
214
+ "model": "sonnet",
215
+ "sandboxMode": "permissive",
216
+ "workerType": "optimize",
217
+ "timestamp": "2026-04-28T12:46:43.991Z",
218
+ "executionId": "optimize_1777380274732_apxz3s"
219
+ }