@claude-flow/shared 3.0.0-alpha.7 → 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 (195) 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/schema.d.ts +96 -96
  27. package/dist/events/event-store.d.ts.map +1 -1
  28. package/dist/events/event-store.js +20 -9
  29. package/dist/events/event-store.js.map +1 -1
  30. package/dist/hooks/executor.d.ts.map +1 -1
  31. package/dist/hooks/executor.js +7 -4
  32. package/dist/hooks/executor.js.map +1 -1
  33. package/dist/hooks/verify-exports.test.js +6 -6
  34. package/dist/hooks/verify-exports.test.js.map +1 -1
  35. package/dist/mcp/server.d.ts.map +1 -1
  36. package/dist/mcp/server.js +3 -6
  37. package/dist/mcp/server.js.map +1 -1
  38. package/dist/mcp/types.d.ts +4 -6
  39. package/dist/mcp/types.d.ts.map +1 -1
  40. package/dist/mcp/types.js.map +1 -1
  41. package/package.json +3 -2
  42. package/ruvector.db +0 -0
  43. package/src/events/event-store.ts +18 -9
  44. package/src/hooks/executor.ts +7 -5
  45. package/src/hooks/verify-exports.test.ts +6 -6
  46. package/src/mcp/server.ts +3 -6
  47. package/src/mcp/types.ts +4 -6
  48. package/tsconfig.tsbuildinfo +1 -1
  49. package/.agentic-flow/intelligence.json +0 -16
  50. package/__tests__/coverage/base.css +0 -224
  51. package/__tests__/coverage/block-navigation.js +0 -87
  52. package/__tests__/coverage/coverage-final.json +0 -50
  53. package/__tests__/coverage/favicon.png +0 -0
  54. package/__tests__/coverage/index.html +0 -326
  55. package/__tests__/coverage/lcov-report/base.css +0 -224
  56. package/__tests__/coverage/lcov-report/block-navigation.js +0 -87
  57. package/__tests__/coverage/lcov-report/favicon.png +0 -0
  58. package/__tests__/coverage/lcov-report/index.html +0 -326
  59. package/__tests__/coverage/lcov-report/prettify.css +0 -1
  60. package/__tests__/coverage/lcov-report/prettify.js +0 -2
  61. package/__tests__/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  62. package/__tests__/coverage/lcov-report/sorter.js +0 -210
  63. package/__tests__/coverage/lcov-report/src/core/config/defaults.ts.html +0 -706
  64. package/__tests__/coverage/lcov-report/src/core/config/index.html +0 -161
  65. package/__tests__/coverage/lcov-report/src/core/config/loader.ts.html +0 -898
  66. package/__tests__/coverage/lcov-report/src/core/config/schema.ts.html +0 -649
  67. package/__tests__/coverage/lcov-report/src/core/config/validator.ts.html +0 -712
  68. package/__tests__/coverage/lcov-report/src/core/event-bus.ts.html +0 -793
  69. package/__tests__/coverage/lcov-report/src/core/index.html +0 -116
  70. package/__tests__/coverage/lcov-report/src/core/interfaces/event.interface.ts.html +0 -886
  71. package/__tests__/coverage/lcov-report/src/core/interfaces/index.html +0 -116
  72. package/__tests__/coverage/lcov-report/src/core/orchestrator/event-coordinator.ts.html +0 -451
  73. package/__tests__/coverage/lcov-report/src/core/orchestrator/health-monitor.ts.html +0 -727
  74. package/__tests__/coverage/lcov-report/src/core/orchestrator/index.html +0 -176
  75. package/__tests__/coverage/lcov-report/src/core/orchestrator/lifecycle-manager.ts.html +0 -874
  76. package/__tests__/coverage/lcov-report/src/core/orchestrator/session-manager.ts.html +0 -922
  77. package/__tests__/coverage/lcov-report/src/core/orchestrator/task-manager.ts.html +0 -1036
  78. package/__tests__/coverage/lcov-report/src/events/domain-events.ts.html +0 -1837
  79. package/__tests__/coverage/lcov-report/src/events/event-store.ts.html +0 -1849
  80. package/__tests__/coverage/lcov-report/src/events/example-usage.ts.html +0 -964
  81. package/__tests__/coverage/lcov-report/src/events/index.html +0 -176
  82. package/__tests__/coverage/lcov-report/src/events/projections.ts.html +0 -1768
  83. package/__tests__/coverage/lcov-report/src/events/state-reconstructor.ts.html +0 -1132
  84. package/__tests__/coverage/lcov-report/src/events.ts.html +0 -1186
  85. package/__tests__/coverage/lcov-report/src/hooks/example-usage.ts.html +0 -1582
  86. package/__tests__/coverage/lcov-report/src/hooks/executor.ts.html +0 -1222
  87. package/__tests__/coverage/lcov-report/src/hooks/index.html +0 -191
  88. package/__tests__/coverage/lcov-report/src/hooks/registry.ts.html +0 -1084
  89. package/__tests__/coverage/lcov-report/src/hooks/safety/bash-safety.ts.html +0 -1897
  90. package/__tests__/coverage/lcov-report/src/hooks/safety/file-organization.ts.html +0 -1504
  91. package/__tests__/coverage/lcov-report/src/hooks/safety/git-commit.ts.html +0 -1954
  92. package/__tests__/coverage/lcov-report/src/hooks/safety/index.html +0 -146
  93. package/__tests__/coverage/lcov-report/src/hooks/session-hooks.ts.html +0 -1762
  94. package/__tests__/coverage/lcov-report/src/hooks/task-hooks.ts.html +0 -1624
  95. package/__tests__/coverage/lcov-report/src/hooks/types.ts.html +0 -1156
  96. package/__tests__/coverage/lcov-report/src/index.html +0 -176
  97. package/__tests__/coverage/lcov-report/src/mcp/connection-pool.ts.html +0 -1399
  98. package/__tests__/coverage/lcov-report/src/mcp/index.html +0 -176
  99. package/__tests__/coverage/lcov-report/src/mcp/server.ts.html +0 -2407
  100. package/__tests__/coverage/lcov-report/src/mcp/session-manager.ts.html +0 -1369
  101. package/__tests__/coverage/lcov-report/src/mcp/tool-registry.ts.html +0 -1783
  102. package/__tests__/coverage/lcov-report/src/mcp/transport/http.ts.html +0 -1756
  103. package/__tests__/coverage/lcov-report/src/mcp/transport/index.html +0 -146
  104. package/__tests__/coverage/lcov-report/src/mcp/transport/stdio.ts.html +0 -1057
  105. package/__tests__/coverage/lcov-report/src/mcp/transport/websocket.ts.html +0 -1537
  106. package/__tests__/coverage/lcov-report/src/mcp/types.ts.html +0 -1780
  107. package/__tests__/coverage/lcov-report/src/plugin-interface.ts.html +0 -2074
  108. package/__tests__/coverage/lcov-report/src/plugin-loader.ts.html +0 -1999
  109. package/__tests__/coverage/lcov-report/src/plugin-registry.ts.html +0 -1897
  110. package/__tests__/coverage/lcov-report/src/plugins/official/hive-mind-plugin.ts.html +0 -1075
  111. package/__tests__/coverage/lcov-report/src/plugins/official/index.html +0 -131
  112. package/__tests__/coverage/lcov-report/src/plugins/official/maestro-plugin.ts.html +0 -1609
  113. package/__tests__/coverage/lcov-report/src/resilience/bulkhead.ts.html +0 -916
  114. package/__tests__/coverage/lcov-report/src/resilience/circuit-breaker.ts.html +0 -1063
  115. package/__tests__/coverage/lcov-report/src/resilience/index.html +0 -161
  116. package/__tests__/coverage/lcov-report/src/resilience/rate-limiter.ts.html +0 -1345
  117. package/__tests__/coverage/lcov-report/src/resilience/retry.ts.html +0 -757
  118. package/__tests__/coverage/lcov-report/src/security/index.html +0 -131
  119. package/__tests__/coverage/lcov-report/src/security/input-validation.ts.html +0 -880
  120. package/__tests__/coverage/lcov-report/src/security/secure-random.ts.html +0 -562
  121. package/__tests__/coverage/lcov-report/src/types/index.html +0 -131
  122. package/__tests__/coverage/lcov-report/src/types/swarm.types.ts.html +0 -850
  123. package/__tests__/coverage/lcov-report/src/types/task.types.ts.html +0 -700
  124. package/__tests__/coverage/lcov-report/src/types.ts.html +0 -1186
  125. package/__tests__/coverage/lcov-report/src/utils/index.html +0 -116
  126. package/__tests__/coverage/lcov-report/src/utils/secure-logger.ts.html +0 -856
  127. package/__tests__/coverage/lcov.info +0 -19877
  128. package/__tests__/coverage/prettify.css +0 -1
  129. package/__tests__/coverage/prettify.js +0 -2
  130. package/__tests__/coverage/sort-arrow-sprite.png +0 -0
  131. package/__tests__/coverage/sorter.js +0 -210
  132. package/__tests__/coverage/src/core/config/defaults.ts.html +0 -706
  133. package/__tests__/coverage/src/core/config/index.html +0 -161
  134. package/__tests__/coverage/src/core/config/loader.ts.html +0 -898
  135. package/__tests__/coverage/src/core/config/schema.ts.html +0 -649
  136. package/__tests__/coverage/src/core/config/validator.ts.html +0 -712
  137. package/__tests__/coverage/src/core/event-bus.ts.html +0 -793
  138. package/__tests__/coverage/src/core/index.html +0 -116
  139. package/__tests__/coverage/src/core/interfaces/event.interface.ts.html +0 -886
  140. package/__tests__/coverage/src/core/interfaces/index.html +0 -116
  141. package/__tests__/coverage/src/core/orchestrator/event-coordinator.ts.html +0 -451
  142. package/__tests__/coverage/src/core/orchestrator/health-monitor.ts.html +0 -727
  143. package/__tests__/coverage/src/core/orchestrator/index.html +0 -176
  144. package/__tests__/coverage/src/core/orchestrator/lifecycle-manager.ts.html +0 -874
  145. package/__tests__/coverage/src/core/orchestrator/session-manager.ts.html +0 -922
  146. package/__tests__/coverage/src/core/orchestrator/task-manager.ts.html +0 -1036
  147. package/__tests__/coverage/src/events/domain-events.ts.html +0 -1837
  148. package/__tests__/coverage/src/events/event-store.ts.html +0 -1849
  149. package/__tests__/coverage/src/events/example-usage.ts.html +0 -964
  150. package/__tests__/coverage/src/events/index.html +0 -176
  151. package/__tests__/coverage/src/events/projections.ts.html +0 -1768
  152. package/__tests__/coverage/src/events/state-reconstructor.ts.html +0 -1132
  153. package/__tests__/coverage/src/events.ts.html +0 -1186
  154. package/__tests__/coverage/src/hooks/example-usage.ts.html +0 -1582
  155. package/__tests__/coverage/src/hooks/executor.ts.html +0 -1222
  156. package/__tests__/coverage/src/hooks/index.html +0 -191
  157. package/__tests__/coverage/src/hooks/registry.ts.html +0 -1084
  158. package/__tests__/coverage/src/hooks/safety/bash-safety.ts.html +0 -1897
  159. package/__tests__/coverage/src/hooks/safety/file-organization.ts.html +0 -1504
  160. package/__tests__/coverage/src/hooks/safety/git-commit.ts.html +0 -1954
  161. package/__tests__/coverage/src/hooks/safety/index.html +0 -146
  162. package/__tests__/coverage/src/hooks/session-hooks.ts.html +0 -1762
  163. package/__tests__/coverage/src/hooks/task-hooks.ts.html +0 -1624
  164. package/__tests__/coverage/src/hooks/types.ts.html +0 -1156
  165. package/__tests__/coverage/src/index.html +0 -176
  166. package/__tests__/coverage/src/mcp/connection-pool.ts.html +0 -1399
  167. package/__tests__/coverage/src/mcp/index.html +0 -176
  168. package/__tests__/coverage/src/mcp/server.ts.html +0 -2407
  169. package/__tests__/coverage/src/mcp/session-manager.ts.html +0 -1369
  170. package/__tests__/coverage/src/mcp/tool-registry.ts.html +0 -1783
  171. package/__tests__/coverage/src/mcp/transport/http.ts.html +0 -1756
  172. package/__tests__/coverage/src/mcp/transport/index.html +0 -146
  173. package/__tests__/coverage/src/mcp/transport/stdio.ts.html +0 -1057
  174. package/__tests__/coverage/src/mcp/transport/websocket.ts.html +0 -1537
  175. package/__tests__/coverage/src/mcp/types.ts.html +0 -1780
  176. package/__tests__/coverage/src/plugin-interface.ts.html +0 -2074
  177. package/__tests__/coverage/src/plugin-loader.ts.html +0 -1999
  178. package/__tests__/coverage/src/plugin-registry.ts.html +0 -1897
  179. package/__tests__/coverage/src/plugins/official/hive-mind-plugin.ts.html +0 -1075
  180. package/__tests__/coverage/src/plugins/official/index.html +0 -131
  181. package/__tests__/coverage/src/plugins/official/maestro-plugin.ts.html +0 -1609
  182. package/__tests__/coverage/src/resilience/bulkhead.ts.html +0 -916
  183. package/__tests__/coverage/src/resilience/circuit-breaker.ts.html +0 -1063
  184. package/__tests__/coverage/src/resilience/index.html +0 -161
  185. package/__tests__/coverage/src/resilience/rate-limiter.ts.html +0 -1345
  186. package/__tests__/coverage/src/resilience/retry.ts.html +0 -757
  187. package/__tests__/coverage/src/security/index.html +0 -131
  188. package/__tests__/coverage/src/security/input-validation.ts.html +0 -880
  189. package/__tests__/coverage/src/security/secure-random.ts.html +0 -562
  190. package/__tests__/coverage/src/types/index.html +0 -131
  191. package/__tests__/coverage/src/types/swarm.types.ts.html +0 -850
  192. package/__tests__/coverage/src/types/task.types.ts.html +0 -700
  193. package/__tests__/coverage/src/types.ts.html +0 -1186
  194. package/__tests__/coverage/src/utils/index.html +0 -116
  195. 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
+ }